【问题标题】:Merging json objects when grouping in postgres在postgres中分组时合并json对象
【发布时间】:2020-03-06 17:03:00
【问题描述】:

我正在尝试在 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 uid 替换为 WHERE uid = 'x'

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

编辑:固定 json

【问题讨论】:

  • 您想要的输出不是 JSON 的有效语法。

标签: json postgresql postgresql-11


【解决方案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 的选择令人困惑,因为它从未使用过。

【讨论】:

  • 谢谢,这工作做得很好! type 列仅用于在聚合之前测试子查询,因此现在可以删除。
猜你喜欢
  • 2019-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-24
  • 1970-01-01
相关资源
最近更新 更多