如何为单个键组合多个arrayFilters?MongoDB数组批量更新咨询
问题描述
假设我有如下Mongoose Schema:
const some_schema = new mongoose.Schema({ "id": { type: Number }, "list": [ { id: { type: Number }, value: { type: Number } } ] })
我需要使用arrayFilters更新list中的多个对象,已知每个要更新的对象的id都是唯一的,且仅匹配list中的一个对象。
我目前通过JavaScript生成动态键实现了需求,代码如下:
// 原始文档值: { "id": 10, "list": [ { id: 1, value: 5 }, { id: 2, value: 10 } ] } let updates = [ { id: 1, newValue: 10 }, { id: 2, newValue: 20 }]; let queryOptions = { arrayFilters: [] }, queryUpdates = { $set: {} }; for (const key in updates) { queryOptions.arrayFilters.push({ [`${key}`]: key }); queryUpdates.$set[[`elem.$[${key}].value`]] = updates[key].newValue; } // 更新查询 await doc.updateOne(queryUpdates, queryOptions); // 更新后的文档值: { "id": 10, "list": [ { id: 1, value: 10 }, { id: 2, value: 20 } ] }
请问是否可以直接在MongoDB的arrayFilters中实现该需求,或者通过其他方法实现,无需使用上述动态键的方式?
解决方案
完全可以不用动态键的方式实现,这里提供两种实用方案:
方案一:聚合管道式更新(MongoDB 4.2+ 支持)
利用$map遍历数组,结合$mergeObjects和$filter匹配更新值,一次性完成批量更新,逻辑清晰且易维护:
const updates = [ { id: 1, newValue: 10 }, { id: 2, newValue: 20 }]; await doc.updateOne( { id: 10 }, // 匹配目标文档的查询条件 [ { $set: { list: { $map: { input: "$list", as: "item", in: { $mergeObjects: [ "$$item", { value: { $let: { vars: { // 从updates中筛选出当前item对应的更新项 matchedUpdate: { $arrayElemAt: [ { $filter: { input: updates, cond: { $eq: ["$$this.id", "$$item.id"] } } }, 0 ] } }, // 有匹配的更新项就用新值,否则保留原值 in: { $ifNull: ["$$matchedUpdate.newValue", "$$item.value"] } } } } ] } } } } } ] );
方案二:单arrayFilter配合条件判断(MongoDB 3.6+ 支持)
如果偏好使用arrayFilters而非聚合管道,可以通过构造包含所有目标ID的过滤条件,再用嵌套$cond匹配赋值:
const updates = [ { id: 1, newValue: 10 }, { id: 2, newValue: 20 }]; const targetIds = updates.map(u => u.id); // 嵌套$cond生成匹配逻辑:每个ID对应一个判断分支 const valueUpdateLogic = updates.reduce((prev, curr) => ({ $cond: [ { $eq: ["$$elem.id", curr.id] }, curr.newValue, prev ] }), "$$elem.value"); await doc.updateOne( { id: 10 }, { $set: { "list.$[elem].value": valueUpdateLogic } }, { arrayFilters: [{ "elem.id": { $in: targetIds } }] } );
注意:这个方案的$cond嵌套层级会随更新项数量增加而变深,更新项较多时不如方案一简洁。
总结
优先推荐方案一的聚合管道更新,它无需生成动态键,能适配任意数量的批量更新需求,代码可读性和可维护性都更高。
内容的提问来源于stack exchange,提问作者zinbrox
相关产品推荐
相关产品推荐

