Skip to main content

rust_store_core/local/
value.rs

1//! 本地磁盘数据源 —— 值工具(点号路径取值 / Mongo BSON 类型序比较 / 极小表达式 / 投影)。
2//!
3//! 语义基准 = **MongoDB 驱动语义**(local 与 mongo 走同一条命令路径,必须同结果)。
4//! 纯逻辑:无 IO / 无时钟 / 无随机(确定性是双端对拍的前提)。
5//!
6//! 三态契约:`显式 null`(`Value::Null`)与`字段缺失`是两种不同状态 ——
7//! [`resolve_path_candidates`] 以「空 = 缺失、含 `Null` = 显式 null」承载该区分。
8
9use std::cmp::Ordering;
10
11use serde_json::{Map, Value};
12
13use crate::bson;
14
15use super::pipeline::{float_of, number_of};
16
17/// 单集合文档数护栏:超限一律 `Err`(禁静默截断,见执行文档 §4.1 / §8.4)。
18pub const MAX_LOCAL_COLLECTION_DOCS: usize = 100_000;
19
20// ---------------------------------------------------------------------------
21// Mongo BSON 类型序权重(升序;跨类型比较的第一基准)
22// ---------------------------------------------------------------------------
23
24const RANK_MINKEY: u8 = 1;
25const RANK_NULL: u8 = 2;
26const RANK_NUMBER: u8 = 3;
27const RANK_STRING: u8 = 4;
28const RANK_OBJECT: u8 = 5;
29const RANK_ARRAY: u8 = 6;
30const RANK_BINARY: u8 = 7;
31const RANK_OID: u8 = 8;
32const RANK_BOOL: u8 = 9;
33const RANK_DATE: u8 = 10;
34const RANK_TIMESTAMP: u8 = 11;
35const RANK_REGEX: u8 = 12;
36const RANK_MAXKEY: u8 = 13;
37
38/// 把点号路径拆段(`a.b.c` → `["a","b","c"]`;空路径 → 空段列表)。
39fn segments(path: &str) -> Vec<&str> {
40    if path.is_empty() {
41        Vec::new()
42    } else {
43        path.split('.').collect()
44    }
45}
46
47// ---------------------------------------------------------------------------
48// 点号路径取值
49// ---------------------------------------------------------------------------
50
51/// 严格点号取值(**不展开数组**):路径任一段缺失 → `None`;显式 `null` → `Some(&Value::Null)`。
52///
53/// 「缺失」与「显式 null」由此区分(三态契约的基础)。空路径 → 整文档。
54/// 数组下钻语义由 [`resolve_path_candidates`] 承担。
55pub fn get_path<'a>(doc: &'a Value, path: &str) -> Option<&'a Value> {
56    let mut cur = doc;
57    for seg in segments(path) {
58        cur = match cur {
59            Value::Object(o) => o.get(seg)?,
60            // 严格取值:遇数组/标量不再下钻
61            _ => return None,
62        };
63    }
64    Some(cur)
65}
66
67/// 解析字段路径在文档上的**全部候选值**(Mongo 过滤命中语义,含数组下钻)。
68///
69/// 规则(对齐 MongoDB 路径匹配):
70/// - 对象 → 取键、消费一段;
71/// - 数组 → **不消费段**,对每个元素继续解析(用于 `a.b` 中 `a` 为文档数组);
72/// - 段缺失 / 非对象非数组 → 该分支终止、不产出。
73///
74/// 返回:空 = 路径缺失(对应 `$exists:false` / 不命中);含 `Value::Null` = 存在显式 null。
75/// 仅返回末段解析到的值本身(数组字段返回该数组整体,元素命中由调用方按 [`matches_value`] 判定)。
76pub fn resolve_path_candidates<'a>(doc: &'a Value, path: &str) -> Vec<&'a Value> {
77    let mut out = Vec::new();
78    collect_candidates(doc, &segments(path), &mut out);
79    out
80}
81
82fn collect_candidates<'a>(v: &'a Value, segs: &[&str], out: &mut Vec<&'a Value>) {
83    if segs.is_empty() {
84        out.push(v);
85        return;
86    }
87    match v {
88        Value::Object(o) => {
89            if let Some(next) = o.get(segs[0]) {
90                collect_candidates(next, &segs[1..], out);
91            }
92        }
93        Value::Array(a) => {
94            for el in a {
95                collect_candidates(el, segs, out);
96            }
97        }
98        _ => {}
99    }
100}
101
102// ---------------------------------------------------------------------------
103// Mongo BSON 类型序比较
104// ---------------------------------------------------------------------------
105
106/// BSON 类型序权重:跨类型比较时先比权重(`MinKey < Null < Number < String < Object < Array < …`)。
107///
108/// 扩展类型(`$numberDecimal` / `$numberLong`)归入 Number;`$minKey` / `$maxKey` / `$timestamp` /
109/// `$regularExpression` 各归其序。
110pub fn bson_rank(v: &Value) -> u8 {
111    match v {
112        Value::Null => RANK_NULL,
113        Value::Bool(_) => RANK_BOOL,
114        Value::Number(_) => RANK_NUMBER,
115        Value::String(_) => RANK_STRING,
116        Value::Array(_) => RANK_ARRAY,
117        Value::Object(o) => extended_kind(o).unwrap_or(RANK_OBJECT),
118    }
119}
120
121/// 单键扩展类型对象 → 其 BSON 序权重;非扩展对象 → `None`(按普通文档处理)。
122fn extended_kind(o: &Map<String, Value>) -> Option<u8> {
123    if o.len() != 1 {
124        return None;
125    }
126    match o.keys().next()?.as_str() {
127        "$minKey" => Some(RANK_MINKEY),
128        "$maxKey" => Some(RANK_MAXKEY),
129        "$numberDecimal" | "$numberLong" => Some(RANK_NUMBER),
130        "$oid" => Some(RANK_OID),
131        "$date" => Some(RANK_DATE),
132        "$timestamp" => Some(RANK_TIMESTAMP),
133        "$binary" => Some(RANK_BINARY),
134        "$regularExpression" => Some(RANK_REGEX),
135        _ => None,
136    }
137}
138
139/// Mongo BSON 序比较(`Ordering::Less/Greater/Equal`)。
140///
141/// 数字间按数值比较(整型/浮点/扩展 Long 统一,`1` 与 `1.0` 相等);
142/// 字符串按字节序;文档逐键(键名 → 键值)比较,前缀相等时短者小;数组逐元素、前缀相等时短者小。
143pub fn compare(a: &Value, b: &Value) -> Ordering {
144    let (ra, rb) = (bson_rank(a), bson_rank(b));
145    if ra != rb {
146        return ra.cmp(&rb);
147    }
148    match ra {
149        RANK_NUMBER => compare_numbers(a, b),
150        RANK_STRING => a.as_str().unwrap_or("").cmp(b.as_str().unwrap_or("")),
151        RANK_BOOL => bool_rank(a).cmp(&bool_rank(b)),
152        RANK_OBJECT => compare_objects(
153            a.as_object().expect("rank=object"),
154            b.as_object().expect("rank=object"),
155        ),
156        RANK_ARRAY => compare_arrays(
157            a.as_array().expect("rank=array"),
158            b.as_array().expect("rank=array"),
159        ),
160        RANK_OID => oid_str(a).cmp(oid_str(b)),
161        RANK_DATE => date_ms(a).cmp(&date_ms(b)),
162        RANK_TIMESTAMP => ts_parts(a).cmp(&ts_parts(b)),
163        RANK_BINARY => compare_binary(a, b),
164        RANK_REGEX => regex_parts(a).cmp(&regex_parts(b)),
165        // Null / MinKey / MaxKey:同类型彼此相等
166        _ => Ordering::Equal,
167    }
168}
169
170/// 等值判定(= BSON 序比较为 `Equal`)。数字跨整型/浮点相等;文档按结构(键序由 serde_json 归一)。
171pub fn values_equal(a: &Value, b: &Value) -> bool {
172    compare(a, b) == Ordering::Equal
173}
174
175/// Mongo 等值命中:`actual` 等于 `expected`,或 `actual` 为**数组**且任一元素等于 `expected`。
176///
177/// 供过滤层 `{f: v}` / `$eq` 的「数组字段任一元素命中」语义复用。
178pub fn matches_value(actual: &Value, expected: &Value) -> bool {
179    if values_equal(actual, expected) {
180        return true;
181    }
182    match actual {
183        Value::Array(a) => a.iter().any(|el| values_equal(el, expected)),
184        _ => false,
185    }
186}
187
188fn compare_numbers(a: &Value, b: &Value) -> Ordering {
189    numeric_value(a)
190        .partial_cmp(&numeric_value(b))
191        .unwrap_or(Ordering::Equal)
192}
193
194/// 数字取值(普通 JSON number / 扩展 `$numberLong` / `$numberDecimal` 统一为 f64);非数字 → `None`。
195///
196/// 与 [`numeric_value`] 的差别:本函数对非数字返回 `None`(供聚合累积器判定「是否忽略该值」),
197/// 后者返回 `0.0`(供比较排序,任何值都可参与)。
198pub fn as_number(v: &Value) -> Option<f64> {
199    match v {
200        Value::Number(n) => n.as_f64(),
201        Value::Object(o) => {
202            if let Some(s) = o.get("$numberLong").and_then(Value::as_str) {
203                s.parse::<f64>().ok()
204            } else {
205                o.get("$numberDecimal")
206                    .and_then(Value::as_str)
207                    .and_then(|s| s.parse::<f64>().ok())
208            }
209        }
210        _ => None,
211    }
212}
213
214/// 数字的数值形式(普通 JSON number / 扩展 `$numberLong` / `$numberDecimal` 统一为 f64)。
215fn numeric_value(v: &Value) -> f64 {
216    as_number(v).unwrap_or(0.0)
217}
218
219fn bool_rank(v: &Value) -> u8 {
220    u8::from(v.as_bool().unwrap_or(false))
221}
222
223fn compare_objects(a: &Map<String, Value>, b: &Map<String, Value>) -> Ordering {
224    let mut ai = a.iter();
225    let mut bi = b.iter();
226    loop {
227        match (ai.next(), bi.next()) {
228            (None, None) => return Ordering::Equal,
229            (None, Some(_)) => return Ordering::Less,
230            (Some(_), None) => return Ordering::Greater,
231            (Some((ka, va)), Some((kb, vb))) => {
232                let key_cmp = ka.cmp(kb);
233                if key_cmp != Ordering::Equal {
234                    return key_cmp;
235                }
236                let val_cmp = compare(va, vb);
237                if val_cmp != Ordering::Equal {
238                    return val_cmp;
239                }
240            }
241        }
242    }
243}
244
245fn compare_arrays(a: &[Value], b: &[Value]) -> Ordering {
246    for (x, y) in a.iter().zip(b.iter()) {
247        let c = compare(x, y);
248        if c != Ordering::Equal {
249            return c;
250        }
251    }
252    a.len().cmp(&b.len())
253}
254
255fn oid_str(v: &Value) -> &str {
256    v.get("$oid").and_then(Value::as_str).unwrap_or("")
257}
258
259fn date_ms(v: &Value) -> i64 {
260    v.get("$date").and_then(Value::as_i64).unwrap_or(0)
261}
262
263fn ts_parts(v: &Value) -> (i64, i64) {
264    match v.get("$timestamp").and_then(Value::as_object) {
265        Some(o) => (
266            o.get("t").and_then(Value::as_i64).unwrap_or(0),
267            o.get("i").and_then(Value::as_i64).unwrap_or(0),
268        ),
269        None => (0, 0),
270    }
271}
272
273fn compare_binary(a: &Value, b: &Value) -> Ordering {
274    // Mongo 序:先按数据长度、再按子类型、再按字节(base64 长度与数据长度成比例,用作长度基准)
275    let (a_b64, a_sub) = binary_parts(a);
276    let (b_b64, b_sub) = binary_parts(b);
277    a_b64
278        .len()
279        .cmp(&b_b64.len())
280        .then_with(|| a_sub.cmp(&b_sub))
281        .then_with(|| a_b64.cmp(&b_b64))
282}
283
284fn binary_parts(v: &Value) -> (String, String) {
285    match v.get("$binary").and_then(Value::as_object) {
286        Some(o) => (
287            o.get("base64")
288                .and_then(Value::as_str)
289                .unwrap_or("")
290                .to_string(),
291            o.get("subType")
292                .and_then(Value::as_str)
293                .unwrap_or("")
294                .to_string(),
295        ),
296        None => (String::new(), String::new()),
297    }
298}
299
300fn regex_parts(v: &Value) -> (String, String) {
301    match v.get("$regularExpression").and_then(Value::as_object) {
302        Some(o) => (
303            o.get("pattern")
304                .and_then(Value::as_str)
305                .unwrap_or("")
306                .to_string(),
307            o.get("options")
308                .and_then(Value::as_str)
309                .unwrap_or("")
310                .to_string(),
311        ),
312        None => (String::new(), String::new()),
313    }
314}
315
316// ---------------------------------------------------------------------------
317// 极小聚合表达式求值(`$expr` / `$lookup.pipeline` / `$group` 键 / `$project` 计算字段)
318// ---------------------------------------------------------------------------
319
320/// 求值极小聚合表达式。
321///
322/// 支持:
323/// - 字面量(数字 / 字符串 / 布尔 / null / 数组 / 扩展类型对象)原样返回;
324/// - `"$field.path"`:从 `doc` 严格取值,缺失 → `null`;
325/// - `"$$var"` / `"$$var.path"`:从 `vars` 取值,缺失 → `null`(供 `$lookup.let` 注入);
326/// - `{ "$eq": [a, b] }`、`{ "$and": [...] }`、`{ "$or": [...] }`、`{ "$ifNull": [a, b] }`;
327/// - `{ "$size": <数组> }`、`{ "$sum/$avg/$min/$max": <数组或单值> }`
328///   (core 关系聚合计算列 `$addFields` 的表达式形态,语义对齐 `pipeline.rs::accumulate`);
329/// - 普通对象 `{k: <expr>, …}` 逐键求值构造新文档;普通数组逐元素求值。
330///
331/// 未知算子一律 `Err`(禁静默)。
332pub fn eval_expr(expr: &Value, doc: &Value, vars: &Map<String, Value>) -> Result<Value, String> {
333    match expr {
334        Value::String(s) => eval_ref(s, doc, vars),
335        Value::Array(a) => a
336            .iter()
337            .map(|e| eval_expr(e, doc, vars))
338            .collect::<Result<Vec<_>, _>>()
339            .map(Value::Array),
340        Value::Object(o) => {
341            // 扩展类型对象(`$oid` 等)原样返回,避免被误判为算子
342            if bson::is_extended(expr) {
343                return Ok(expr.clone());
344            }
345            if o.len() == 1 {
346                if let Some(op) = o.keys().next().map(String::as_str) {
347                    if let Some(stripped) = op.strip_prefix('$') {
348                        return eval_operator(stripped, o.values().next().expect("len=1"), doc, vars);
349                    }
350                }
351            }
352            let mut out = Map::new();
353            for (k, v) in o {
354                out.insert(k.clone(), eval_expr(v, doc, vars)?);
355            }
356            Ok(Value::Object(out))
357        }
358        other => Ok(other.clone()),
359    }
360}
361
362fn eval_operator(
363    op: &str,
364    arg: &Value,
365    doc: &Value,
366    vars: &Map<String, Value>,
367) -> Result<Value, String> {
368    match op {
369        "eq" => {
370            let (a, b) = two_operands("$eq", arg)?;
371            Ok(Value::Bool(values_equal(
372                &eval_expr(a, doc, vars)?,
373                &eval_expr(b, doc, vars)?,
374            )))
375        }
376        "and" => {
377            for e in array_operands("$and", arg)? {
378                if !truthy(&eval_expr(e, doc, vars)?) {
379                    return Ok(Value::Bool(false));
380                }
381            }
382            Ok(Value::Bool(true))
383        }
384        "or" => {
385            for e in array_operands("$or", arg)? {
386                if truthy(&eval_expr(e, doc, vars)?) {
387                    return Ok(Value::Bool(true));
388                }
389            }
390            Ok(Value::Bool(false))
391        }
392        "ifNull" => {
393            let (a, b) = two_operands("$ifNull", arg)?;
394            let v = eval_expr(a, doc, vars)?;
395            if v.is_null() {
396                eval_expr(b, doc, vars)
397            } else {
398                Ok(v)
399            }
400        }
401        // `$cond`:core 的 `$lookup.let`(数组外键守卫)与 `$group` 的 `$count` 字段形态
402        // 均发射该算子,故必须支持;数组形态 `[if, then, else]` 与对象形态 `{if,then,else}` 均可。
403        "cond" => {
404            let (c, t, e) = cond_operands(arg)?;
405            if truthy(&eval_expr(c, doc, vars)?) {
406                eval_expr(t, doc, vars)
407            } else {
408                eval_expr(e, doc, vars)
409            }
410        }
411        "isArray" => Ok(Value::Bool(eval_expr(arg, doc, vars)?.is_array())),
412        // `$in`:core 数组外键关系发射 `{$expr: {$in: ["$fk", "$$let"]}}`
413        "in" => {
414            let (needle, hay) = two_operands("$in", arg)?;
415            let needle = eval_expr(needle, doc, vars)?;
416            let hay = eval_expr(hay, doc, vars)?;
417            let items = hay
418                .as_array()
419                .ok_or_else(|| "$in 的第二个操作数必须是数组".to_string())?;
420            let hit = match &needle {
421                Value::Array(a) => a
422                    .iter()
423                    .any(|n| items.iter().any(|c| values_equal(n, c))),
424                other => items.iter().any(|c| values_equal(other, c)),
425            };
426            Ok(Value::Bool(hit))
427        }
428        // `$arrayElemAt`:core 的全表单组空集护栏(`$facet` + `$replaceRoot`)使用
429        "arrayElemAt" => {
430            let (arr_e, idx_e) = two_operands("$arrayElemAt", arg)?;
431            let arr = eval_expr(arr_e, doc, vars)?;
432            let idx_v = eval_expr(idx_e, doc, vars)?;
433            let idx = idx_v
434                .as_i64()
435                .ok_or_else(|| "$arrayElemAt 的下标必须是整数".to_string())?;
436            let items = arr
437                .as_array()
438                .ok_or_else(|| "$arrayElemAt 的第一个操作数必须是数组".to_string())?;
439            let len = items.len() as i64;
440            let i = if idx < 0 { len + idx } else { idx };
441            // 越界 → `null`(Mongo 为 missing,在 `$ifNull` 兜底语境下等价)
442            Ok(items.get(i as usize).cloned().unwrap_or(Value::Null))
443        }
444        // `$size`:数组长度(core 关系聚合计算列 `$count` 发射 `{$size: {$ifNull: [..., []]}}`)
445        "size" => {
446            let arr = eval_expr(arg, doc, vars)?;
447            let items = arr
448                .as_array()
449                .ok_or_else(|| format!("$size 的操作数必须是数组,收到 {}", value_type_name(&arr)))?;
450            Ok(Value::from(items.len() as i64))
451        }
452        // `$sum` / `$avg` / `$min` / `$max`:数组聚合表达式形态(core 关系聚合计算列
453        // `$addFields` 发射 `{$sum/$avg/$min/$max: "$as.field"}`,`as` 为 `$lookup` 结果数组)。
454        // 语义对齐 `pipeline.rs::accumulate`($sum 忽略非数值、整数值→整数形态;$avg 恒浮点;
455        // $min/$max 按 BSON 序、忽略 null;空集 → $sum=0 / 其余 null)。单值形态 = 单元素数组。
456        "sum" | "avg" | "min" | "max" => {
457            let v = eval_expr(arg, doc, vars)?;
458            match v {
459                Value::Array(items) => accumulate_expr(op, &items),
460                other => accumulate_expr(op, std::slice::from_ref(&other)),
461            }
462        }
463        other => Err(format!(
464            "不支持的聚合表达式算子: ${other}(本地求值器仅支持 $eq/$and/$or/$ifNull/$cond/$isArray/$in/$arrayElemAt/$size/$sum/$avg/$min/$max,拒绝静默)"
465        )),
466    }
467}
468
469/// 数组聚合(`$sum` / `$avg` / `$min` / `$max` 的表达式形态)——语义镜像
470/// [`super::pipeline::accumulate`],保证「关系聚合计算列」与「根级 `$group`」数值形态一致。
471fn accumulate_expr(op: &str, items: &[Value]) -> Result<Value, String> {
472    match op {
473        "sum" => {
474            let mut sum = 0.0_f64;
475            for v in items {
476                if let Some(n) = as_number(v) {
477                    sum += n;
478                }
479            }
480            Ok(number_of(sum))
481        }
482        "avg" => {
483            let (mut sum, mut count) = (0.0_f64, 0_usize);
484            for v in items {
485                if let Some(n) = as_number(v) {
486                    sum += n;
487                    count += 1;
488                }
489            }
490            Ok(if count == 0 {
491                Value::Null
492            } else {
493                float_of(sum / count as f64)
494            })
495        }
496        _ => {
497            let mut best: Option<&Value> = None;
498            for v in items {
499                if v.is_null() {
500                    continue;
501                }
502                best = Some(match best {
503                    None => v,
504                    Some(cur) => {
505                        let take = if op == "min" {
506                            compare(v, cur) == Ordering::Less
507                        } else {
508                            compare(v, cur) == Ordering::Greater
509                        };
510                        if take {
511                            v
512                        } else {
513                            cur
514                        }
515                    }
516                });
517            }
518            Ok(best.cloned().unwrap_or(Value::Null))
519        }
520    }
521}
522
523fn value_type_name(v: &Value) -> &'static str {
524    match v {
525        Value::Null => "null",
526        Value::Bool(_) => "bool",
527        Value::Number(_) => "number",
528        Value::String(_) => "string",
529        Value::Array(_) => "array",
530        Value::Object(_) => "object",
531    }
532}
533
534fn two_operands<'a>(name: &str, arg: &'a Value) -> Result<(&'a Value, &'a Value), String> {
535    let arr = arg
536        .as_array()
537        .ok_or_else(|| format!("{name} 需要操作数数组"))?;
538    if arr.len() != 2 {
539        return Err(format!("{name} 需要恰好两个操作数,收到 {}", arr.len()));
540    }
541    Ok((&arr[0], &arr[1]))
542}
543
544fn array_operands<'a>(name: &str, arg: &'a Value) -> Result<&'a [Value], String> {
545    arg.as_array()
546        .map(Vec::as_slice)
547        .ok_or_else(|| format!("{name} 需要操作数数组"))
548}
549
550/// `$cond` 操作数:数组形态 `[if, then, else]` 或对象形态 `{if, then, else}`。
551fn cond_operands(arg: &Value) -> Result<(&Value, &Value, &Value), String> {
552    if let Some(a) = arg.as_array() {
553        if a.len() != 3 {
554            return Err(format!("$cond 数组形态需要 3 个操作数,收到 {}", a.len()));
555        }
556        return Ok((&a[0], &a[1], &a[2]));
557    }
558    if let Some(o) = arg.as_object() {
559        let pick = |k: &str| {
560            o.get(k)
561                .ok_or_else(|| format!("$cond 对象形态缺少 \"{k}\""))
562        };
563        return Ok((pick("if")?, pick("then")?, pick("else")?));
564    }
565    Err("$cond 需要数组 [if, then, else] 或对象 {if, then, else}".to_string())
566}
567
568/// Mongo 聚合布尔真值:`false` / `null` / `0` 为假,其余为真。
569fn truthy(v: &Value) -> bool {
570    match v {
571        Value::Null => false,
572        Value::Bool(b) => *b,
573        Value::Number(n) => n.as_f64().map(|x| x != 0.0).unwrap_or(true),
574        _ => true,
575    }
576}
577
578/// `"$field"` / `"$$var"` 引用求值;非 `$` 前缀的字符串为字面量。
579fn eval_ref(s: &str, doc: &Value, vars: &Map<String, Value>) -> Result<Value, String> {
580    if let Some(rest) = s.strip_prefix("$$") {
581        let (name, path) = match rest.split_once('.') {
582            Some((n, p)) => (n, p),
583            None => (rest, ""),
584        };
585        let base = vars.get(name).cloned().unwrap_or(Value::Null);
586        Ok(reference_path(&base, path))
587    } else if let Some(rest) = s.strip_prefix('$') {
588        Ok(get_path(doc, rest).cloned().unwrap_or(Value::Null))
589    } else {
590        Ok(Value::String(s.to_string()))
591    }
592}
593
594fn reference_path(base: &Value, path: &str) -> Value {
595    if path.is_empty() {
596        base.clone()
597    } else {
598        get_path(base, path).cloned().unwrap_or(Value::Null)
599    }
600}
601
602// ---------------------------------------------------------------------------
603// 投影(包含 / 排除;`_id` 可与包含式混用)
604// ---------------------------------------------------------------------------
605
606/// 应用投影:包含式 / 排除式;`_id` 特殊处理(包含式默认带 `_id`,`_id: 0` 可与之混用)。
607///
608/// 空投影对象 → 原样返回(视为无投影)。同时含非 `_id` 的包含与排除 → `Err`(禁静默)。
609pub fn apply_projection(doc: &Value, projection: &Value) -> Result<Value, String> {
610    let obj = projection
611        .as_object()
612        .ok_or_else(|| format!("投影必须是对象,收到 {}", type_name(projection)))?;
613    if obj.is_empty() {
614        return Ok(doc.clone());
615    }
616
617    let mut include_paths: Vec<&String> = Vec::new();
618    let mut exclude_paths: Vec<&String> = Vec::new();
619    let mut id: Option<bool> = None;
620    for (k, v) in obj {
621        let flag = projection_flag(v)?;
622        if k == "_id" {
623            id = Some(flag);
624        } else if flag {
625            include_paths.push(k);
626        } else {
627            exclude_paths.push(k);
628        }
629    }
630    if !include_paths.is_empty() && !exclude_paths.is_empty() {
631        return Err("投影不允许同时包含与排除字段(`_id` 除外)".to_string());
632    }
633
634    let include_mode = if !include_paths.is_empty() {
635        true
636    } else if !exclude_paths.is_empty() {
637        false
638    } else {
639        // 仅出现 `_id` 指令:`_id:1` → 只保留 `_id`;`_id:0` → 全保留除 `_id`
640        id == Some(true)
641    };
642
643    if include_mode {
644        let mut out = Map::new();
645        if id != Some(false) {
646            if let Some(v) = doc.get("_id") {
647                out.insert("_id".to_string(), v.clone());
648            }
649        }
650        for path in &include_paths {
651            if let Some(v) = project_included(doc, &segments(path)) {
652                merge_into(&mut out, v);
653            }
654        }
655        Ok(Value::Object(out))
656    } else {
657        let mut out = doc.clone();
658        for path in &exclude_paths {
659            remove_segments(&mut out, &segments(path));
660        }
661        if id == Some(false) {
662            if let Value::Object(m) = &mut out {
663                m.remove("_id");
664            }
665        }
666        Ok(out)
667    }
668}
669
670/// 投影值 → 包含(bool=true) / 排除(bool=false);仅接受 `0/1` 与 `false/true`。
671fn projection_flag(v: &Value) -> Result<bool, String> {
672    match v {
673        Value::Bool(b) => Ok(*b),
674        Value::Number(n) => match (n.as_i64(), n.as_f64()) {
675            (Some(0), _) | (_, Some(0.0)) => Ok(false),
676            (Some(1), _) | (_, Some(1.0)) => Ok(true),
677            _ => Err(format!("投影值必须是 0/1 或 false/true,收到 {n}")),
678        },
679        other => Err(format!(
680            "不支持的投影形态: {other}(本地求值器仅支持包含/排除,拒绝静默)"
681        )),
682    }
683}
684
685/// 按剩余段对 `doc` 做包含投影,返回投影后的子树;路径不存在 → `None`。
686/// 遇数组:逐元素按同段继续(Mongo 数组映射语义的简化),无命中项跳过。
687fn project_included(doc: &Value, segs: &[&str]) -> Option<Value> {
688    if segs.is_empty() {
689        return Some(doc.clone());
690    }
691    match doc {
692        Value::Object(o) => {
693            let child = o.get(segs[0])?;
694            let sub = project_included(child, &segs[1..])?;
695            let mut m = Map::new();
696            m.insert(segs[0].to_string(), sub);
697            Some(Value::Object(m))
698        }
699        Value::Array(a) => {
700            let mut arr = Vec::with_capacity(a.len());
701            for el in a {
702                if let Some(v) = project_included(el, segs) {
703                    arr.push(v);
704                }
705            }
706            Some(Value::Array(arr))
707        }
708        _ => None,
709    }
710}
711
712/// 包含式取字段(点号路径,嵌套还原);路径不存在 → `None`。
713///
714/// 供聚合投影(`$project` 含表达式值)按路径并入输出文档使用。
715pub fn project_include(doc: &Value, path: &str) -> Option<Value> {
716    project_included(doc, &segments(path))
717}
718
719/// 把投影子树并入输出文档;同名对象递归合并,其余覆盖。
720pub fn merge_into(dst: &mut Map<String, Value>, src: Value) {
721    let Value::Object(src) = src else {
722        return;
723    };
724    for (k, v) in src {
725        let nested = matches!(dst.get(&k), Some(Value::Object(_))) && v.is_object();
726        if nested {
727            if let Some(Value::Object(d)) = dst.get_mut(&k) {
728                merge_into(d, v);
729            }
730        } else {
731            dst.insert(k, v);
732        }
733    }
734}
735
736/// 按段做排除投影(原地);遇数组对每个元素继续。
737pub fn remove_path(doc: &mut Value, path: &str) {
738    remove_segments(doc, &segments(path));
739}
740
741/// 按段路径写入值(`$addFields` / `$unwind` 用):中间层不存在或非对象时补建为对象。
742pub fn set_path(doc: &mut Value, path: &str, val: Value) {
743    set_segments(doc, &segments(path), val);
744}
745
746fn set_segments(cur: &mut Value, segs: &[&str], val: Value) {
747    if segs.is_empty() {
748        *cur = val;
749        return;
750    }
751    if !cur.is_object() {
752        *cur = Value::Object(Map::new());
753    }
754    let Value::Object(o) = cur else { return };
755    if segs.len() == 1 {
756        o.insert(segs[0].to_string(), val);
757        return;
758    }
759    let entry = o
760        .entry(segs[0].to_string())
761        .or_insert_with(|| Value::Object(Map::new()));
762    set_segments(entry, &segs[1..], val);
763}
764
765fn remove_segments(doc: &mut Value, segs: &[&str]) {
766    if segs.is_empty() {
767        return;
768    }
769    match doc {
770        Value::Object(o) => {
771            if segs.len() == 1 {
772                o.remove(segs[0]);
773            } else if let Some(v) = o.get_mut(segs[0]) {
774                remove_segments(v, &segs[1..]);
775            }
776        }
777        Value::Array(a) => {
778            for el in a {
779                remove_segments(el, segs);
780            }
781        }
782        _ => {}
783    }
784}
785
786fn type_name(v: &Value) -> &'static str {
787    match v {
788        Value::Null => "null",
789        Value::Bool(_) => "bool",
790        Value::Number(_) => "number",
791        Value::String(_) => "string",
792        Value::Array(_) => "array",
793        Value::Object(_) => "object",
794    }
795}
796
797#[cfg(test)]
798mod tests {
799    use super::*;
800    use serde_json::json;
801
802    #[test]
803    fn max_docs_guard_constant() {
804        assert_eq!(MAX_LOCAL_COLLECTION_DOCS, 100_000);
805    }
806
807    #[test]
808    fn get_path_three_states() {
809        let doc = json!({ "a": null, "b": { "c": 1 } });
810        // 显式 null:Some(Null);缺失:None
811        assert_eq!(get_path(&doc, "a"), Some(&Value::Null));
812        assert_eq!(get_path(&doc, "missing"), None);
813        assert_eq!(get_path(&doc, "b.missing"), None);
814        assert_eq!(get_path(&doc, "b.c"), Some(&json!(1)));
815        // 严格取值不下钻数组
816        let arr = json!({ "a": [ { "b": 1 } ] });
817        assert_eq!(get_path(&arr, "a.b"), None);
818    }
819
820    #[test]
821    fn candidates_three_states_and_array_expansion() {
822        let doc = json!({ "a": null, "arr": [ { "b": 1 }, { "b": 2 } ] });
823        // 缺失 → 空;显式 null → [Null]
824        assert!(resolve_path_candidates(&doc, "missing").is_empty());
825        assert_eq!(resolve_path_candidates(&doc, "a"), vec![&Value::Null]);
826        // 数组下钻:a.b 命中每个元素
827        assert_eq!(
828            resolve_path_candidates(&doc, "arr.b"),
829            vec![&json!(1), &json!(2)]
830        );
831        // 末段为数组字段:候选为数组整体
832        let tags = json!({ "tags": ["x", "y"] });
833        assert_eq!(
834            resolve_path_candidates(&tags, "tags"),
835            vec![&json!(["x", "y"])]
836        );
837    }
838
839    #[test]
840    fn bson_type_order_across_types() {
841        let min = json!({ "$minKey": 1 });
842        let null = Value::Null;
843        let num = json!(5);
844        let s = json!("abc");
845        let obj = json!({ "x": 1 });
846        let arr = json!([1, 2]);
847        let boolean = json!(true);
848        let date = json!({ "$date": 0 });
849        let max = json!({ "$maxKey": 1 });
850        let seq = [&min, &null, &num, &s, &obj, &arr, &boolean, &date, &max];
851        for (i, a) in seq.iter().enumerate() {
852            for (j, b) in seq.iter().enumerate() {
853                assert_eq!(compare(a, b), i.cmp(&j), "rank {i} vs {j}");
854            }
855        }
856    }
857
858    #[test]
859    fn number_order_across_int_and_float() {
860        assert_eq!(compare(&json!(1), &json!(1.0)), Ordering::Equal);
861        assert!(values_equal(&json!(1), &json!(1.0)));
862        assert_eq!(compare(&json!(2), &json!(1.5)), Ordering::Greater);
863        assert_eq!(compare(&json!(-1), &json!(0)), Ordering::Less);
864        // 扩展 Long 与普通数字同序
865        assert!(values_equal(&json!({ "$numberLong": "3" }), &json!(3)));
866    }
867
868    #[test]
869    fn compare_object_and_array() {
870        assert_eq!(
871            compare(&json!({ "a": 1 }), &json!({ "a": 2 })),
872            Ordering::Less
873        );
874        // 前缀相等 → 短者小
875        assert_eq!(
876            compare(&json!({ "a": 2 }), &json!({ "a": 2, "b": 1 })),
877            Ordering::Less
878        );
879        assert_eq!(compare(&json!([1, 2]), &json!([1, 2, 3])), Ordering::Less);
880        assert_eq!(compare(&json!([1, 3]), &json!([1, 2])), Ordering::Greater);
881    }
882
883    #[test]
884    fn array_eq_hit_helper() {
885        // 数组字段等值命中(Mongo 语义):候选为数组,元素命中即真
886        assert!(matches_value(&json!(["x", "y"]), &json!("x")));
887        assert!(matches_value(&json!("x"), &json!("x")));
888        assert!(!matches_value(&json!(["y"]), &json!("x")));
889    }
890
891    #[test]
892    fn eval_expr_minimal() {
893        let doc = json!({ "x": 1, "y": 2 });
894        let vars = Map::new();
895        assert_eq!(
896            eval_expr(&json!({ "$eq": ["$x", 1] }), &doc, &vars).unwrap(),
897            json!(true)
898        );
899        assert_eq!(
900            eval_expr(&json!({ "$eq": ["$x", "$y"] }), &doc, &vars).unwrap(),
901            json!(false)
902        );
903        assert_eq!(
904            eval_expr(
905                &json!({ "$and": [ { "$eq": ["$x", 1] }, { "$eq": ["$y", 2] } ] }),
906                &doc,
907                &vars
908            )
909            .unwrap(),
910            json!(true)
911        );
912        // $ifNull:缺失字段 → 回退值
913        assert_eq!(
914            eval_expr(&json!({ "$ifNull": ["$missing", "d"] }), &doc, &vars).unwrap(),
915            json!("d")
916        );
917        // $$var 引用
918        let mut vars2 = Map::new();
919        vars2.insert("v".to_string(), json!(42));
920        assert_eq!(eval_expr(&json!("$$v"), &doc, &vars2).unwrap(), json!(42));
921        assert_eq!(
922            eval_expr(&json!({ "$eq": ["$x", "$$v"] }), &doc, &vars2).unwrap(),
923            json!(false)
924        );
925        // 未知算子 → Err(禁静默)
926        assert!(eval_expr(&json!({ "$mul": [1, 2] }), &doc, &vars).is_err());
927    }
928
929    #[test]
930    fn expr_size() {
931        let vars = Map::new();
932        let doc = json!({ "arr": [1, 2, 3], "empty": [], "n": 5 });
933        assert_eq!(eval_expr(&json!({ "$size": "$arr" }), &doc, &vars).unwrap(), json!(3));
934        assert_eq!(eval_expr(&json!({ "$size": "$empty" }), &doc, &vars).unwrap(), json!(0));
935        // 非数组 → Err(Mongo 同样抛错,禁静默)
936        assert!(eval_expr(&json!({ "$size": "$n" }), &doc, &vars).is_err());
937        assert!(eval_expr(&json!({ "$size": "$missing" }), &doc, &vars).is_err());
938    }
939
940    #[test]
941    fn expr_sum_avg_min_max_array_form() {
942        let vars = Map::new();
943        let doc = json!({
944            "scores": [3, 7, 5],
945            "mixed": [1, "x", 4, null],
946            "empty": [],
947            "gone": null,
948            "one": 9
949        });
950        // $sum:整数值 → 整数形态(对齐 pipeline.rs::accumulate 的 number_of)
951        assert_eq!(eval_expr(&json!({ "$sum": "$scores" }), &doc, &vars).unwrap(), json!(15));
952        // 忽略非数值与 null;空数组 → 0;缺失/null → 0
953        assert_eq!(eval_expr(&json!({ "$sum": "$mixed" }), &doc, &vars).unwrap(), json!(5));
954        assert_eq!(eval_expr(&json!({ "$sum": "$empty" }), &doc, &vars).unwrap(), json!(0));
955        assert_eq!(eval_expr(&json!({ "$sum": "$gone" }), &doc, &vars).unwrap(), json!(0));
956        assert_eq!(eval_expr(&json!({ "$sum": "$missing" }), &doc, &vars).unwrap(), json!(0));
957        // $avg:恒浮点;空集/无可用值 → null
958        assert_eq!(eval_expr(&json!({ "$avg": "$scores" }), &doc, &vars).unwrap(), json!(5.0));
959        assert_eq!(eval_expr(&json!({ "$avg": "$empty" }), &doc, &vars).unwrap(), Value::Null);
960        assert_eq!(eval_expr(&json!({ "$avg": "$missing" }), &doc, &vars).unwrap(), Value::Null);
961        // $min / $max:BSON 序极值,忽略 null;空集 → null
962        assert_eq!(eval_expr(&json!({ "$min": "$scores" }), &doc, &vars).unwrap(), json!(3));
963        assert_eq!(eval_expr(&json!({ "$max": "$scores" }), &doc, &vars).unwrap(), json!(7));
964        assert_eq!(eval_expr(&json!({ "$min": "$mixed" }), &doc, &vars).unwrap(), json!(1));
965        assert_eq!(eval_expr(&json!({ "$min": "$empty" }), &doc, &vars).unwrap(), Value::Null);
966        // 单值形态 = 单元素数组($sum(9) → 9 整数;$avg(9) → 9.0 浮点)
967        assert_eq!(eval_expr(&json!({ "$sum": "$one" }), &doc, &vars).unwrap(), json!(9));
968        assert_eq!(eval_expr(&json!({ "$avg": "$one" }), &doc, &vars).unwrap(), json!(9.0));
969        assert_eq!(eval_expr(&json!({ "$min": "$one" }), &doc, &vars).unwrap(), json!(9));
970    }
971
972    #[test]
973    fn projection_include_nested() {
974        let doc = json!({ "_id": 7, "a": { "b": 1, "c": 2 }, "d": 3 });
975        // 嵌套包含:仅保留 a.b
976        assert_eq!(
977            apply_projection(&doc, &json!({ "a.b": 1 })).unwrap(),
978            json!({ "_id": 7, "a": { "b": 1 } })
979        );
980        // 包含式默认带 _id
981        assert_eq!(
982            apply_projection(&doc, &json!({ "d": 1 })).unwrap(),
983            json!({ "_id": 7, "d": 3 })
984        );
985        // _id:0 与包含式混用
986        assert_eq!(
987            apply_projection(&doc, &json!({ "d": 1, "_id": 0 })).unwrap(),
988            json!({ "d": 3 })
989        );
990    }
991
992    #[test]
993    fn projection_exclude_and_errors() {
994        let doc = json!({ "_id": 7, "a": { "b": 1, "c": 2 }, "d": 3 });
995        // 嵌套排除
996        assert_eq!(
997            apply_projection(&doc, &json!({ "a.b": 0 })).unwrap(),
998            json!({ "_id": 7, "a": { "c": 2 }, "d": 3 })
999        );
1000        // 仅 _id:0 → 去掉 _id,其余保留
1001        assert_eq!(
1002            apply_projection(&doc, &json!({ "_id": 0 })).unwrap(),
1003            json!({ "a": { "b": 1, "c": 2 }, "d": 3 })
1004        );
1005        // 非 _id 的包含/排除混用 → Err
1006        assert!(apply_projection(&doc, &json!({ "a": 1, "d": 0 })).is_err());
1007        // 非法投影值 → Err
1008        assert!(apply_projection(&doc, &json!({ "a": 2 })).is_err());
1009    }
1010}