如何在MongoDB中更新多个文档的对象数组字段(mongoose场景)
问题根因
- 你当前使用的普通更新语法不支持引用文档自身的字段值,你写的
"$_id"、"$b_id"会被当做普通字符串存储,不会解析为对应文档的字段值,这是更新不符合预期的核心原因。 - 虽然你加了
multi: true参数,但是因为更新值的解析问题,最终效果和预期不符。
优化解决方案
MongoDB 4.2及以上版本支持聚合管道形式的更新操作,可以直接引用当前文档的字段完成更新,无需手动循环遍历文档,性能远高于循环更新的方案。
原生MongoDB语法
等效$push(允许重复元素)
db.getCollection("test").update( { _id: { $in: [ObjectId("string1"), ObjectId("string2")] } }, [ // 用数组包裹更新逻辑,启用聚合管道模式 { $set: { my_array: { $concatArrays: [ "$my_array", [{ a_id: ObjectId("target_a_id"), b: "$_id", c: Date.now(), d: true, e: "$b_id" }] ] } } } ], { multi: true } )
等效$addToSet(仅不存在时插入)
如果需要实现去重插入,调整管道逻辑增加存在性判断即可:
db.getCollection("test").update( { _id: { $in: [ObjectId("string1"), ObjectId("string2")] } }, [ { $set: { my_array: { $cond: { // 自定义去重判断规则,示例为判断a_id是否已存在 if: { $in: [ObjectId("target_a_id"), "$my_array.a_id"] }, then: "$my_array", // 已存在则不修改 else: { // 不存在则追加元素 $concatArrays: [ "$my_array", [{ a_id: ObjectId("target_a_id"), b: "$_id", c: Date.now(), d: true, e: "$b_id" }] ] } } } } } ], { multi: true } )
Mongoose写法
在Mongoose中直接使用updateMany方法对应实现即可,不需要额外加multi参数:
const Test = mongoose.model('test', yourSchema); const targetDocIds = [mongoose.Types.ObjectId("string1"), mongoose.Types.ObjectId("string2")]; const targetAId = mongoose.Types.ObjectId("target_a_id"); await Test.updateMany( { _id: { $in: targetDocIds } }, [ { $set: { my_array: { $cond: { if: { $in: [targetAId, "$my_array.a_id"] }, then: "$my_array", else: { $concatArrays: [ "$my_array", [{ a_id: targetAId, b: "$_id", c: Date.now(), d: true, e: "$b_id" }] ] } } } } } ] );
注意事项
- 该方案依赖MongoDB 4.2及以上版本的聚合管道更新能力,低于该版本需要升级服务端或改用批量写入接口。
- 去重规则可根据业务调整,如需判断数组元素的所有字段完全一致再去重,修改
$cond的判断逻辑即可。
内容的提问来源于stack exchange,提问作者Batman
相关产品推荐
相关产品推荐

