MongoDB中按profileId唯一性更新数组对象的实现
MongoDB 数组去重插入方案(按profileId唯一)
现有集合结构
{ "_id":"Some random ID", "name":"ABC", "profiles":[ { "profileId": "123", "attachedOn": "2022-07-28T15:08:11.072Z" }, { "profileId": "456", "attachedOn": "2022-07-28T15:08:11.072Z" }, { "profileId": "789", "attachedOn": "2022-07-28T15:08:11.072Z" }, { "profileId": "101", "attachedOn": "2022-07-28T15:08:11.072Z" } ] }
前端传入数据
data.profiles = [ { "profileId": "123", "attachedOn": "2022-07-28T15:08:11.072Z" }, { "profileId": "456", "attachedOn": "2022-07-28T15:08:11.072Z" }, { "profileId": "789", "attachedOn": "2022-07-28T15:08:11.072Z" }, { "profileId": "202", "attachedOn": "2022-07-28T15:08:11.072Z" } ]
需求
仅向profiles数组插入profileId唯一的对象(示例中仅插入profileId: '202'的项)
原尝试方案问题
原使用$addToSet的方式无法满足需求,因为$addToSet是对比整个对象的完全匹配,只要对象中任意字段(如attachedOn)不同就会重复插入,而我们只需要基于profileId判断唯一性。
query = [ { name:"ABC", }, { $addToSet: { profiles: { $each: data.profiles } } }, { $set: { ...record, }, }, ];
正确实现方案
方案1:聚合式更新(单数据库操作)
利用MongoDB的聚合更新语法,直接在数据库层面筛选出需要插入的项:
db.collection.updateOne( // 匹配目标文档 { name: "ABC" }, [ // 筛选并合并新数组 { $set: { profiles: { $concatArrays: [ "$profiles", // 原有数组 { $filter: { input: data.profiles, // 前端传入的数组 cond: { // 只保留profileId不在现有数组中的项 $not: { $in: ["$$this.profileId", "$profiles.profileId"] } } } } ] } } }, // 更新其他字段(如果需要) { $set: { ...record } } ] )
方案2:后端前置筛选(更直观)
先查询现有文档的profileId列表,在后端筛选出需要插入的项后再执行更新:
// 1. 查询现有文档的profileId集合 const existingDoc = await db.collection.findOne( { name: "ABC" }, { projection: { "profiles.profileId": 1 } } ); const existingProfileIds = existingDoc.profiles.map(p => p.profileId); // 2. 筛选前端传入数据中未存在的项 const newProfiles = data.profiles.filter( p => !existingProfileIds.includes(p.profileId) ); // 3. 仅当有新项时执行更新 if (newProfiles.length > 0) { await db.collection.updateOne( { name: "ABC" }, { $push: { profiles: { $each: newProfiles } }, $set: { ...record } } ); }
方案对比
- 聚合式更新:无需额外查询,一次数据库操作完成,适合逻辑简单的场景。
- 后端前置筛选:逻辑更直观,便于调试和扩展复杂判断,减少数据库端的计算压力。
内容的提问来源于stack exchange,提问作者Abbas Shaikh
相关产品推荐
相关产品推荐

