【问题标题】:Resursively include jsonb postgresql递归地包含 jsonb postgresql
【发布时间】:2022-01-19 15:03:11
【问题描述】:

我有一个看起来像这样的闭包表:

| id  | childrenId                           | parentId                             | childrenName | depth | | |
|-----|--------------------------------------|--------------------------------------|--------------|-------|---|
| --  | ----------                           | --------                             | ------------ | ----- | | |
| 98  | 8b4a5764-37ec-4ef4-8a3e-570c2e9920dd | 8b4a5764-37ec-4ef4-8a3e-570c2e9920dd | country1     | 0     | | |
| 99  | 8b4a5764-37ec-4ef4-8a3e-570c2e9920dd | |                                    | country1     | 1     | | |
| 100 | 5d95fb4a-abd4-48e6-81e3-c40bc420410c | 5d95fb4a-abd4-48e6-81e3-c40bc420410c | country2     | 0     | | |
| 101 | 5d95fb4a-abd4-48e6-81e3-c40bc420410c | |                                    | country2     | 1     | | |
| 102 | cd8bbeca-1a30-4a13-893a-1e8f95571924 | cd8bbeca-1a30-4a13-893a-1e8f95571924 | site2        | 0     | | |
| 105 | 1c58f75c-8b60-473c-b5b9-956d08ffb669 | 1c58f75c-8b60-473c-b5b9-956d08ffb669 | site1        | 0     | | |
| 106 | 1c58f75c-8b60-473c-b5b9-956d08ffb669 | 5d95fb4a-abd4-48e6-81e3-c40bc420410c | site1        | 1     | | |
| 107 | 1c58f75c-8b60-473c-b5b9-956d08ffb669 | |                                    | site1        | 2     | | |
| 108 | 51b31260-a335-46f4-94d2-130ffc7e009e | 51b31260-a335-46f4-94d2-130ffc7e009e | building1    | 0     | | |
| 109 | 51b31260-a335-46f4-94d2-130ffc7e009e | cd8bbeca-1a30-4a13-893a-1e8f95571924 | building1    | 1     | | |
| 112 | 746d0ac8-144a-401a-b26b-8313a4667b78 | 746d0ac8-144a-401a-b26b-8313a4667b78 | floor1       | 0     | | |
| 113 | 746d0ac8-144a-401a-b26b-8313a4667b78 | 51b31260-a335-46f4-94d2-130ffc7e009e | floor1       | 1     | | |
| 114 | 746d0ac8-144a-401a-b26b-8313a4667b78 | cd8bbeca-1a30-4a13-893a-1e8f95571924 | floor1       | 2     | | |
| 117 | cd8bbeca-1a30-4a13-893a-1e8f95571924 | 5d95fb4a-abd4-48e6-81e3-c40bc420410c | site2        | 1     | | |
| 118 | 51b31260-a335-46f4-94d2-130ffc7e009e | 5d95fb4a-abd4-48e6-81e3-c40bc420410c | building1    | 2     | | |
| 119 | 746d0ac8-144a-401a-b26b-8313a4667b78 | 5d95fb4a-abd4-48e6-81e3-c40bc420410c | floor1       | 3     | | |
| 120 | cd8bbeca-1a30-4a13-893a-1e8f95571924 | |                                    | site2        | 2     | | |
| 121 | 51b31260-a335-46f4-94d2-130ffc7e009e | |                                    | building1    | 3     | | |
| 122 | 746d0ac8-144a-401a-b26b-8313a4667b78 | |                                    | floor1       | 4     | | |
| 123 | af574093-8ec2-42ac-9779-d3894cb329fc | af574093-8ec2-42ac-9779-d3894cb329fc | room1        | 0     | | |
| 124 | af574093-8ec2-42ac-9779-d3894cb329fc | 746d0ac8-144a-401a-b26b-8313a4667b78 | room1        | 1     | | |
| 125 | af574093-8ec2-42ac-9779-d3894cb329fc | 51b31260-a335-46f4-94d2-130ffc7e009e | room1        | 2     | | |
| 126 | af574093-8ec2-42ac-9779-d3894cb329fc | cd8bbeca-1a30-4a13-893a-1e8f95571924 | room1        | 3     | | |
| 127 | af574093-8ec2-42ac-9779-d3894cb329fc | 5d95fb4a-abd4-48e6-81e3-c40bc420410c | room1        | 4     | | |
| 128 | af574093-8ec2-42ac-9779-d3894cb329fc | |                                    | room1        | 5     | | |

