MongoDB条件式插入/替换文档:仅旧文档价格更低时更新
解决MongoDB Upsert时的重复键错误问题
错误原因
你遇到的E11000 duplicate key error,根源在于用replaceOne搭配upsert: true且filter包含price: {$lt: item.price}的逻辑:
- 当集合中已存在
_id匹配但price不小于当前条目值的文档时,filter无法命中任何结果; - 此时
upsert会尝试插入当前条目作为新文档,而_id已存在,触发唯一键冲突。
解决方案
改用updateOne替代replaceOne,通过条件更新控制仅在现有文档price低于新值时执行更新,同时保留upsert实现插入逻辑。
方案1(MongoDB 4.2+推荐)
利用updateOne的condition参数直接判断更新条件:
await abc.bulkWrite(input.map(function (item) { return { updateOne: { filter: { _id: item._id }, update: { $set: { title: item.title, price: item.price, updated_at: item.updated_at } }, upsert: true, // 仅当现有文档price小于新price时才执行更新 condition: { $lt: ["$price", item.price] } } }; }));
方案2(兼容低版本MongoDB)
用$cond在更新逻辑中判断是否替换字段:
await abc.bulkWrite(input.map(function (item) { return { updateOne: { filter: { _id: item._id }, update: { $set: { updated_at: item.updated_at, title: { $cond: { if: { $lt: ["$price", item.price] }, then: item.title, else: "$title" } }, price: { $cond: { if: { $lt: ["$price", item.price] }, then: item.price, else: "$price" } } } }, upsert: true } }; }));
方案3(简化版,仅关注price更新)
如果只需要保证price取最大值,同时同步更新其他字段(无论price是否变化),可以用$max简化:
await abc.bulkWrite(input.map(function (item) { return { updateOne: { filter: { _id: item._id }, update: { $max: { price: item.price }, $set: { updated_at: item.updated_at, title: item.title } }, upsert: true } }; }));
效果说明
- 当文档不存在时:
upsert触发插入,创建新文档; - 当文档存在且
price低于新值时:执行更新,替换为新的字段值; - 当文档存在且
price不低于新值时:不执行任何操作,避免无效更新和键冲突。
内容的提问来源于stack exchange,提问作者vbarbarosh
相关产品推荐
相关产品推荐

