You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 03:25:16