You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MongoDB聚合查询:关联两个集合并获取子集合指定字段

MongoDB聚合实现Contacts与Companies关联查询

问题背景

需要将Contacts集合与Companies集合通过companyId关联,把Companies中的companyName和domain字段合并到对应的Contact文档中,替代原有的TypeScript循环逻辑。

集合结构

Contacts集合示例

{
  _id: ObjectId('2341908342'),
  fName: "Test",
  lName: "TestTwo",
  companyId: "61f4147b8415abfc04d09207",
  orgId: ObjectId("xxx")
},
{
  _id: ObjectId('2341908343'),
  fName: "John",
  lName: "Doe",
  companyId: "62847a16ee5c5047820415d2",
  orgId: ObjectId("xxx")
}

Companies集合示例

{
  _id: ObjectId('61f4147b8415abfc04d09207'),
  companyName: "Facebook",
  domain: "www.facebook.com",
},
{
  _id: ObjectId('62847a16ee5c5047820415d2'),
  companyName: "Google",
  domain: "www.Google.com",
}

期望输出

{
  _id: ObjectId('2341908342'),
  fName: "Test",
  lName: "TestTwo",
  companyId: "61f4147b8415abfc04d09207",
  companyName: "Facebook",
  domain: "www.facebook.com",
  orgId: ObjectId("xxx")
}

原代码错误分析

你提供的聚合代码存在以下问题:

  • let语句错误:取了Contacts的_id转换为字符串,而应该取companyId字段
  • 匹配条件错误:Companies集合中没有companyId字段,应该用$_id转换为字符串后与Contacts的companyId匹配
  • as字段命名不合理:lookup结果是数组,应该用类似companyInfo的名称,而非companyName

正确聚合实现方案

以下是与原TypeScript逻辑完全一致的聚合代码:

try {
  const allContacts = await contactsCollection.aggregate([
    // 过滤当前组织的联系人
    {
      $match: {
        orgId: new ObjectId(req.user.orgId)
      }
    },
    // 关联Companies集合,匹配对应公司
    {
      $lookup: {
        from: "companies",
        let: { contactCompanyId: "$companyId" }, // 引用当前Contact的companyId
        pipeline: [
          {
            $match: {
              $expr: {
                // 将Companies的_id转为字符串,与Contact的companyId(字符串)匹配
                $eq: [{ $toString: "$_id" }, "$$contactCompanyId"]
              }
            }
          },
          // 仅返回需要的字段,减少数据量
          {
            $project: {
              _id: 0,
              companyName: 1,
              domain: 1
            }
          }
        ],
        as: "companyInfo" // 关联结果存入数组
      }
    },
    // 展开关联数组,保留无匹配公司的联系人
    {
      $unwind: {
        path: "$companyInfo",
        preserveNullAndEmptyArrays: true
      }
    },
    // 将公司字段合并到根文档
    {
      $addFields: {
        companyName: "$companyInfo.companyName",
        domain: "$companyInfo.domain"
      }
    },
    // 移除临时的companyInfo字段(可选)
    {
      $project: {
        companyInfo: 0
      }
    }
  ]).toArray();
} catch (err) {
  console.error('聚合查询失败:', err);
}

代码逻辑说明

  1. $match:过滤出当前用户所属组织的联系人
  2. $lookup:关联Companies集合,通过转换字段类型实现字符串companyId与ObjectId_id的匹配,同时仅返回需要的companyName和domain字段
  3. $unwind:展开关联结果数组,preserveNullAndEmptyArrays: true确保即使没有匹配到公司,联系人文档也会被保留
  4. $addFields:将关联得到的公司字段合并到联系人根文档中
  5. $project:移除临时的companyInfo数组字段,使输出结构更简洁

内容的提问来源于stack exchange,提问作者CameronWood23

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 19:40:27