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

MongoDB聚合查询:从嵌套数组提取唯一对象数组

MongoDB嵌套数组聚合获取唯一对象数组问题

需要从嵌套数组结构的文档中,通过聚合查询提取出唯一的竞赛类型对象数组,当前使用$group等聚合阶段后返回结果不符合预期。

数据结构

{
   _id: ObjectId('637b22639356492ae41bbe23'),
   studentAgeCategories: [
       {
          competitionsType: [
           {
                    competitionsTypeId: ObjectId("5fec0c53a534c297c6b4c5c3"),
                    competitionsTypeName: "Animation/Documentary/Film"
            }
          ]
       }
   ]
}

尝试的查询

const pipeline = [
    {'$match': {'_id': ObjectId('637b22639356492ae41bbe23')}},
    {'$unwind': '$studentAgeCategories'},
    {'$group': {
        '_id': '$studentAgeCategories.competitionsType.competitionsTypeId',
        'competitionType': {
            '$push': {
                '_id': '$studentAgeCategories.competitionsType.competitionsTypeId',
                'competitionsTypeName': '$studentAgeCategories.competitionsType.competitionsTypeName'
                }
            }
        }
    }
];

dbconn.clnEvents.aggregate(pipeline);

当前结果

/* 1 */
{
    "_id" : [
        ObjectId("5fec0c53a534c297c6b4c5c3"),
        ObjectId("5fec0c65a534c297c6b4ce3a"),
        ObjectId("5fec0c8aa534c297c6b4dda7")
    ],
    "competitionType" : [
        {
            "_id" : [
                ObjectId("5fec0c53a534c297c6b4c5c3"),
                ObjectId("5fec0c65a534c297c6b4ce3a"),
                ObjectId("5fec0c8aa534c297c6b4dda7")
            ],
            "competitionsTypeName" : [ "Animation/Documentary/Film", "Visual coding", "Websites/Web/Mobile Apps" ]
        }
    ]
},
/* 其余结果省略 */

期望结果

"competitionType" : [
    {"_id" : ObjectId("5fec0c9fa534c297c6b4e4b6"),
     "competitionsTypeName" : "AI/Robotics/IoT" },
    {"_id" : ObjectId("5fec0c65a534c297c6b4ce3a"),
     "competitionsTypeName" : "Visual coding" },
    {"_id" : ObjectId("5fec0c8aa534c297c6b4dda7"),
     "competitionsTypeName" : "Websites/Web/Mobile Apps" }]

问题原因及解决方案

原来的查询只对studentAgeCategories数组做了$unwind,但competitionsType也是嵌套数组,直接引用其字段会得到数组值,导致$group的分组键是数组而非单个竞赛类型对象,最终返回嵌套数组的结果。

正确的聚合管道需要多一步对competitionsType的$unwind,将每个竞赛类型对象单独拆分出来,再按competitionsTypeId分组去重,最后整理成目标格式:

const pipeline = [
    // 过滤目标文档
    {'$match': {'_id': ObjectId('637b22639356492ae41bbe23')}},
    // 拆分studentAgeCategories数组
    {'$unwind': '$studentAgeCategories'},
    // 拆分每个studentAgeCategories下的competitionsType数组(关键步骤)
    {'$unwind': '$studentAgeCategories.competitionsType'},
    // 按competitionsTypeId分组,确保每个类型唯一
    {'$group': {
        '_id': '$studentAgeCategories.competitionsType.competitionsTypeId',
        'competitionsTypeName': {'$first': '$studentAgeCategories.competitionsType.competitionsTypeName'}
    }},
    // 重新构造对象格式
    {'$project': {
        '_id': 0,
        '_id': '$_id',
        'competitionsTypeName': 1
    }},
    // 将所有结果收集到competitionType数组中
    {'$group': {
        '_id': null,
        'competitionType': {'$push': '$$ROOT'}
    }},
    // 只保留competitionType字段
    {'$project': {
        '_id': 0,
        'competitionType': 1
    }}
];

dbconn.clnEvents.aggregate(pipeline);

步骤说明

  1. $match:定位到目标文档,减少后续处理的数据量。
  2. 两次$unwind:先拆分外层的studentAgeCategories,再拆分内层的competitionsType,将每个嵌套的竞赛类型对象单独提取为一条文档。
  3. 第一次$group:按competitionsTypeId分组,使用$first获取对应的类型名称,实现去重。
  4. $project:调整字段结构,符合期望的对象格式。
  5. 第二次$group:将所有去重后的竞赛类型对象收集到一个数组中。
  6. 最后一次$project:只保留competitionType字段,得到最终期望的结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 18:25:23