MongoDB中能否用$addToSet按对象指定字段去重,向数组添加元素
MongoDB中按对象指定字段而非整个对象判断重复的实现方案
默认情况下,$addToSet会对比整个对象的所有字段是否完全匹配,所以你示例里的{name: "xx", value: "def"}会因为和数组中原有的{name: "xx", value: "abc"}不是完全相同的对象而被添加,无法实现仅按name字段判断重复的需求。
以下是两种可行的实现方式:
1. 单元素条件式推送
针对单个待添加元素,可以先通过查询条件筛选出数组中不存在对应name的文档,再执行$push:
// 仅当cookies数组中没有name为"xx"的元素时,才添加该cookie db.audits.updateOne( { _id: 1, "cookies.name": { $ne: "xx" } }, { $push: { cookies: { name: "xx", value: "def" } } } )
如果需要批量处理多个元素,这种方式需要循环遍历每个元素执行上述操作。
2. 聚合管道批量去重添加(MongoDB 4.2+)
利用聚合管道可以一次性处理多个待添加元素,通过$filter过滤掉name已存在的元素,再用$setUnion合并到原数组:
db.audits.updateOne( { _id: 1 }, [ { $set: { cookies: { $setUnion: [ "$cookies", { $filter: { input: [ {name: "xx", value: "def"}, {name: "yy", value: "abc"} ], cond: { $not: { $in: [ "$$this.name", "$cookies.name" ] } } } } ] } } } ] )
这段逻辑会自动过滤掉待添加数组中name已存在于原cookies的元素,最终只有{name: "yy", value: "abc"}会被添加到数组中,完全符合你的期望。
额外场景:存在则更新,不存在则添加
如果你的需求是当name存在时更新对应value,不存在时添加新元素,可以用聚合管道一次性完成:
db.audits.updateOne( { _id: 1 }, [ // 先更新已有name为"xx"的元素的value { $set: { cookies: { $map: { input: "$cookies", as: "cookie", in: { $cond: [ { $eq: [ "$$cookie.name", "xx" ] }, { name: "xx", value: "def" }, "$$cookie" ] } } } } }, // 若数组中没有name为"xx"的元素,则添加 { $set: { cookies: { $cond: [ { $in: [ "xx", "$cookies.name" ] }, "$cookies", { $concatArrays: [ "$cookies", [ { name: "xx", value: "def" } ] ] } ] } } } ] )
内容的提问来源于stack exchange,提问作者Steve
相关产品推荐
相关产品推荐

