MongoDB嵌套数组更新求助:基于条件更新memberOfGroups与roles
MongoDB嵌套数组更新问题及解决方案
我正尝试基于过滤条件更新MongoDB中的嵌套数组,但在更新嵌套数组时遇到困难,尤其是无法正确更新roles数组中的sources子数组,需要指导正确的写法。
示例文档
{ "_id": "67448af8-a68b-4d08-8948-2cddca57d708", "identity": { "_id": "7.26Demo@test.com", "type": "Email" }, "type": "User", "links": [ { "linkType": "Org", "linkPath": "0923689a-e009-4d67-8db5-5ba40f840bf3/facd3c31-dbfd-4097-9a27-0d862bb0c8e9", "status": "Activated", "createdOn": { "$date": "2022-07-26T14:49:38.780Z" }, "createdBy": "" } ], "memberOfGroups": [ { "_id": "ba93384d-d18a-4b36-9a24-7d3ebb1619d7", "name": "test group" } ], "roles": [ { "_id": "ba93384d-d18a-4b36-9a24-7d3ebb1619d7", "name": "test group", "sources": [ { "type": "group", "linkType": "app", "groupId": "ba93384d-d18a-4b36-9a24-7d3ebb1619d7" } ] } ] }
原错误更新管道
我使用了以下update pipeline,但无法更新roles数组中的sources子数组:
db.principals.update({ _id: "67448af8-a68b-4d08-8948-2cddca57d708" }, [ { $set: { "memberOfGroups": {$ifNull: ["$memberOfGroups", []]} } }, { $set: { "roles": {$ifNull: ["$roles", []]} } }, { $set: { "memberOfGroups": { $cond: [ { $ne: [ "$memberOfGroups._id", "ba93384d-d18a-4b36-9a24-7d3ebb1619d7" ] }, { $concatArrays: [ "$memberOfGroups.items", [ { _id: "ba93384d-d18a-4b36-9a24-7d3ebb1619d7", name: "test group" } ] ] }, {$concatArrays: ["$memberOfGroups.items", []]} ] } } }, { $set: { "roles": { $cond: [ { $ne: [ "$roles._id", "ba93384d-d18a-4b36-9a24-7d3ebb1619d7" ] }, { $concatArrays: [ "$roles.items", [ { _id: "ba93384d-d18a-4b36-9a24-7d3ebb1619d7", name: "test group", sources: [ { type: "group", linkType: "app", groupId: "ba93384d-d18a-4b36-9a24-7d3ebb1619d7" } ] } ] ] }, {} ] } } }, { $set: { roles: { $reduce: { input: {$ifNull: ["$roles", []]}, initialValue: { role: { _id: "0923689a-e009-4d67-8db5-5ba40f840bf3", name: "testRole", sources: [ { type: "group", linkType: "org" } ] }, sources: [ { type: "group", linkType: "org", groupid: "0923689a-e009-4d67-8db5-5ba40f840bf3" } ] }, in: { $cond: [ {$eq: [ "$$this.roleid", "0923689a-e009-4d67-8db5-5ba40f840bf3" ] }, {$cond: [ {$ne: [ "$$this.sources.groupId", "0923689a-e009-4d67-8db5-5ba40f840bf3" ] }, {$concatArrays: ["$$this.sources", "$$value.sources"]}, {$concatArrays: ["$$this.sources", []]} ] }, {} ] } } } } } ])
需求说明
- 若
memberOfGroups数组中不存在指定groupId的元素,则添加对应的memberOfGroup对象; - 若
roles数组中不存在指定roleId的元素,则添加对应的Role对象; - 若
roles数组存在匹配roleId的元素,但sources子数组无指定groupId元素,则添加对应的source对象。
正确的更新管道
针对上述需求,以下是可以实现功能的update pipeline:
db.principals.update({ _id: "67448af8-a68b-4d08-8948-2cddca57d708" }, [ // 初始化空数组避免null问题 { $set: { memberOfGroups: {$ifNull: ["$memberOfGroups", []]}, roles: {$ifNull: ["$roles", []]} } }, // 处理memberOfGroups:不存在则添加 { $set: { memberOfGroups: { $cond: [ { $in: [ "ba93384d-d18a-4b36-9a24-7d3ebb1619d7", "$memberOfGroups._id" ] }, "$memberOfGroups", { $concatArrays: [ "$memberOfGroups", [{_id: "ba93384d-d18a-4b36-9a24-7d3ebb1619d7", name: "test group"}] ] } ] } } }, // 处理roles数组:先判断是否存在目标role,不存在则添加;存在则更新其sources { $set: { roles: { $let: { vars: { targetRoleId: "0923689a-e009-4d67-8db5-5ba40f840bf3", newRole: { _id: "0923689a-e009-4d67-8db5-5ba40f840bf3", name: "testRole", sources: [{type: "group", linkType: "org", groupId: "0923689a-e009-4d67-8db5-5ba40f840bf3"}] }, newSource: {type: "group", linkType: "org", groupId: "0923689a-e009-4d67-8db5-5ba40f840bf3"} }, in: { $cond: [ // 判断是否存在目标role { $in: ["$$targetRoleId", "$roles._id"] }, // 存在则遍历roles,更新匹配的role的sources { $map: { input: "$roles", as: "role", in: { $cond: [ {$eq: ["$$role._id", "$$targetRoleId"]}, { $mergeObjects: [ "$$role", { sources: { $cond: [ // 判断sources中是否存在目标groupId { $in: ["$$newSource.groupId", "$$role.sources.groupId"] }, "$$role.sources", {$concatArrays: ["$$role.sources", ["$$newSource"]]} ] } } ] }, "$$role" ] } } }, // 不存在则添加新role {$concatArrays: ["$roles", ["$$newRole"]]} ] } } } } } ])
代码解释
- 初始化数组:用
$ifNull确保memberOfGroups和roles始终是数组,避免后续操作报错; - 处理memberOfGroups:通过
$in判断目标groupId是否存在,不存在则用$concatArrays添加新对象; - 处理roles及嵌套sources:
- 使用
$let定义变量,让代码更清晰; - 先通过
$in判断目标roleId是否存在于roles数组中; - 若存在,用
$map遍历roles,对匹配的role使用$mergeObjects更新其sources:再次通过$in判断目标groupId是否在sources中,不存在则用$concatArrays添加新source; - 若不存在,直接用
$concatArrays添加新的role对象。
- 使用
内容的提问来源于stack exchange,提问作者user2252915
相关产品推荐
相关产品推荐

