如何不使用$unwind快速识别嵌套数组含重复a.p.id的MongoDB文档
不使用$unwind匹配嵌套数组中存在重复字段的MongoDB文档
可以不依赖$unwind实现需求,下面是基于$reduce、$addToSet等数组操作符的高效聚合方案,避免了$unwind带来的文档爆炸问题,在大数据量场景下性能更优。
方案1:输出精简格式({ key:2 , id:3 })
db.collection.aggregate([ { $addFields: { duplicateIds: { $reduce: { input: "$a", initialValue: { allIds: [], duplicates: [] }, in: { $let: { vars: { currentPIds: "$p.id", newAllIds: { $concatArrays: ["$$value.allIds", "$$currentPIds"] }, uniqueIds: { $addToSet: "$$newAllIds" }, duplicates: { $setDifference: [ "$$newAllIds", "$$uniqueIds" ] } }, in: { allIds: "$$newAllIds", duplicates: { $addToSet: "$$duplicates" } } } } } } } }, { $match: { "duplicateIds.duplicates": { $ne: [] } } }, { $project: { key: 1, duplicateIds: { $reduce: { input: "$duplicateIds.duplicates", initialValue: [], in: { $concatArrays: ["$$value", "$$this"] } } } } }, { $unwind: "$duplicateIds" }, { $group: { _id: "$key", ids: { $addToSet: "$duplicateIds" } } }, { $project: { _id: 0, key: "$_id", id: { $arrayElemAt: ["$ids", 0] } } } ])
步骤拆解:
- $addFields:遍历外层数组
a,收集所有p.id到临时数组allIds;通过$addToSet生成去重后的id列表,再用$setDifference找出重复的id值,存入duplicates。 - $match:过滤出确实存在重复id的文档。
- $project:把嵌套的重复id数组扁平化,得到所有重复id的集合。
- $unwind + $group:对重复id去重,确保每个
key对应唯一的重复id。 - $project:整理成你需要的
{ key, id }格式。
方案2:返回完整匹配文档
如果需要返回完整的源文档,只需调整聚合管道的最后几个阶段:
db.collection.aggregate([ { $addFields: { duplicateIds: { $reduce: { input: "$a", initialValue: { allIds: [], duplicates: [] }, in: { $let: { vars: { currentPIds: "$p.id", newAllIds: { $concatArrays: ["$$value.allIds", "$$currentPIds"] }, uniqueIds: { $addToSet: "$$newAllIds" }, duplicates: { $setDifference: [ "$$newAllIds", "$$uniqueIds" ] } }, in: { allIds: "$$newAllIds", duplicates: { $addToSet: "$$duplicates" } } } } } } } }, { $match: { "duplicateIds.duplicates": { $ne: [] } } }, { $project: { duplicateIds: 0 // 可选:移除临时生成的duplicateIds字段 } } ])
这个方案全程在单文档内完成数组运算,不会像$unwind那样拆分文档,处理大数组时性能提升明显。
内容的提问来源于stack exchange,提问作者R2D2
相关产品推荐
相关产品推荐

