MongoDB中如何将权限集合的组用户解析为对应组内用户
MongoDB 将权限集合中的组替换为对应用户的聚合实现
集合数据
permissions集合
[{ "_id": 1, "resource": "resource:docs/61", "permissions": [ { "permission": "role:documentOwner", "users": [ "user:abc", "user:def", "group:abc", "group:bff" ] }, { "permission": "document.read", "users": ["user:xxx"] }, { "permission": "document.update", "users": ["user:xxx"] } ] }]
groups集合
[ { "_id": 1, "id": "abc", "name": "Test Group", "users": ["cpo", "yyy"] }, { "_id": 2, "id": "bff", "name": "Another Group", "users": ["xxx"] } ]
需求
查询permissions集合中resource:docs/61的数据,将每个权限项users中的组(格式如group:xxx)替换为对应组内的用户(格式转换为user:xxx),最终结果如下:
{ "resource": "resource:docs/61", "permissions": [ { "permission": "role:documentOwner", "users": [ "user:abc", "user:def", "user:cpo", "user:yyy", "user:xxx" ] }, { "permission": "document.read", "users": ["user:xxx"] }, { "permission": "document.update", "users": ["user:xxx"] } ] }
用户现有尝试(未完成)
db.permissions.aggregate([ { "$match": { "resource": "resource:docs/61" } }, { $unwind: "$permissions" }, { "$lookup": { "from": "groups", "let": { "users": { "$filter": { "input": "$permissions.users", "as": "user", "cond": { "$ne": [ -1, { "$indexOfCP": [ "$$user", "group:" ] } ] } } } }, "pipeline": [ { "$match": { "$expr": { "$in": [ { "$concat": [ "group:", "$id" ] }, "$$users" ] } } }, { "$project": { "_id": 0, "id": { "$concat": [ "group:", "$id" ] }, "users": 1 } } ], "as": "groups" } }, { "$project": { "groups": 1, "permissions": { "permission": "$permissions.permission", "users": "permissions.users" } } } ])
正确实现方案
提供两种可行方案,推荐第一种无需$unwind的高效实现:
方案一:无需$unwind的聚合管道
db.permissions.aggregate([ // 匹配目标资源 { "$match": { "resource": "resource:docs/61" } }, // 关联groups集合,获取所有相关组的映射关系 { "$lookup": { "from": "groups", "let": { // 提取当前权限中的所有组ID(去掉group:前缀) "groupIds": { "$map": { "input": { "$filter": { "input": "$permissions.users", "as": "u", "cond": { "$eq": [{ "$indexOfCP": ["$$u", "group:"] }, 0] } } }, "as": "g", "in": { "$substrCP": ["$$g", 6, { "$strLenCP": "$$g" }] } } } }, "pipeline": [ { "$match": { "$expr": { "$in": ["$id", "$$groupIds"] } } }, { "$project": { "_id": 0, "groupId": "$id", "users": "$users" } } ], "as": "groupMappings" } }, // 遍历每个权限项,替换组为对应用户 { "$addFields": { "permissions": { "$map": { "input": "$permissions", "as": "perm", "in": { "permission": "$$perm.permission", "users": { "$reduce": { "input": "$$perm.users", "initialValue": [], "in": { "$concatArrays": [ "$$value", { "$cond": [ // 判断当前项是否为组 { "$eq": [{ "$indexOfCP": ["$$this", "group:"] }, 0] }, // 是组则转换为对应用户列表(添加user:前缀) { "$map": { "input": { "$arrayElemAt": [ "$groupMappings.users", { "$indexOfArray": [ "$groupMappings.groupId", { "$substrCP": ["$$this", 6, { "$strLenCP": "$$this" }] } ] } ] }, "as": "u", "in": { "$concat": ["user:", "$$u"] } } }, // 是普通用户则直接加入数组 ["$$this"] ] } ] } } } } } } } }, // 移除不需要的中间字段 { "$project": { "_id": 0, "groupMappings": 0 } } ])
方案二:使用$unwind后重新分组
如果偏好拆分-聚合的直观逻辑,可使用此方案:
db.permissions.aggregate([ { "$match": { "resource": "resource:docs/61" } }, { "$unwind": "$permissions" }, // 拆分每个权限的users数组,逐个处理 { "$unwind": "$permissions.users" }, // 关联groups集合,获取组对应的用户 { "$lookup": { "from": "groups", "let": { "userOrGroup": "$permissions.users" }, "pipeline": [ { "$match": { "$expr": { "$eq": [ { "$concat": ["group:", "$id"] }, "$$userOrGroup" ] } } }, { "$unwind": "$users" }, { "$project": { "_id": 0, "user": { "$concat": ["user:", "$users"] } } } ], "as": "groupUsers" } }, // 转换每个项为最终用户格式 { "$addFields": { "finalUsers": { "$cond": [ { "$ne": ["$groupUsers", []] }, "$groupUsers.user", ["$permissions.users"] ] } } }, // 按权限项合并用户列表 { "$group": { "_id": { "resource": "$resource", "permission": "$permissions.permission" }, "users": { "$addToSet": { "$arrayElemAt": ["$finalUsers", 0] } } } }, // 重新聚合回原结构 { "$group": { "_id": "$_id.resource", "permissions": { "$push": { "permission": "$_id.permission", "users": "$users" } } } }, { "$project": { "_id": 0, "resource": "$_id", "permissions": 1 } } ])
方案说明
- 方案一:通过一次
$lookup获取所有相关组的映射,再用$map+$reduce直接处理权限数组,避免多次拆分合并,性能更优。 - 方案二:通过两次
$unwind拆分层级,关联组后转换格式,再通过两次$group聚合回原结构,逻辑更直观但性能略逊。
内容的提问来源于stack exchange,提问作者bflemi3
相关产品推荐
相关产品推荐

