MongoDB Node.js upsert插入报错:位置运算符未匹配查询项
解决MongoDB Upsert时位置操作符报错的问题
先给你捋清楚为啥会出这个问题:你用upsert: true的时候,更新已有文档完全正常,但插入新文档就报错,核心原因是位置操作符$在插入场景下找不到匹配的数组元素。
当MongoDB要执行upsert插入新文档时,你的查询条件里有"options.key": item,但这时候新文档里根本还没有options数组,更别说匹配的key了,$这个位置符直接抓瞎,自然抛出The positional operator did not find the match needed from the query的错误。而更新现有文档时,因为已经存在对应的options元素,$能精准定位,所以一切正常。
下面给你两个可行的解决方案:
方案一:用$setOnInsert区分更新和插入逻辑
这个方法能在同一个操作里处理两种场景:更新时加票数,插入时直接初始化带目标选项的数组。代码改成这样:
db.collection('polls').update( { _id: require("mongodb").ObjectId(id), "options.key": item }, { $inc: { "options.$.votes": 1 }, $setOnInsert: { options: [{ key: item, votes: 1 }] } }, { upsert: true, safe: false }, function(err, data) { if(err) throw err; if(data) { console.log(data); } } );
原理很简单:如果查询到了已有文档,就执行$inc给对应选项加1票;如果没查到要插入新文档,就触发$setOnInsert,创建一个包含目标选项、票数初始为1的poll文档。
方案二:分开执行更新和插入操作
这种方式逻辑更直观:先尝试更新,要是发现没有文档被修改,就手动插入新文档。代码示例:
db.collection('polls').update( { _id: require("mongodb").ObjectId(id), "options.key": item }, { $inc: { "options.$.votes": 1 } }, { safe: false }, function(err, updateResult) { if(err) throw err; // 没有文档被更新,说明需要插入新的poll if(updateResult.modifiedCount === 0) { db.collection('polls').insertOne( { _id: require("mongodb").ObjectId(id), options: [{ key: item, votes: 1 }] }, function(err, insertResult) { if(err) throw err; console.log(insertResult); } ); } else { console.log(updateResult); } } );
另外提一句,如果你用的是新版本的MongoDB驱动,建议用updateOne替代旧的update方法,API更规范,兼容性也更好。
内容的提问来源于stack exchange,提问作者JEEZSUSCRIZE
相关产品推荐
相关产品推荐

