MongoDB如何不使用循环、仅用原生查询更新数组中多个不同值的对象?
MongoDB批量匹配数组元素更新的正确实现方案
原代码错误点
- 参数拼写错误:数组过滤的参数是
arrayFilters,而非$arrayFilter,缺少后缀s且多了前缀$ - 语法规则错误:
$in运算符不支持直接将$function返回值作为右操作数 - 逻辑缺失:仅做了name匹配过滤,没有对应不同name映射不同occupation的逻辑
- 参数位置错误:
multi参数应该和arrayFilters平级,不能写在过滤规则内部
推荐实现方案
方案1:聚合管道更新(MongoDB 4.2+ 支持,逻辑统一)
该方案通过MongoDB原生聚合操作符直接处理数组更新,仅需一次数据库请求,无需遍历数据库文档:
首先将全局变量转换为name到occupation的映射对象(仅本地处理变量,无数据库操作循环):
const nameOccuMap = Object.fromEntries(peopleGlobalVar.map(item => [item.name, item.occupation]))
执行更新语句:
collection.updateMany( {}, // 可在此处添加文档过滤条件,无特殊需求则匹配所有文档 [ { $set: { persons: { $map: { input: "$persons", as: "p", in: { $cond: [ { $in: ["$$p.name", Object.keys(nameOccuMap)] }, { name: "$$p.name", occupation: nameOccuMap["$$p.name"] }, "$$p" ] } } } } } ], { multi: true } )
方案2:arrayFilters 动态更新(MongoDB 3.6+ 支持,性能更优)
该方案直接定位匹配的数组元素做更新,不需要重写整个persons数组,执行效率更高:
先生成动态更新规则和过滤规则(仅本地处理全局变量,无数据库循环):
const updateOps = {} const filters = [] peopleGlobalVar.forEach((person, index) => { updateOps[`persons.$[elem${index}].occupation`] = person.occupation filters.push({ [`elem${index}.name`]: person.name }) })
执行更新语句:
collection.updateMany( {}, { $set: updateOps }, { arrayFilters: filters, multi: true } )
执行结果验证
两种方案执行后,都会得到你预期的文档结构,匹配到name的occupation字段会更新为全局变量中的值,未匹配的条目保持不变。
内容的提问来源于stack exchange,提问作者Zefan
相关产品推荐
相关产品推荐

