Mongoose如何按language字段更新数组子文档不新增重复条目
问题原因
你使用的$addToSet操作符的作用是向数组中添加不存在的全新元素。你传入的{"language": "fr-CA", "text": "Nouvel article"}与原有数组中的{"language": "fr-CA", "text": "livre"}是两个完全不同的对象,因此会触发新增逻辑,而非更新原有元素。
同时你原有代码存在方法使用错误:findByIdAndUpdate是专门用于匹配MongoDB默认_id字段的方法,第一个参数直接传_id的值即可,不需要传入查询对象。如果你要匹配自定义的id字段,请使用findOneAndUpdate方法。
修复代码
要更新数组中匹配指定language的子文档,需要在查询条件中匹配数组元素规则,再配合$占位符定位到命中的数组下标,更新对应text字段:
const input = { "id": "1", "isActive": false }; const targetLang = "fr-CA"; const newText = "Nouvel article"; await langs.findOneAndUpdate( // 查询条件:匹配指定id,且name数组中存在language为目标值的元素 { id: Number(input.id), "name.language": targetLang }, { ...input, $set: { // $占位符会自动匹配查询条件命中的数组元素下标,更新对应text字段 "name.$.text": newText } }, { new: true, upsert: true } );
兼容「无对应语言则新增」场景
如果需要支持数组中不存在目标language时自动新增条目,可以使用MongoDB 4.2及以上版本支持的聚合更新逻辑:
await langs.findOneAndUpdate( { id: Number(input.id) }, [ { $set: { ...input, name: { $cond: { // 先判断数组中是否存在目标语言 if: { $in: [targetLang, "$name.language"] }, // 存在则遍历数组更新对应条目的text then: { $map: { input: "$name", in: { $mergeObjects: [ "$$this", { text: { $cond: { if: { $eq: ["$$this.language", targetLang] }, then: newText, else: "$$this.text" } } } ] } } }, // 不存在则拼接新的语言条目 else: { $concatArrays: ["$name", [{ language: targetLang, text: newText }]] } } } } } ], { new: true, upsert: true } );
内容的提问来源于stack exchange,提问作者Mehran Ishanian
相关产品推荐
相关产品推荐

