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

MongoDB如何对双层嵌套对象数组执行$lookup关联查询

MongoDB聚合查询:关联嵌套数组并保留自定义字段

我有一个数据模型,每个product包含多个variant,每个variant又包含多个modification。数据库中的数据结构如下:

const mods = db.modifications.insertMany([
  {
    title: 'Modification #1',
    image: 'img1.png',
  },
  {
    title: 'Modification #2',
    image: 'img2.png',
  },
  {
    title: 'Modification #3',
    image: 'img3.png',
  },
])

db.products.insertOne({
  slug: 'product1',
  title: 'Product #1',
  variants: [
    {
      size: 20,
      price: 200,
      modifications: [
        { id: mods.insertedIds[0], price: 10 },
        { id: mods.insertedIds[1], price: 15 },
      ],
    },
    {
      size: 30,
      price: 250,
      modifications: [
        { id: mods.insertedIds[0], price: 15 },
        { id: mods.insertedIds[2], price: 20 },
      ],
    },
  ],
})

需要执行聚合查询,将variants.modifications中的id关联到modifications集合的完整数据,同时保留每个modification对应的price字段,最终得到如下格式的结果:

{
  slug: 'product1',
  title: 'Product #1',
  variants: [
    {
      size: 20,
      price: 200,
      modifications: [
        { _id: '…', title: 'Modification #1', image: '…', price: 10 },
        { _id: '…', title: 'Modification #2', image: '…', price: 15 },
      ],
    },
    {
      size: 30,
      price: 250,
      modifications: [
        { _id: '…', title: 'Modification #1', image: '…', price: 15 },
        { _id: '…', title: 'Modification #3', image: '…', price: 20 },
      ],
    },
  ],
}

我曾尝试先执行两次$unwind,再执行$lookup:

db.products.aggregate([
  { $match: { slug: 'product1' } },
  { $unwind: '$variants' },
  { $unwind: '$variants.modifications' },
  {
    $lookup: {
      from: 'modifications',
      localField: 'variants.modifications.id',
      foreignField: '_id',
      let: { price: '$variants.modifications.price' },
      pipeline: [{ $addFields: { price: '$$price' } }],
      as: 'modifications',
    },
  },
])

但不知道如何通过$group将数据重新组合成目标格式。


解决方案

可以通过嵌套的$lookup结合$map和$mergeObjects实现,无需多次$unwind和$group,步骤如下:

  1. 使用$lookup关联modifications集合,获取所有相关的修改项数据
  2. 遍历variants数组,对每个variant的modifications数组进行映射,将id匹配到对应的修改项,并合并自定义的price字段

完整聚合查询代码:

db.products.aggregate([
  { $match: { slug: 'product1' } },
  // 关联modifications集合,获取所有相关的修改数据
  {
    $lookup: {
      from: 'modifications',
      localField: 'variants.modifications.id',
      foreignField: '_id',
      as: 'modsData'
    }
  },
  // 处理variants数组,将每个modification关联到完整数据并保留price
  {
    $addFields: {
      variants: {
        $map: {
          input: '$variants',
          as: 'variant',
          in: {
            // 保留variant的原有字段
            size: '$$variant.size',
            price: '$$variant.price',
            modifications: {
              $map: {
                input: '$$variant.modifications',
                as: 'mod',
                in: {
                  // 匹配对应的modsData项,合并price字段
                  $mergeObjects: [
                    { $first: { $filter: {
                      input: '$modsData',
                      cond: { $eq: ['$$this._id', '$$mod.id'] }
                    }}},
                    { price: '$$mod.price' }
                  ]
                }
              }
            }
          }
        }
      },
      // 移除临时的modsData字段
      modsData: '$$REMOVE'
    }
  }
])

代码解释

  • $lookup阶段:一次性关联所有与当前产品相关的修改项数据,存入modsData临时字段,避免多次关联的性能损耗。
  • 外层$map:遍历每个variant,保留原有size和price字段,同时处理内部的modifications数组。
  • 内层$map:遍历每个modification,通过$filter从modsData中匹配对应的完整修改项,再用$mergeObjects将自定义的price字段合并进去,覆盖(或补充)原有数据。
  • $$REMOVE:清理临时的modsData字段,保证最终输出结构整洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 14:20:34