【问题标题】:Creative json tree structure in postgrespostgres中的创意json树结构
【发布时间】:2021-12-07 19:26:33
【问题描述】:

我正在尝试创建一个 JSON 表示任意深度和广泛的层次结构,例如生物:

CREATE TABLE creatures (
    name text PRIMARY KEY,
    parent text REFERENCES creatures(name)
);

INSERT INTO creatures(name,parent)
VALUES
('amoeba',NULL),
('beetle','amoeba'),
('coelacanth','amoeba'),
('salmon','coelacanth'),
('tuna','coelacanth'),
('lizard','coelacanth'),
('t-rex','lizard'),
('plant',NULL);

我想把它变成这样的 JSON:

[{"name":"amoeba",
  "children": [{"name": "beetle",
                "children": []}, 
               {"name": "coelacanth",
                "children": [{"name": "tuna",
                              "children": []}, 
                             {"name": "salmon",
                              "children": []} 
                             {"name": "lizard",
                              "children": [{"name": "t-rex",
                                            "children": []}]}
                             ]}]},
 {"name": "plant",
  "children": []}]

这可以在 Postgres 中实现吗?

到目前为止我已经尝试过

WITH RECURSIVE r AS
    -- Get all the leaf nodes, group by parent.
    (SELECT parent,
            json_build_object('name', parent, 
                              'children', array_agg(name)) AS json
     FROM creatures c
     WHERE parent NOTNULL
         AND NOT EXISTS
             (SELECT 1
              FROM creatures c2
              WHERE c.name = c2.parent)
     GROUP BY parent
     
     UNION 
     
     -- Recursive term - go one step up towards the top.
     SELECT c.parent,
            json_build_object('name', c.parent, 
                              'children', array_agg(c.name)) AS json
     FROM r
     JOIN creatures c ON r.parent = c.name
     GROUP BY c.parent)
SELECT *
FROM r;

但它失败了

ERROR:  aggregate functions are not allowed in a recursive query's recursive term
LINE 19:                            'children', array_agg(c.name)) AS...

有什么办法可以解决这个问题,或者有其他解决方案可以让我成为我的好树吗?

【问题讨论】:

  • 使用递归函数。并确保您的creatures 不包含圆形路径……
  • @Bergi。没有圆形路径。正如我在问题中所描述的那样,这就是我想要做的......
  • 好吧,但是 Postgres 不知道这一点,所以它拒绝与递归查询进行联合,从而永远生成行……
  • 我相信this 是您尝试的查询,但这实际上并不是在做递归

标签: json postgresql


【解决方案1】:

首先你应该在postgres中使用jsonb格式而不是json格式,参见文档here

一般来说,大多数应用程序应该更喜欢将 JSON 数据存储为 jsonb,除非有非常特殊的需求,例如遗留 关于对象键排序的假设..

然后,下面是一种将 jsonb 转换为文本的结果的方法,因为 jsonb 替换函数 jsonb_set 在您的情况下是不合适的:

CREATE VIEW parent_children (parent, children, root, cond) AS
(   SELECT jsonb_build_object('name', c.parent, 'children', '[]' :: jsonb) :: text AS parent
         , jsonb_agg(jsonb_build_object('name', c.name, 'children', '[]' :: jsonb)) :: text AS children
         , array[c.parent] AS root
         , array[c.parent] AS cond
      FROM creatures AS c
     GROUP BY c.parent
) ;

WITH RECURSIVE list(parent, children, root, cond) AS
(   SELECT children, children, root, cond
      FROM parent_children
     WHERE root = array[NULL]   -- start with the root parents
    UNION
    SELECT p.parent
         , replace(p.children, c.parent, replace(c.parent, '[]', c.children))
         , p.root
         , p.cond || c.cond
      FROM list AS p
     INNER JOIN parent_children AS c
        ON position(c.parent IN p.children) > 0
       AND NOT p.cond @> c.root -- condition to avoid circular path
)
SELECT children :: jsonb
  FROM list AS l
  ORDER BY array_length(cond, 1) DESC
  LIMIT 1 ;

结果是:

[
  {
    "name": "amoeba",
    "children": [
      {
        "name": "beetle",
        "children": []
      },
      {
        "name": "coelacanth",
        "children": [
          {
            "name": "salmon",
            "children": []
          },
          {
            "name": "tuna",
            "children": []
          },
          {
            "name": "lizard",
            "children": [
              {
                "name": "t-rex",
                "children": []
              }
            ]
          }
        ]
      }
    ]
  },
  {
    "name": "plant",
    "children": []
  }
] 

【讨论】:

    猜你喜欢
    • 2017-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-30
    • 1970-01-01
    • 2011-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多