You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 09:46:14