MongoDB按条件执行不同更新操作的实现问题
解决方案
你之前的问题核心是混淆了更新操作符和聚合阶段操作符的使用场景:$push属于更新操作符,不能在聚合的$project阶段使用。要实现「存在则追加销售记录,不存在则添加新产品」的需求,推荐使用MongoDB的更新管道来完成一次原子操作,具体实现如下:
代码实现
先定义待更新的对象(示例):
const updateObj = { Company: "Alpha", Products: [ { Name: "Car", SalesRecord: [{ date: "2024-05-20", amount: 100 }] } ] }; const targetProductName = updateObj.Products[0].Name; const newSalesRecord = updateObj.Products[0].SalesRecord[0];
执行更新操作:
db.collection.findOneAndUpdate( // 匹配条件:按公司名称查找 { Company: updateObj.Company }, // 更新管道 [ { $set: { Products: { $cond: [ // 第一步:判断Products数组中是否存在目标产品 { $in: [targetProductName, "$Products.Name"] }, // 情况1:产品存在 → 遍历数组,给匹配到的产品追加销售记录 { $map: { input: "$Products", as: "product", in: { $cond: [ { $eq: ["$$product.Name", targetProductName] }, { $mergeObjects: [ "$$product", { SalesRecord: { $concatArrays: ["$$product.SalesRecord", [newSalesRecord]] } } ] }, "$$product" ] } } }, // 情况2:产品不存在 → 将新产品追加到Products数组 { $concatArrays: ["$Products", updateObj.Products] } ] } } } ], // 可选:返回更新后的文档 { returnNewDocument: true } );
关键逻辑说明
- 产品存在性判断:
$in: [targetProductName, "$Products.Name"]会提取原文档中所有产品的Name组成数组,再检查目标名称是否在其中。 - 追加销售记录:用
$map遍历原Products数组,对匹配到的产品,通过$mergeObjects合并原产品数据和新的销售记录($concatArrays实现数组追加,效果等同于$push)。 - 添加新产品:如果产品不存在,用
$concatArrays将原产品数组和待添加的新产品数组合并,实现追加效果。
替代方案:分两次操作(非原子)
如果你的MongoDB版本较低不支持更新管道,可以先尝试更新已存在的产品,若未找到匹配项再插入新产品:
// 第一步:尝试更新已有产品的销售记录 const updateResult = await db.collection.updateOne( { Company: updateObj.Company, "Products.Name": targetProductName }, { $push: { "Products.$.SalesRecord": newSalesRecord } } ); // 第二步:如果没有匹配到文档,添加新产品 if (updateResult.matchedCount === 0) { await db.collection.updateOne( { Company: updateObj.Company }, { $push: { Products: updateObj.Products[0] } } ); }
注意:这种方式不是原子操作,高并发场景下可能出现重复添加的问题,优先推荐使用更新管道的原子方案。
内容的提问来源于stack exchange,提问作者echo
相关产品推荐
相关产品推荐