使用一些“带有递归”的 sql(抓取:here):

with recursive 
recursive_tree_parents as
(
     select "childrenId",'{}'::uuid[] as parents, "childrenName", "depth"
     from "ResourcesTree"
     -- Top filter  = depth
     where "parentId" isnull and depth = 1
     union all
     select c."childrenId",parents || c."parentId",  c."childrenName", p.depth+1
       from recursive_tree_parents p
       join "ResourcesTree" c
       on c."parentId" = p."childrenId"
       where c."parentId" != c."childrenId" 
       and c.depth = 1 
       and not c."childrenId" = any(parents)
),
recursive_tree_childrens as
(
     select c."parentId",
            json_agg(jsonb_build_object('Name', c."childrenName") 
            ||jsonb_build_object('childrenId', c."childrenId"))::jsonb as js,
            tree.depth
      from recursive_tree_parents tree
      join "ResourcesTree" c using("childrenId")
      where c."childrenId" in 
          ( SELECT distinct "childrenId"
            FROM "ResourcesTree" b
            -- Bottom Filter = tree.depth
            where c.depth = 1 and tree.depth < 6
            ) 
      and not c."childrenId" = any(parents)
        group by c."parentId", tree.depth
        --this seems to do nothing
  union all
     select c."parentId",
               jsonb_build_object('Name', c."childrenName")
            ||jsonb_build_object('id', c."childrenId")   
            || jsonb_build_object('Childrens', js) as js,
            tree.depth
       from recursive_tree_childrens tree
       join "ResourcesTree" c on c."parentId"::text = tree.js->> 'childrenId'
       --end
),
final_step as
(
  select *
  from recursive_tree_childrens
  order by depth desc
)
select * from final_step

我设法在下面获得了类似的东西:

| parentId                             | js                                                                                                                                                       | depth | | |
|--------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|-------|---|
| --------                             | --                                                                                                                                                       | ----- | | |
| 746d0ac8-144a-401a-b26b-8313a4667b78 | [{"Name": "room1", "childrenId": "af574093-8ec2-42ac-9779-d3894cb329fc"}]                                                                                | 5     | | |
| 51b31260-a335-46f4-94d2-130ffc7e009e | [{"Name": "floor1", "childrenId": "746d0ac8-144a-401a-b26b-8313a4667b78"}]                                                                               | 4     | | |
| cd8bbeca-1a30-4a13-893a-1e8f95571924 | [{"Name": "building1", "childrenId": "51b31260-a335-46f4-94d2-130ffc7e009e"}]                                                                            | 3     | | |
| 5d95fb4a-abd4-48e6-81e3-c40bc420410c | [{"Name": "site1", "childrenId": "1c58f75c-8b60-473c-b5b9-956d08ffb669"}, {"Name": "site2", "childrenId": "cd8bbeca-1a30-4a13-893a-1e8f95571924"}]       | 2     | | |
| |                                    | [{"Name": "country1", "childrenId": "8b4a5764-37ec-4ef4-8a3e-570c2e9920dd"}, {"Name": "country2", "childrenId": "5d95fb4a-abd4-48e6-81e3-c40bc420410c"}] | 1     | | |

我想以某种方式递归地将所有内容合并到一行中,以获得如下所示的 json:

