首页/数据/NoSQL 速查/聚合 $lookup 关联查询
MongoDB99 条

聚合 $lookup 关联查询

$lookup 在管道内做左外连接:localField 与 foreignField 相等的文档放入 as 数组,替代应用侧两次查询。

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

$lookup 是什么

$lookup 在聚合管道内做左外连接:用 localField 与 from 集合的 foreignField 相等来匹配,命中的文档放进 as 指定的数组字段。它是 MongoDB 侧替代「两次查询再在应用里拼装」的标准做法。因为是左外连接,无匹配时 as 是空数组;多匹配时是数组,需要单元素时用 $unwind 拍平。

基本信息

语法{ $lookup: { from, localField, foreignField, as } }
语义左外连接,无匹配时 as 为空数组
性能前提from 集合的连接字段应有索引
结果形态匹配多条时 as 为数组;$unwind 可拍平成对象
进阶形式pipeline 加 let 可实现带条件的关联
适用版本MongoDB 3.2 起基础形式,3.6 起支持 pipeline

典型使用场景

  • 订单关联商品维表,补齐名称与规格
  • 明细关联用户表,补齐归属人与联系方式
  • 关联后过滤:只保留维表条件满足的记录
  • 聚合统计时补齐维度名称,便于报表展示
  • 数据核查:找出维表中查不到对应记录的主表文档
  • 迁移前预演多表拼装,评估结果规模

示例

示例
db.orders.aggregate([
  { $lookup: { from: "products", localField: "sku", foreignField: "sku", as: "product" } },
  { $unwind: "$product" },                                  # 单元素数组拍平
  { $project: { _id: 0, sku: 1, name: "$product.name", qty: 1 } },
  { $match: { qty: { $gt: 0 } } }
])

// 只保留维表命中的记录(等价内连接)
db.orders.aggregate([
  { $lookup: { from: "products", localField: "sku", foreignField: "sku", as: "product" } },
  { $match: { "product.0": { $exists: true } } }
])

参数与结果读法

项含义注意点
from被关联的集合名不支持跨库,需同一数据库
localField主集合连接字段值为 null 时不参与匹配
foreignFieldfrom 集合连接字段建议建索引
as结果数组字段名多匹配时数组会变长
$unwind拍平数组空数组会丢文档
$unwind 紧跟 $lookup 可把单元素数组拍平成对象形态;维表存在多条匹配时数组会被保留,业务侧要明确取哪一条。
from 侧连接字段没有索引时,$lookup 会对每条主文档逐次扫描维表,整体开销会成倍放大,是大管道最常见的性能问题。
$lookup 结果是左外连接语义:要过滤出维表没有对应记录的主表文档,可以用 $match 判断数组为空,不必额外查询。

易错点

  • 每次 $lookup 都要依据 from 集合查找,连接字段无索引会拖垮整个管道
  • $unwind 不保留空数组时,维表缺记录的文档会整条消失,若需保留要加 preserveNullAndEmptyArrays
  • 把关联结果字段直接当单值使用,遇到多条匹配会拿到数组并引发类型错误
  • $lookup 要跨数据库关联时无法实现(from 只接受同一库集合),只能先在库内同步数据
  • 在大结果集上链式多次 $lookup 会让中间文档膨胀,先 $match 缩小主表范围再关联更稳

数据来源与参考

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