0

我正在尝试在 Postgres 11.5 中构建用户权限结构。

基本思想是一个用户可以属于多个组,一个组可以拥有多个应用程序的权限。用户的权限(如果有)将覆盖在组级别设置的任何权限。

用户和用户组级别的权限将存储为 json 对象,我想将这些对象与用户权限合并在一起,如果有任何重叠,则覆盖用户组权限。

示例: Brendan、James 和其他 n 个用户在完全相同的用户组中,但 James 应该无法访问 app2。

设置:

CREATE TABLE public.users
(
    uid character varying COLLATE pg_catalog."default" NOT NULL,
    ugid character varying[],
    permissions json,
    CONSTRAINT users_pkey PRIMARY KEY (uid)
);

INSERT INTO public.users VALUES
('brendan','{default,gisteam}','{}'),
('james','{default,gisteam}','{"app2":{"enabled":false}}');


CREATE TABLE public.usergroups
(
    ugid character varying COLLATE pg_catalog."default" NOT NULL,
    permissions json,
    CONSTRAINT usergroups_pkey PRIMARY KEY (ugid)
);
INSERT INTO public.usergroups VALUES
('default','{"app1":{"enabled":true}}'),
('gisteam','{"app2":{"enabled":true},"app3":{"enabled":true}}');

询问:

SELECT uid, json_agg(permissions)
FROM (
    SELECT 
        u.uid,
        ug.permissions,
        'group' AS type
    FROM public.users u
    JOIN public.usergroups ug
    ON ug.ugid = ANY(u.ugid)
    UNION ALL
    SELECT 
        uid,
        permissions,
        'user' AS type
    FROM public.users u2
) a
GROUP BY uid;

实际查询结果:

+---------+----------------------------------------------------------------------------------------------------------+
|   uid   |                                            final_permissions                                             |
+---------+----------------------------------------------------------------------------------------------------------+
| brendan | [{"app1":{"enabled":true}},{"app2":{"enabled":true},"app3":{"enabled":true}},{}]                         |
| james   | [{"app1":{"enabled":true}},{"app2":{"enabled":true},"app3":{"enabled":true}},{"app2":{"enabled":false}}] |
+---------+----------------------------------------------------------------------------------------------------------+

这种工作,但我希望对象被展平和键合并。

期望的结果:

+---------+---------------------------------------------------------------------------+
|   uid   |                             final_permissions                             |
+---------+---------------------------------------------------------------------------+
| brendan | {"app1":{"enabled":true},"app2":{"enabled":true},"app3":{"enabled":true}} |
| james   | {"app1":{"enabled":true},"app2":{"enabled":false},"app3":{"enabled":true}}|
+---------+---------------------------------------------------------------------------+

DB小提琴:https ://www.db-fiddle.com/f/9kb1v1T82YVxWERxnWLThL/3

其他信息: 为每个应用程序在用户组级别设置的实际权限对象将比示例中更复杂,例如启用功能 A,禁用功能 B 等,实际上将来会添加更多应用程序,所以我不想如果可能,硬编码对特定应用程序或功能的任何引用。

我想从技术上讲,如果更容易的话,所需的输出将是单个用户的权限对象,因此可以GROUP BY uidWHERE uid = 'x'

问题/问题: 如何修改查询以生成扁平/合并权限 json 对象?

编辑:固定的json

4

1 回答 1

1

您指示的所需输出在语法上不是有效的 JSON。如果我猜测您真正想要什么,您可以使用 jsonb_object_agg 而不是 jsonb_agg 来获得它。您必须首先取消您选择的值,以便您可以将它们重新聚合在一起,这是通过针对 json_each 的横向连接完成的:

select uid, jsonb_object_agg(key,value) 
from (
    SELECT 
        u.uid,
        ug.permissions,
        'group' AS type
    FROM public.users u
    JOIN public.usergroups ug
    ON ug.ugid = ANY(u.ugid)
    UNION ALL
    SELECT 
        uid,
        permissions,
        'user' AS type
    FROM public.users u2) a 
CROSS JOIN LATERAL json_each(permissions) 
GROUP BY uid;

产量:

   uid   |                                  jsonb_object_agg                                  
---------+------------------------------------------------------------------------------------
 brendan | {"app1": {"enabled": true}, "app2": {"enabled": true}, "app3": {"enabled": true}}
 james   | {"app1": {"enabled": true}, "app2": {"enabled": false}, "app3": {"enabled": true}}

您的选择"group" as type令人困惑,因为它从未使用过。

于 2020-03-06T17:35:50.113 回答