[[{"Name":"country1","childrenId":"8b4a5764-37ec-4ef4-8a3e-570c2e9920dd"},{"Name":"country2","childrenId":"5d95fb4a-abd4-48e6-81e3-c40bc420410c","childrens":[{"Name":"site1","childrenId":"1c58f75c-8b60-473c-b5b9-956d08ffb669"},{"Name":"site2","childrenId":"cd8bbeca-1a30-4a13-893a-1e8f95571924","childrens":[{"Name":"building1","childrenId":"51b31260-a335-46f4-94d2-130ffc7e009e","childrens":[{"Name":"floor1","childrenId":"746d0ac8-144a-401a-b26b-8313a4667b78","childrens":[{"Name":"room1","childrenId":"af574093-8ec2-42ac-9779-d3894cb329fc"}]}]}]}]}]]

有人想通过查询来实现吗?

谢谢

【问题讨论】:

    标签: json postgresql


    【解决方案1】:

    下面的解决方案需要首先基于jsonb_set() 函数创建用户定义的聚合:

    CREATE OR REPLACE FUNCTION jsonb_set(x jsonb, y jsonb, p text[], e jsonb, b boolean)
    RETURNS jsonb LANGUAGE sql AS $$
    SELECT CASE WHEN x IS NULL THEN e ELSE jsonb_set(x, p, e, b) END ; $$ ;
    
    CREATE OR REPLACE AGGREGATE jsonb_set_agg(x jsonb, p text[], e jsonb, b boolean)
    (SFUNC = jsonb_set, STYPE = jsonb) ;
    

    下面的查询应该提供预期的结果:

    WITH RECURSIVE list AS
    (
    SELECT parentid
         , array_agg(childrenid) FILTER (WHERE depth = 1) AS children_array
         , CASE
             WHEN parentid IS NULL
             THEN jsonb_agg(childrenid) FILTER (WHERE depth = 1)
             ELSE jsonb_build_object( 'Name', max(childrenname) FILTER (WHERE depth = 0)
                                    , 'childrenid', parentid
                                    )
           END AS data
      FROM your_table
     GROUP BY parentid
    ), rec_list (parentid, children_array, data, path, next_path, rank) AS
    (
    SELECT p.parentid, p.children_array, data, NULL :: text[], '{', 1
      FROM list AS p
     WHERE p.parentid IS NULL
    UNION ALL
    SELECT c.parentid, c.children_array
         , c.data  || CASE
                        WHEN c.children_array IS NULL 
                        THEN '{}' :: jsonb
                        ELSE jsonb_build_object('childrens', to_jsonb(c.children_array)) 
                      END
         , p.path || (p.next_path || (array_position(p.children_array, c.parentid)-1) :: text || '}') :: text[]
         , '{childrens,'
         , p.rank + 1
      FROM rec_list AS p
     INNER JOIN list AS c
        ON p.children_array @> array[c.parentid]
    )
    SELECT jsonb_set_agg(NULL :: jsonb, path, data, true ORDER BY rank)
      FROM rec_list
    

    在dbfiddle 中查看演示。

    【讨论】:

    • 感谢您的解决方案,它工作正常。我重新阅读了我的问题,并意识到它并不清楚。我的意思是我想知道我们是否可以通过简单的查询(而不是函数)设法做到这一点。对不起,我不会说英语
    • 我还看到我没有指定我希望能够从顶部或底部进行过滤。我修改了我的问题中的查询
    • 我建议您的解决方案是查询。你还需要什么?
    • 你是对的,对不起。我不是高级数据库用户,我认为 create 函数需要 postgresql 服务器上的特定权限。您对顶部和底部过滤器有任何线索
    • 此外,这个函数是在一个api路由上调用的,调用create命令返回一个错误'function \"jsonb_set_agg\" already exists with same argument type"。无论如何谢谢你的帮助。跨度>
    猜你喜欢
    • 2021-12-07
    • 1970-01-01
    • 2016-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-23
    • 2015-02-14
    • 2011-12-05
    相关资源
    最近更新 更多