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

MongoDB聚合:如何将lookup结果存入嵌套数组?

MongoDB聚合:将嵌套数组中的参与者ID替换为关联对象

我需要把文档中schedules数组里每个schedule的participants(ID数组)替换为从customers集合查询到的用户对象,最终得到如下格式的结果:

{
  "name": "EXAMPLE",
  "schedules": [
    {
      "schedule_id": "id1",
      "participants": [
        { "_id": "participant_id1", "name": "name1" },
        { "_id": "participant_id2", "name": "name2" }
      ]
    },
    {
      "schedule_id": "id2",
      "participants": [
        { "_id": "participant_id1", "name": "name1" },
        { "_id": "participant_id2", "name": "name2" }
      ]
    },
    {
      "schedule_id": "id3",
      "participants": [
        { "_id": "participant_id1", "name": "name1" }
      ]
    }
  ]
}

原有的聚合管道存在逻辑问题:分组时用$first只能拿到单个schedule和参与者,无法将同一schedule下的多个参与者重新聚合回数组,达不到预期效果。以下是修正后的两种实现方案:


方案一:MongoDB 3.6+ 版本(推荐)

利用3.6+版本支持的数组字段直接关联特性,无需多次展开数组,简化流程:

[
  // 展开schedules数组,单独处理每个schedule条目
  {
    $unwind: {
      path: "$schedules",
      preserveNullAndEmptyArrays: true
    }
  },
  // 直接用schedule的participants数组关联customers集合,返回匹配的用户对象数组
  {
    $lookup: {
      from: "customers",
      localField: "schedules.participants",
      foreignField: "_id",
      as: "schedules.participant_details"
    }
  },
  // 过滤掉参与者对象中不需要的字段(如address、birthday)
  {
    $project: {
      name: 1,
      "schedules.schedule_id": 1,
      "schedules.participant_details._id": 1,
      "schedules.participant_details.name": 1
    }
  },
  // 把临时字段重命名为participants,覆盖原ID数组,并删除临时字段
  {
    $addFields: {
      "schedules.participants": "$schedules.participant_details",
      "schedules.participant_details": "$$REMOVE"
    }
  },
  // 重新分组,将单个schedule条目聚合回数组,恢复原文档结构
  {
    $group: {
      _id: "$_id",
      name: { $first: "$name" },
      schedules: { $push: "$schedules" }
    }
  }
]

步骤说明

  1. 展开schedules数组:将嵌套的schedules拆分为单个文档,方便单独处理每个schedule的参与者关联。
  2. 关联customers集合:直接用当前schedule的participants(ID数组)匹配customers的_id,返回的用户对象存入临时字段,省去提前展开participants的步骤。
  3. 过滤冗余字段:只保留需要的字段,移除不必要的参与者属性。
  4. 重命名字段:替换掉原有的ID数组,恢复schedule的预期结构。
  5. 重新聚合文档:按原文档ID分组,用$push将处理后的schedule条目重新组合成数组。

方案二:兼容MongoDB 3.6以下版本

如果你的MongoDB版本不支持数组字段关联,需要通过两次分组来聚合参与者数组:

[
  { $unwind: { path: "$schedules", preserveNullAndEmptyArrays: true } },
  // 展开每个schedule的participants数组,单个条目对应一个参与者ID
  { $unwind: { path: "$schedules.participants", preserveNullAndEmptyArrays: true } },
  // 关联单个参与者对象
  {
    $lookup: {
      from: "customers",
      localField: "schedules.participants",
      foreignField: "_id",
      as: "participant"
    }
  },
  { $unwind: { path: "$participant", preserveNullAndEmptyArrays: true } },
  // 过滤冗余字段
  {
    $project: {
      name: 1,
      "schedules.schedule_id": 1,
      "participant._id": 1,
      "participant.name": 1
    }
  },
  // 第一次分组:按文档ID+schedule_id聚合,把同一schedule的参与者组合成数组
  {
    $group: {
      _id: { _id: "$_id", schedule_id: "$schedules.schedule_id" },
      name: { $first: "$name" },
      participants: { $push: "$participant" }
    }
  },
  // 第二次分组:按文档ID聚合,把schedule条目组合成数组
  {
    $group: {
      _id: "$_id._id",
      name: { $first: "$name" },
      schedules: {
        $push: {
          schedule_id: "$_id.schedule_id",
          participants: "$participants"
        }
      }
    }
  }
]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 09:01:31