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);
步骤说明
- $match:定位到目标文档,减少后续处理的数据量。
- 两次$unwind:先拆分外层的
studentAgeCategories,再拆分内层的competitionsType,将每个嵌套的竞赛类型对象单独提取为一条文档。 - 第一次$group:按
competitionsTypeId分组,使用$first获取对应的类型名称,实现去重。 - $project:调整字段结构,符合期望的对象格式。
- 第二次$group:将所有去重后的竞赛类型对象收集到一个数组中。
- 最后一次$project:只保留
competitionType字段,得到最终期望的结果。
内容的提问来源于stack exchange,提问作者Kabeer Hadi
相关产品推荐
相关产品推荐

