不相关子查询指$lookup中pipeline的匹配条件不依赖当前文档字段,如固定{status:"active"};MongoDB 3.6起支持pipeline但无法传参,5.0新增let允许通过$$引用外层字段实现动态关联,同时带来索引使用、性能及兼容性等新约束。
“不相关子查询”指 $lookup 的连接条件不依赖于当前文档的字段,比如固定匹配 { status: "active" },或完全由管道内逻辑生成数据。MongoDB 3.6 之前只支持「相关联」的等值连接(localField/foreignField),无法脱离当前文档做任意条件查;3.6 引入了 pipeline 字段,才真正支持不相关子查询——但此时 pipeline 内无法引用外层文档字段,只能写死条件。
let + pipeline 解决了什么问题5.0 在原有 pipeline 语法基础上加了 let 参数,允许把当前文档字段绑定为变量,在 pipeline 内通过 $$变量名 引用。这解决了 3.6 版本下“能写管道但不能传参”的硬伤。
user_id):{ $lookup: { from: "orders", pipeline: [ { $match: { status: "paid" } } ], as: "paid_orders" } }
{ $lookup: { from: "orders", let: { uid: "$user_id" }, pipeline: [ { $match: { $expr: { $eq: ["$user_id", "$$uid"] } } } ], as: "user_orders" } }
$expr 是关键:它让 $match 支持表达式运算,否则 $$uid 在普通 $match 里会被当作文本字面量let 绑定的是字段路径值,不是整个字段对象;若 $user_id 为 null 或缺失,$$uid 就是 null,匹配结果为空数组let/pipeline
语法灵活了,但性能代价更隐蔽:
pipeline 内若没建好索引(尤其 $expr 中的字段),很容易全表扫 from 集合let 变量不能用于 $sort 或 $limit 的字段推导,那些阶段仍需靠 from 集合自身索引支撑localField/foreignField 模式可自动利用两集合上对应字段的索引;5.0 的 pipeline 模式只认 from 集合的索引,且仅当 $match 在 pipeline 最前且含可索引字段时才生效localField/foreignField 更稳——5.0 并未废弃旧语法,两者可混用看似只是多一个 let,但实际部署时几个点常踩坑:
1.12+ 才完整支持 5.0 的 let 语法,低版本会静默忽略 let 字段,导致查不到数据pipeline 内不能用 $lookup 嵌套(即子 pipeline 不能再 $lookup),否则报错 Unrecognized pipeline stage name: '$lookup'(MongoDB 5.0 不支持递归 join)as 字段输出始终是数组,哪怕只匹配一条;若业务假定是单文档,得加 $arrayElemAt: ["$user_orders", 0] 提取,否则后续 $project 可能出错maxTimeMS)按整个 pipeline 计,不是按单次 $lookup 子查询;复杂 pipeline + 高并发易触发超时let:多数一对多关联用老式 localField 就够了;只有涉及多字段组合、范围判断、或子集合预过滤时,才值得引入 pipeline + let,并务必在 from 集合上补好复合索引。