如何使用另一集合字段更新MongoDB嵌套数组内的所有对象
解决方案
你之前编写的是只读聚合查询,默认仅返回计算结果,不会修改原集合数据,MongoDB 4.2及以上版本支持在update操作中使用聚合管道,可直接实现你的更新需求,具体实现如下:
完整更新代码
db.vehicles.updateMany({}, [ // 关联suppliers集合,拉取所有匹配的供应商数据 { $lookup: { from: "suppliers", localField: "suppliers.supplierId", foreignField: "_id", as: "matchedSuppliers" } }, // 遍历suppliers数组,匹配写入internalId { $set: { suppliers: { $map: { input: "$suppliers", as: "supplierItem", in: { $mergeObjects: [ "$$supplierItem", // 保留数组项原有所有字段 { internalId: { $getField: { field: "internalId", input: { $first: { $filter: { input: "$matchedSuppliers", cond: { $eq: ["$$this._id", { $toObjectId: "$$supplierItem.supplierId" }] } } } } } } } ] } } } } }, // 删除临时关联字段 { $unset: "matchedSuppliers" } ])
代码说明
- 用
updateMany代替只读聚合,第一个参数{}表示匹配vehicles集合所有文档,直接对原集合执行更新 - 全程无需
$unwind拆分数组,通过$lookup一次性关联当前文档所有对应供应商数据,存到临时字段matchedSuppliers - 用
$map遍历原suppliers数组,通过$mergeObjects保留数组项原有字段的同时新增internalId,再通过$filter匹配到对应供应商的internalId值 - 最后删除临时的
matchedSuppliers字段,不影响原文档其他结构 - 如果你存的
supplierId本身就是ObjectId类型而非字符串,可以去掉$toObjectId转换逻辑,注意internalId字段大小写要和suppliers集合的定义保持一致
低版本MongoDB兼容方案
如果你的MongoDB版本低于4.2,不支持update带聚合管道,可以用聚合+$merge的方式写回原集合:
db.vehicles.aggregate([ { $lookup: { from: "suppliers", localField: "suppliers.supplierId", foreignField: "_id", as: "matchedSuppliers" } }, { $set: { suppliers: { $map: { input: "$suppliers", as: "supplierItem", in: { $mergeObjects: [ "$$supplierItem", { internalId: { $getField: { field: "internalId", input: { $first: { $filter: { input: "$matchedSuppliers", cond: { $eq: ["$$this._id", { $toObjectId: "$$supplierItem.supplierId" }] } } } } } } } ] } } } } }, { $unset: "matchedSuppliers" }, // 结果按_id匹配写回原集合 { $merge: { into: "vehicles", on: "_id", whenMatched: "replace", whenNotMatched: "insert" } } ])
你原有代码的问题
- 仅用了只读聚合,没有加
$merge/$out写回阶段,不会修改原集合 $project阶段仅保留了supplierObjId字段,原文档的品牌、型号、供应商其他字段都被丢弃,就算写回也会丢失数据$lookup返回的是数组,直接取$supplierInfo.internalid得到的是数组而非单个字符串值,需要先取第一个元素
内容的提问来源于stack exchange,提问作者birmsi
相关产品推荐
相关产品推荐

