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