PostgreSQL实体化路径/ Ltree到分层JSON对象

Dac*_*d3r 6 postgresql tree json materialized-path-pattern ltree

我具有使用PostgreSQL的ltree模块构建的这种物化路径树结构。

  • id1
  • id1.id2
  • id1.id2.id3
  • id1.id2.id5
  • id1.id2.id3.id4 ...等

我当然可以轻松地使用ltree从整棵树或特定路径/子路径中获取所有节点,但是当我这样做时,自然地,我得到的是很多行(等于节点中的节点的数组/切片)结束.. Golang /您使用的任何编程语言)

我要做的是将树(理想情况下是从某个起点和终点的路径/点)作为树状JSON树对象等来获取

{
  "id": 1,
  "path": "1",
  "name": "root",
  "children": [
    {
      "id": 2,
      "path": "1.2",
      "name": "Node 2",
      "children": [
        {
          "id": 3,
          "path": "1.2.3",
          "name": "Node 3",
          "children": [
            {
              "id": 4,
              "path": "1.2.3.4",
              "name": "Node 4",
              "children": [

              ]
            }
          ]
        },
        {
          "id": 5,
          "path": "1.2.5",
          "name": "Node 5",
          "children": [

          ]
        }
      ]
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

我从线性(非分层)行/数组/切片结果集中知道,我当然可以在Golang中进行分解,并在其中进行创建此json的必要业务逻辑,但是如果有方便的话,肯定会好得多直接通过PostgreSQL实现此目标的方法。

那么,您如何在PostgreSQL中将ltree树形结构输出到json-从起始路径到结束路径都集中地?

如果您不知道ltree,我想这个问题可能会进一步推广到“分层路径树到分层json”

我也在考虑在ltree路径之外的所有节点上添加一个parent_id的想法,因为至少那样我就可以使用使用该id的递归调用来获取我猜的json ...我也考虑过根据父ID的更改发生时间在该parent_id上放置触发器以管理路径(保持更新)-我知道这是另一个问题,但是也许您也可以告诉我您的观点,对此吗?

我希望有一些天才可以帮助我。:)

为了方便起见,这里有一个示例创建脚本,可用于节省时间:

CREATE TABLE node
(
  id bigserial NOT NULL,
  path ltree NOT NULL,
  name character varying(255),
  CONSTRAINT node_pkey PRIMARY KEY (id)
);

INSERT INTO node (path,name) 
VALUES ('1','root');

INSERT INTO node (path,name) 
VALUES ('1.2','Node 1');

INSERT INTO node (path,name) 
VALUES ('1.2.3','Node 3');

INSERT INTO node (path,name) 
VALUES ('1.2.3.4','Node 4');

INSERT INTO node (path,name) 
VALUES ('1.2.5','Node 5');
Run Code Online (Sandbox Code Playgroud)

Dac*_*d3r 4

我能够找到并稍微更改它以使用 ltree 的物化路径,而不是像邻接树结构中经常使用的父 ID。

虽然我仍然希望有更好的解决方案,但我想这可以完成工作。

我觉得除了 ltree 路径之外,我还必须添加parent_id,因为这当然没有引用父 id 那么快。

好的,归功于这个人的解决方案,这是我使用 ltree 的子路径、ltree2text 和 nlevel 稍微修改的代码来实现完全相同的效果:

WITH RECURSIVE c AS (
    SELECT *, 1 as lvl
    FROM node
    WHERE id=1
  UNION ALL
    SELECT node.*, c.lvl + 1 as lvl
    FROM node
    JOIN c ON ltree2text(subpath(node.path,nlevel(node.path)-2 ,nlevel(node.path))) = CONCAT(subpath(c.path,nlevel(c.path)-1,nlevel(c.path)),'.',node.id)
),
maxlvl AS (
  SELECT max(lvl) maxlvl FROM c
),
j AS (
    SELECT c.*, json '[]' children
    FROM c, maxlvl
    WHERE lvl = maxlvl
  UNION ALL
    SELECT (c).*, json_agg(j) children FROM (
      SELECT c, j
      FROM j
      JOIN c ON ltree2text(subpath(j.path,nlevel(j.path)-2,nlevel(j.path))) = CONCAT(subpath(c.path,nlevel(c.path)-1,nlevel(c.path)),'.',j.id)
    ) v
    GROUP BY v.c
)
SELECT row_to_json(j)::text json_tree
FROM j
WHERE lvl = 1;
Run Code Online (Sandbox Code Playgroud)

不过,到目前为止,这个解决方案存在一个大问题.. 请参阅下图以了解错误(缺少节点 5):

JSON 对象中缺少节点 5