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

MongoDB 4.4多层嵌套聚合查询报错,求正确实现方案

MongoDB 4.4 嵌套关联聚合查询问题解决

问题背景

使用MongoDB 4.4,需要编写聚合查询,根据_id查询offer文档,并返回包含嵌套的modules、每个module对应的items、每个item对应的notes的结构。

各集合结构如下:

// offers集合
{
    _id: '64a6c6ed3f24ff001b74cf9d',
    name: 'offer name',
}

// modules集合
{
    _id: '64a6c6ed3f24ff001b74cfab',
    name: 'module name',
    offerId: '64a6c6ed3f24ff001b74cf9d',
}

// items集合
{
    _id: '5e984aea5e6df000399e6b97',
    name: 'item name',
    moduleId: '64a6c6ed3f24ff001b74cfab',
}

// notes集合
{
    _id: '64c78caf0e5791001b15833c',
    name: 'note name',
    itemId: '5e984aea5e6df000399e6b97',
}

期望返回结构:

{
  _id: "64a6c6ed3f24ff001b74cf9d",
  name: "offer name",
  modules: [
    {
      _id: "64a6c6ed3f24ff001b74cfab",
      name: "module name",
      items: [
        {
          _id: "5e984aea5e6df000399e6b97",
          name: "item name",
          notes: [
            {
              _id: "64c78caf0e5791001b15833c",
              name: "note name"
            },
            ...
          ]
        },
        ...
      ]
    },
    ...
  ]
}

编写的聚合查询执行时出现错误:Unrecognized expression '$push',查询代码如下:

db.getCollection("offers").aggregate([
  {
    $match: {
        _id: ObjectId('64a6c6ed3f24ff001b74cf9d'),
    }
  },
  {
      $lookup: {
          from: 'modules',
          localField: '_id',
          foreignField: 'offerId',
          as: 'modules',
      },
  },
  {
      $unwind: '$modules',
  },
  {
      $lookup: {
          from: 'items',
          localField: 'modules._id',
          foreignField: 'moduleId',
          as: 'items',
      },
  },
  {
      $unwind: '$items',
  },
  {
    $lookup: {
        from: 'notes',
        localField: 'items._id',
        foreignField: 'itemId',
        as: 'notes',
    },
  },
  {
      $group: {
        _id: "$_id",
        name: { $first: "$name" },
        modules: { 
            $push: {
                _id: "$modules._id",
                name: "$modules.name",
                items: {
                    $push: {
                        _id: "$items._id",
                        name: "$items.name",
                        notes: {
                          $push: {
                              _id: "$notes._id",
                              name: "$notes.name",
                          }
                        },
                    }
                },
            }
        }
      }
  },
  {
      $project: {
        _id: 1,
        name: 1,
        modules: 1,
      }
  },
  
]);

错误原因

MongoDB的$group阶段不支持嵌套使用$push操作符。$push只能作为$group中字段的直接聚合表达式,无法在$push生成的对象内部再次使用$push来嵌套构建数组结构。

解决方案:使用嵌套管道式$lookup

MongoDB 3.6及以上版本支持带自定义管道的$lookup,可以在关联集合时直接嵌套后续的关联逻辑,无需多次$unwind和$group,更高效且能直接生成预期的嵌套结构。

正确的聚合查询代码如下:

db.getCollection("offers").aggregate([
  // 筛选目标offer
  {
    $match: {
      _id: ObjectId('64a6c6ed3f24ff001b74cf9d')
    }
  },
  // 关联modules集合,并在modules的管道内嵌套关联items和notes
  {
    $lookup: {
      from: 'modules',
      let: { offerId: '$_id' },
      pipeline: [
        { $match: { $expr: { $eq: ['$offerId', '$$offerId'] } } },
        // 在modules管道内关联items集合
        {
          $lookup: {
            from: 'items',
            let: { moduleId: '$_id' },
            pipeline: [
              { $match: { $expr: { $eq: ['$moduleId', '$$moduleId'] } } },
              // 在items管道内关联notes集合
              {
                $lookup: {
                  from: 'notes',
                  localField: '_id',
                  foreignField: 'itemId',
                  as: 'notes'
                }
              },
              // 可选:只保留items需要的字段
              {
                $project: {
                  _id: 1,
                  name: 1,
                  notes: 1
                }
              }
            ],
            as: 'items'
          }
        },
        // 可选:只保留modules需要的字段
        {
          $project: {
            _id: 1,
            name: 1,
            items: 1
          }
        }
      ],
      as: 'modules'
    }
  },
  // 可选:保留offer需要的字段
  {
    $project: {
      _id: 1,
      name: 1,
      modules: 1
    }
  }
]);

代码解释

  1. 外层$match:精准筛选目标offer,减少后续关联的数据量。
  2. 第一层$lookup(关联modules):通过let定义变量传递offer的_id,用pipeline内的$match匹配关联的modules。
  3. 第二层$lookup(关联items):在modules的管道内,继续用变量传递module的_id,关联对应的items。
  4. 第三层$lookup(关联notes):在items的管道内,直接通过localField和foreignField关联对应的notes,生成嵌套数组。
  5. $project阶段:可选,过滤掉不需要的字段,精简返回结果。

这种方式避免了多次$unwind和$group操作,逻辑清晰且性能更优,能直接生成符合预期的嵌套结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 13:05:57