使用upsert:true插入文档时如何排除查询字段避免重复键错误
解决方案:拆分查询与插入逻辑
因为upsert的机制就是「没找到匹配文档时,用查询里的等值条件生成新文档」,所以直接在查询里指定_id的话,肯定会触发重复键问题。咱们换个思路,拆分两步操作:
先查有没有符合条件的文档
先确认集合里是否存在满足_id指定值且count<2的文档:const targetDoc = db.getCollection('placeFollow').findOne({ "_id": ObjectId("5af19959204438676c0d5268"), "count": {"$lt": 2} });根据查询结果做对应操作
- 如果查到了符合条件的文档,直接更新它:
if (targetDoc) { db.getCollection('placeFollow').updateOne( {"_id": targetDoc._id}, {"$set": {"data": "check"}} ); } - 如果没查到,就插入一个全新的文档(不指定
_id,让MongoDB自动生成):else { db.getCollection('placeFollow').insertOne({ "data": "check", // 这里可以给count设个默认值,比如0,根据你的业务需求调整 "count": 0 }); }
- 如果查到了符合条件的文档,直接更新它:
为啥原语句会报错?
给你掰扯下MongoDBupsert: true的底层逻辑:
- 先按照查询条件找文档;
- 找到的话,就执行更新;
- 找不到的话,会把查询里的所有等值条件(比如你这里的
_id)当成新文档的字段,再把更新操作($set的内容)加上,然后插入这个新文档。
你的情况是:集合里确实有_id为5af19959204438676c0d5268的文档,但它的count不满足<2,所以查询没匹配到。这时候upsert要插入新文档,就会把查询里的_id带进去,可这个_id已经存在了,自然触发唯一索引的重复键错误。
补充:MongoDB 4.2+的聚合管道更新(不推荐你用)
如果你想尝试单操作实现,MongoDB 4.2及以上支持用聚合管道做更新,但这解决不了_id重复的问题,因为upsert还是会用查询里的_id,所以仅作参考:
db.getCollection('placeFollow').update( { "_id": ObjectId("5af19959204438676c0d5268"), "count": {"$lt": 2} }, [{"$set": {"data": "check"}}], {"upsert": true} )
内容的提问来源于stack exchange,提问作者Mohammad Jilani
相关产品推荐
相关产品推荐

