MongoDB嵌套数组元素动态更新异常问题排查
MongoDB数组动态更新异常原因解析
场景与代码示例
集合中的文档结构如下:
{ _id: ObjectId("..."), data: [ { year: '2023', week: '04', value: 1 }, // 其他元素 ] }
为实现data数组元素的动态更新(存在则更新,不存在则添加),编写了如下循环逻辑:
const data = [ { year: '2023', week: '04', value: 1 }, { year: '2023', week: '05', value: 1 }, { year: '2023', week: '06', value: 1 } ] for(let i = 0; i < 5; i ++) { for await (let entry of data) { const res = await collection.updateOne( { _id: new ObjectId(uId), 'data.year': entry.year, 'data.week': entry.week }, { $set: { 'data.$': entry } } ) if(res.matchedCount === 0) { // 条目不存在则执行添加 await collection.updateOne({ _id: new ObjectId(uId) }, { $push: { 'data': { $each: [stat], $sort: { year: 1, week: 1 } } } }) } } }
异常表现
执行后data数组长度不符合预期:
第一次迭代:数组含3个元素 → 正确
第二次迭代:数组含3个元素 → 正确
第三次迭代:数组含4个元素 → 错误
第五次迭代:数组含5个元素 → 错误
第六次迭代:数组含6个元素 → 错误
核心原因
匹配条件逻辑错误:原查询条件
'data.year': entry.year, 'data.week': entry.week是对数组元素的跨元素匹配——只要数组里存在任意一个元素的year匹配,同时存在任意一个元素的week匹配,查询就会命中。但data.$只会指向第一个匹配year的元素,导致更新了错误的元素,后续matchedCount === 0的判断完全失效,不该执行的添加操作被触发,数组元素不断增多。变量引用错误:添加操作中使用了未定义的
stat变量,应该替换为当前遍历的entry。如果stat是无效值,会插入错误的元素,进一步破坏数组结构。非原子操作风险:两次
updateOne调用不是原子操作,即使单线程下逻辑错误会导致异常,高并发场景下还可能出现竞态条件,加剧数据错误。
修正方案
将查询条件改为用$elemMatch限定同一个数组元素同时满足year和week的匹配:
// 修正后的updateOne查询条件 { _id: new ObjectId(uId), data: { $elemMatch: { year: entry.year, week: entry.week } } }
同时修复添加操作中的变量错误,把[stat]改为[entry]。
内容的提问来源于stack exchange,提问作者Ivan Bacher
相关产品推荐
相关产品推荐

