Mongoose实现嵌套数组产品更新/新增及缺失路径自动创建
基于Mongoose实现嵌套数组产品的更新/插入(自动补全层级)
针对你需要在date/hours[hour]/categories[category]/products路径下更新或添加产品,同时自动创建缺失层级的需求,以下是两种可落地的实现方案:
方案1:分步骤操作(逻辑清晰,易维护)
先尝试更新已存在的产品,若未匹配到目标路径,则手动创建缺失层级并添加/更新产品,避免单次操作的复杂度。
第一步:尝试更新已存在的产品
通过数组定位操作符$结合arrayFilters精准匹配目标层级,直接更新产品:
const updateResult = await YourModel.findOneAndUpdate( // 匹配基础路径:date、目标hour、目标category { date: "05/05/24", "hours.hour": "09", "hours.categories.category": "computers" }, // 定位目标产品并更新字段 { $set: { "hours.$[hourElem].categories.$[catElem].products.$[prodElem].last_published": new Date() } }, { arrayFilters: [ { "hourElem.hour": "09" }, { "catElem.category": "computers" }, { "prodElem.product_source_id": "112233" } ], new: true, // 返回更新后的文档 runValidators: true // 确保符合Schema验证规则 } );
第二步:补全缺失层级并添加/更新产品
如果上述操作返回null,说明路径存在缺失,手动创建层级:
if (!updateResult) { // 1. 确保date对应的文档存在,不存在则创建 let doc = await YourModel.findOne({ date: "05/05/24" }); if (!doc) { doc = new YourModel({ date: "05/05/24", hours: [] }); } // 2. 查找或创建目标hour节点 let hourItem = doc.hours.find(h => h.hour === "09"); if (!hourItem) { hourItem = { hour: "09", categories: [] }; doc.hours.push(hourItem); } // 3. 查找或创建目标category节点 let categoryItem = hourItem.categories.find(c => c.category === "computers"); if (!categoryItem) { categoryItem = { category: "computers", products: [] }; hourItem.categories.push(categoryItem); } // 4. 查找或更新/添加产品 let productItem = categoryItem.products.find(p => p.product_source_id === "112233"); if (productItem) { productItem.last_published = new Date(); } else { categoryItem.products.push({ product_source_id: "112233", last_published: new Date() }); } // 保存最终文档 await doc.save(); }
方案2:原子聚合操作(性能更优,单次DB请求)
利用MongoDB聚合管道的$cond、$map、$concatArrays等操作符,在单次数据库请求中完成层级检查、创建和产品更新/插入,适合对性能要求较高的场景:
await YourModel.findOneAndUpdate( { date: "05/05/24" }, [ // 步骤1:检查并添加缺失的hour节点 { $set: { hours: { $cond: [ { $in: ["09", "$hours.hour"] }, "$hours", { $concatArrays: ["$hours", [{ hour: "09", categories: [] }]] } ] } } }, // 步骤2:检查并添加缺失的category节点 { $set: { hours: { $map: { input: "$hours", as: "h", in: { $cond: [ { $eq: ["$$h.hour", "09"] }, { hour: "$$h.hour", categories: { $cond: [ { $in: ["computers", "$$h.categories.category"] }, "$$h.categories", { $concatArrays: ["$$h.categories", [{ category: "computers", products: [] }]] } ] } }, "$$h" ] } } } } }, // 步骤3:更新或添加目标产品 { $set: { hours: { $map: { input: "$hours", as: "h", in: { $cond: [ { $eq: ["$$h.hour", "09"] }, { hour: "$$h.hour", categories: { $map: { input: "$$h.categories", as: "c", in: { $cond: [ { $eq: ["$$c.category", "computers"] }, { category: "$$c.category", products: { $cond: [ { $in: ["112233", "$$c.products.product_source_id"] }, { $map: { input: "$$c.products", as: "p", in: { $cond: [ { $eq: ["$$p.product_source_id", "112233"] }, { ..."$$p", last_published: new Date() }, "$$p" ] } } }, { $concatArrays: ["$$c.products", [{ product_source_id: "112233", last_published: new Date() }]] } ] } }, "$$c" ] } } } }, "$$h" ] } } } } } ], { upsert: true, new: true, runValidators: true } );
注意事项
- 确保
product_source_id的类型与Schema定义一致(Schema中为String,需传入字符串类型的ID); - 启用
runValidators: true可避免更新操作违反Schema的必填项、枚举值等规则; - 方案1适合中小规模业务,逻辑直观易调试;方案2适合高并发场景,减少DB交互次数。
内容的提问来源于stack exchange,提问作者elad BA
相关产品推荐
相关产品推荐

