$lookup 关联
本教程共 50 篇 · 第 32 篇 · 更新于 2026-07-30 · 约 8 分钟阅读
32. $lookup 关联
本节目标:学会用 $lookup 把两个集合按字段关联起来,掌握基础的 localField/foreignField 写法,以及更灵活的 let/pipeline 写法,理解它本质是「左外连接」。
MongoDB 不强制建外键,但经常需要「订单连用户」「订单连商品」。聚合里的 $lookup 就是干这个的,类似 SQL 的 LEFT JOIN——左集合的每条文档都会保留,匹配不到右集合时就给个空数组。
db.users.insertMany([
{ _id: 1, name: "张三", city: "北京" },
{ _id: 2, name: "李四", city: "上海" },
{ _id: 3, name: "王五", city: "北京" }
])
db.orders.insertMany([
{ _id: 1001, user_id: 1, total: 657 },
{ _id: 1002, user_id: 2, total: 1099 },
{ _id: 1003, user_id: 9, total: 597 } // user_id 9 在 users 里不存在
])
基础写法:localField / foreignField
四个必填字段:from(右集合)、localField(左集合字段)、foreignField(右集合字段)、as(结果存到哪个新数组字段)。
// 把 orders 和 users 按 user_id = _id 关联,结果放进 buyer 数组
db.orders.aggregate([
{
$lookup: {
from: "users",
localField: "user_id",
foreignField: "_id",
as: "buyer"
}
}
])
每条订单会多出一个 buyer 数组:能匹配到的装进对应用户文档,_id:1003 因为 user_id:9 没对应,得到 buyer: []。
Note结果永远是「数组」,哪怕只匹配到一条。因为 MongoDB 允许一对多。后续想取第一条,用
$unwind或$arrayElemAt。
高级写法:let / pipeline
基础写法只能做等值匹配。如果匹配逻辑更复杂(比如带过滤、带投影),用 let 定义变量,再在 pipeline 里写子管道。
// 关联用户,但只取名字和城市,且只关联北京的用户
db.orders.aggregate([
{
$lookup: {
from: "users",
let: { uid: "$user_id" },
pipeline: [
{ $match: { $expr: { $eq: ["$_id", "$$uid"] } } },
{ $match: { city: "北京" } },
{ $project: { _id: 0, name: 1, city: 1 } }
],
as: "buyer"
}
}
])
这里 let 把左集合的 user_id 命名为 uid,子管道里用 $$uid 引用(双美元符表示外部变量)。
Tip子管道里能写任意阶段(
$match、$project、$sort等),比基础写法灵活得多。8.3 两种写法都支持,按需选用。
orders 关联 products(数组字段)
订单里的商品存在 items 数组,要逐行关联商品表,得先 $unwind 拆行(下章细讲),再 $lookup:
db.products.insertMany([
{ _id: 101, name: "机械键盘", price: 399 },
{ _id: 102, name: "无线鼠标", price: 129 }
])
db.orders.insertOne({
_id: 1006,
items: [
{ product: "机械键盘", qty: 1 },
{ product: "无线鼠标", qty: 2 }
]
})
// 把每行商品名关联到 products 表
db.orders.aggregate([
{ $unwind: "$items" },
{
$lookup: {
from: "products",
localField: "items.product",
foreignField: "name",
as: "productInfo"
}
}
])
Warning
$lookup是「左外连接」:左集合文档一定保留。若想只保留关联成功的,要在后面加$match: { buyer: { $ne: [] } }过滤掉空数组。
两种写法对比
| 写法 | 匹配能力 | 适用 |
|---|---|---|
localField/foreignField | 仅等值 | 简单外键关联 |
let/pipeline | 任意子管道逻辑 | 带过滤、投影、复杂条件 |
关联是聚合里信息量最大的一步。下一章讲 $unwind,它是配合 $lookup 和数组统计的常用搭档。