在 Postgres 中以嵌套 JSON 形式返回邻接列表

问题描述 投票:0回答:2

考虑到两列 Postgres 表(id,parent_id)的最简单基本情况,有没有办法查询 id 并以嵌套 json 结构的形式返回所有子项,如下所示?

{
    "id": 1,
    "children": [{
        "id": 2,
        "children": [{
            "id": 3,
            "children": []
        }]
    }]
}

我了解如何递归表,但我无法拼凑出如何使用任何 psql json 函数来返回如上所述的结果。也许我应该使用我选择的语言在后处理中进行转换?

当前进度的

SQLFiddle

json postgresql recursion adjacency-list
2个回答
3
投票

非常困难,花了我几个小时才解决:-P

CREATE TABLE foo (
id INT PRIMARY KEY,
parent_id INT);

insert into foo values (1, null);
insert into foo values (2, 1);
insert into foo values (3, 2);

WITH RECURSIVE 
    tree AS (
    SELECT 1 AS round, id, parent_id, ARRAY(SELECT id FROM foo WHERE parent_id = f.id) AS children
    FROM foo f
    WHERE id = 1
    UNION ALL
    SELECT round+1, f.id, f.parent_id, ARRAY(SELECT id FROM foo WHERE parent_id = f.id) AS children
    FROM tree t
    JOIN foo f ON (f.id = ANY(t.children))
    ),
    rev AS (
    SELECT r.round-1 AS round,
            to_jsonb(ARRAY(
                    SELECT a
                    FROM (
                        SELECT f.parent_id AS id, json_agg(jsonb_build_object('id', f.id, 'children', '{}'::text[])) AS children
                        FROM tree t
                        JOIN foo f ON (f.id = t.id)
                        WHERE t.round = r.round
                        GROUP BY f.parent_id
                        ) a
                    )) AS list
    FROM (SELECT MAX(round)::int AS round FROM tree) r
    UNION ALL
    SELECT r.round-1,
            to_jsonb(ARRAY(
                    SELECT a
                    FROM (
                        SELECT f.parent_id AS id, json_agg(jsonb_build_object('id', f.id, 'children', t->'children')) AS children
                        FROM jsonb_array_elements(list) t
                        JOIN foo f ON (f.id = (t->>'id')::int)
                        GROUP BY f.parent_id
                        ) a
                    )) AS list
    FROM rev r
    WHERE round > 1
    )
SELECT list as nested_json_tree
FROM rev
WHERE round = 1

复杂性在于要求首先构建树(自上而下),然后从树自下而上构建对象。由于递归查询的限制,自下而上的递归很棘手,例如 UNION ALL 部分中的递归别名无法分组,也无法包含在子查询中。我通过反向循环展开来解决了这个问题。

此查询应正确构建复杂的树,每个节点有多个子节点,以及任意数量的嵌套级别。


0
投票

这只会返回主路径。如果您在任何级别添加多个子级,它将丢弃这些子级。尝试使用:

CREATE TABLE foo (
id INT PRIMARY KEY,
parent_id INT);

insert into foo values (1, null);
insert into foo values (2, 1);
insert into foo values (3, 2);
insert into foo values (4, 2);
insert into foo values (5, 4);
insert into foo values (6, 1);
insert into foo values (7, 8);
insert into foo values (8, 5);
insert into foo values (9, 2);
insert into foo values (10, 9);

退货

[{"id": 1, "children": [{"id": 2, "children": [{"id": 4, "children": [{"id": 5, "children": [{"id": 8, "children": [{"id": 7, "children": []}]}]}]}]}]}]
© www.soinside.com 2019 - 2024. All rights reserved.