首页/数据/NoSQL 速查/$lookup 管道式关联
MongoDB99 条

$lookup 管道式关联

进阶写法用 let + pipeline 在关联侧再做过滤、排序与截断,适合“每个主文档只关联少量子文档”的场景。

NoSQL 速查数据更新于 2026-10-13

管道式关联的价值

$lookup 有两种写法:简单的 localField + foreignField 等值关联,以及用 let + pipeline 的管道式写法。后者可以在关联侧做过滤、排序与截断,适合「每个主文档只关联少量子文档」的场景,比如每张订单只取最近一条物流记录,避免先把子文档全部拉回来再截断。

基本信息

写法{ $lookup: { from, let, pipeline: [...], as } }
变量引用pipeline 内用 $$变量名 引用 let 定义的字段
优势关联侧先过滤再返回,减少结果膨胀与内存占用
位置可作为聚合管道中的阶段,通常放在已收敛数据之后
适用版本3.6 及以上
配合写法关联侧条件常用 $expr 表达字段与变量的比较

典型使用场景

  • 每单带最近一条状态记录:只取最新一条,避免数组膨胀
  • 关联侧聚合:在子文档上先分组汇总再返回结果
  • 复杂关联条件:多字段或不等值条件用 $expr 表达
  • 关联侧限量:限制返回条数控制结果体积
  • 替代多次查询:把原本多轮查询合并到一次管道执行

每单带最近一条物流

每单带最近一条物流
db.orders.aggregate([
  { $match: { status: "paid" } },                        // 先收敛主文档
  { $lookup: {
      from: "shipments",
      let: { orderNo: "$orderNo" },
      pipeline: [
        { $match: { $expr: { $eq: ["$orderNo", "$$orderNo"] } } },
        { $sort: { ts: -1 } },
        { $limit: 1 }                                      // 每个主文档只保留最新一条
      ],
      as: "latestShipment"
  } },
  { $project: { orderNo: 1, status: 1, latestShipment: { $arrayElemAt: ["$latestShipment", 0] } } }
])

两种写法对照

写法适用注意点
localField + foreignField简单等值关联关联侧无索引时成本高,返回结果可能膨胀
let + pipeline需要过滤、排序、限量或复杂条件内部多用 $expr,优化器能力有限,先小范围验证
关联字段索引两种写法都受益关联集合上的匹配字段应建立索引
结果展开用 $arrayElemAt 或 $unwind 处理数组注意 $unwind 会成倍放大行数
管道式关联的收益来自「关联侧先收敛」:在子集合上过滤与排序后再返回,避免把大量子文档拉进内存再截断,大比例减少内存与耗时。
$$变量名拼写错误通常不报明确错误,而是静默返回空数组:排查时先核对 let 与 pipeline 里的变量名是否完全一致。

易错点

  • $$变量名拼错:结果为空数组且不报错,排查方向容易被误导到数据本身
  • 关联侧没有索引:管道式写法同样要为每个主文档扫描子集合,成本随主文档数放大
  • 用 $unwind 展开关联结果:行数成倍增长,应在关联侧就限制条数
  • 把 $lookup 放在管道最前面:主文档尚未收敛,关联执行次数被显著放大

数据来源与参考

  • MongoDB Manual: $lookup with pipeline
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