首页 / MongoDB 入门教程 / $lookup 关联

MongoDB 入门教程

$lookup 关联

本教程共 50 篇 · 第 32 篇 · 更新于 2026-07-30 · 约 8 分钟阅读

MongoDBMongoDB 入门教程lookup关联左外连接pipeline

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 和数组统计的常用搭档。