使用$set与$currentDate更新Mongo文档时出现冲突问题
问题排查与解决
嘿,我瞅了下你的代码和问题,你碰到的这个MongoError大概率是两个原因搞的鬼:
- 自定义构造函数的对象不被MongoDB兼容:你用
new MyObject()创建的是带原型链的自定义对象,MongoDB在序列化这种对象的时候容易出问题,甚至没法正确解析后面要修改的foo.time路径。 - 同字段的操作冲突:你同时用
$set把整个foo替换成自定义对象,又用$currentDate去修改它的子字段foo.time,MongoDB的原子更新逻辑可能会判定这俩操作有冲突。
直接可用的修复方案
方案1:拆分字段更新(最稳妥)
不用一次性设置整个foo对象,分别设置foo.bar和foo.time,MongoDB会自动帮你创建foo这个父对象:
collection.update( { _id: 1 }, { $set: { "foo.bar": { apples: 4, bananas: 5 } }, $currentDate: { "foo.time": { $type: 'timestamp' } } }, function (err) { if (err) { console.log(err.name, err.message); } else { console.log("文档更新成功啦"); } } );
方案2:替换自定义对象为普通字面量
如果你非要先定义好bar的结构,那就把自定义构造函数换成普通的对象字面量(MongoDB对这种原生对象支持最好):
// 用普通对象替代自定义构造函数,去掉原型链问题 const fooBase = { bar: { apples: 4, bananas: 5 } }; collection.update( { _id: 1 }, { $set: { "foo": fooBase }, $currentDate: { "foo.time": { $type: 'timestamp' } } }, // 可选:如果_id=1的文档不存在,自动创建它 { upsert: true }, function (err) { if (err) { console.log(err.name, err.message); } else { console.log("搞定!文档更新完成"); } } );
为啥原来的代码不行?
再给你掰扯清楚背后的原因:
- 自定义构造函数
MyObject创建的对象带原型链,MongoDB存文档的时候不待见这种带额外原型信息的对象,会导致路径解析失败,触发报错。 - 就算忽略原型的问题,
$set覆盖整个foo的操作,和$currentDate修改foo.time的操作,在MongoDB的原子更新流程里,会被判定为“你刚把foo换成新对象,又要改它的子字段,这逻辑不对啊”,所以直接给你报错了。
内容的提问来源于stack exchange,提问作者dodov
相关产品推荐
相关产品推荐

