MongoDB聚合$out报E11000重复键错误,求排查原因
问题原因分析
你遇到的这个重复键问题,核心原因是**$unwind阶段导致多个聚合结果文档共享同一个父文档的_id**,而$out阶段默认会保留这些_id值,当尝试插入到目标集合时,重复的_id就触发了唯一索引冲突。
具体来说,你的聚合流程里藏着关键的触发点:
- 先用$match筛选出包含符合条件的events数组元素的文档
- $unwind把每个匹配的events数组拆成单独的文档——这些拆分后的文档会直接继承原父文档的_id
- 再次$match过滤出符合条件的events元素
- $project只提取field3,但MongoDB默认不会自动移除原文档的_id字段(除非你显式指定排除)
- 最后$out尝试把这些文档插入results集合时,因为多个文档带着相同的_id,就触发了E11000重复键错误
哪怕你提前删除了results集合也没用——问题出在聚合生成的中间文档本身就有重复的_id,和目标集合是否为空完全无关。
解决方案
有两种简单直接的办法可以解决这个问题:
方案1:在$project阶段显式排除_id字段
修改你的$project步骤,明确指定_id: 0,这样聚合结果就不会携带原父文档的_id,MongoDB会自动为$out的文档生成新的唯一_id:
db.collection.aggregate( {$match:{'events':{$elemMatch:{'field1':1,'field2':2}}}}, {$unwind:'$events'}, {$match:{'events.field1':1,'events.field2':2}}, {$project:{'_id': 0, 'field3':'$events.field3'}}, { $out : "results" } )
方案2:为每个聚合文档生成新的唯一_id
如果你需要保留一个唯一标识符(但不需要原父文档的_id),可以在$project或$addFields阶段生成新的_id,比如使用$uuid(MongoDB 4.0+支持)或者$objectId:
db.collection.aggregate( {$match:{'events':{$elemMatch:{'field1':1,'field2':2}}}}, {$unwind:'$events'}, {$match:{'events.field1':1,'events.field2':2}}, {$addFields: {_id: {$uuid: {}}}}, // 生成新的唯一UUID作为_id {$project:{'field3':'$events.field3'}}, { $out : "results" } )
补充说明
记住,MongoDB的聚合管道中,除非你显式排除或修改_id字段,否则所有文档都会保留原始的_id值。当你使用$unwind处理数组时,每个拆分出的文档都会继承父文档的_id,这就很容易导致重复——尤其是当一个父文档有多个符合条件的events元素时,拆分后就会生成多个带着相同_id的文档,最终在$out阶段触发冲突。
内容的提问来源于stack exchange,提问作者Panikos Stavrou
相关产品推荐
相关产品推荐

