sql - Postgres无限 self 加入

标签 sql postgresql go go-gorm

所以我有一篇文章,以及对这篇文章的“评论”..

评论允许人们回复..你可以回复回复..等等,这意味着最深的树根是N

表格外观的快速模型

Comments(id, news_id, user_id, body, likes)

Replies(id, parent_id) --> id here is = Comments.id

User(id, username, password)

News(id, title, body, image)

有没有办法查询 Postgres DB 给我类似的结果

因此 Replies 表中的所有 parent_id 为空的内容都是“主要”评论(又名不是回复)。如果可能的话,我希望 children 字段在自身内部填充(即回复的回复)

这甚至可以用 Postgres 实现吗?或者我是否应该获取所有 回复 并使用 评论 加入它们,然后遍历每个回复以尝试找到它的正确目的地?

顺便说一句,我正在使用 GoLang 作为我的后端,并使用 Gorm 包来访问我的 postgres 数据库

编辑: 我正在使用这个查询

with recursive commentss as (
  select r.id, r.parent, array[r.id] as all_parents, 
         c.body, u.username 
    from replies r 
          inner join comments c 
                  on c.id = r.id 
                join users u 
                  on u.id = c.user_refer 
   where (parent <> '') IS NOT TRUE 
   union all 
  select r.id, r.parent, c.all_parents || r.id, 
         co.body, u.username 
    from replies r 
          join comments co 
            on co.id = r.id 
          join users u 
            on u.id = co.user_refer 
          join commentss c 
            on r.parent = c.id 
               and r.id <> ALL (c.all_parents)
  ) 
   select * from commentss order by all_parents;

结果:

enter image description here

这更近了一步......但是我需要的是返回一个 JSON 对象,看起来像

comments: [
  {
    comment_id: ...,
    username: ...,
    comment_body: ....,
    comment_likes: ....,
    children: [...]
  },
  {
    .....
  }
]

comments 对象中的第一个项目是不是回复的评论,children 字段应该填充回复的评论...... children 中的评论也应该有他们的 children 填充到对该回复的回复

最佳答案

希望这是您的预期结果。 (我在这里做了类似的事情:https://stackoverflow.com/a/52076212/3984221)

demo: db<>fiddle

注释:

id  body          user_id  likes  
--  ------------  -------  -----  
a   foo           1        1      
b   foofoo        1        232    
c   foofoofoo     1        23232  
d   fooFOO        1        53     
e   cookies       1        864    
f   bar           1        44     
g   barbar        1        54     
h   barBAR        1        222    
i   more cookies  1        1      

表格回复

id  parent_id  
--  ---------  
a   (null)     
b   a          
c   b          
d   a          
e   (null)     
f   (null)     
g   f          
h   f          
i   (null)     

结果:

{
    "comments": [{
        "children": [],
        "username": "Mike Tyson",
        "comment_id": "i",
        "comment_body": "more cookies",
        "comment_likes": 1
    },
    {
        "children": [{
            "children": [],
            "username": "Mike Tyson",
            "comment_id": "b",
            "comment_body": "foofoo",
            "comment_likes": 232
        },
        {
            "children": [{
                "children": [],
                "username": "Mike Tyson",
                "comment_id": "c",
                "comment_body": "foofoofoo",
                "comment_likes": 23232
            }],
            "username": "Mike Tyson",
            "comment_id": "d",
            "comment_body": "fooFOO",
            "comment_likes": 53
        }],
        "username": "Mike Tyson",
        "comment_id": "a",
        "comment_body": "foo",
        "comment_likes": 1
    },
    {
        "children": [],
        "username": "Mike Tyson",
        "comment_id": "e",
        "comment_body": "cookies",
        "comment_likes": 864
    },
    {
        "children": [{
            "children": [],
            "username": "Mike Tyson",
            "comment_id": "g",
            "comment_body": "barbar",
            "comment_likes": 54
        },
        {
            "children": [],
            "username": "Mike Tyson",
            "comment_id": "h",
            "comment_body": "barBAR",
            "comment_likes": 222
        }],
        "username": "Mike Tyson",
        "comment_id": "f",
        "comment_body": "bar",
        "comment_likes": 44
    }]
}

查询:

递归:

WITH RECURSIVE parent_tree AS (
    SELECT 
        id, 
        NULL::text[] as parent_id,
        array_append('{comments}'::text[], (row_number() OVER ())::text) as path, 
        rc.children  
    FROM replies r
    LEFT JOIN LATERAL (SELECT parent_id, ARRAY_AGG(id) as children FROM replies WHERE parent_id = r.id GROUP BY parent_id) rc ON rc.parent_id = r.id
    WHERE r.parent_id IS NULL 

    UNION

    SELECT 
        r.id, 
        array_append(pt.parent_id, r.parent_id), 
        array_append(array_append(pt.path, 'children'), (row_number() OVER (PARTITION BY pt.parent_id))::text),
        rc.children      
    FROM parent_tree pt
    JOIN replies r ON r.id = ANY(pt.children)
    LEFT JOIN LATERAL (SELECT parent_id, ARRAY_AGG(id) as children FROM replies WHERE parent_id = r.id GROUP BY parent_id) rc ON rc.parent_id = r.id
), json_objects AS (
   SELECT c.id, jsonb_build_object('children', '[]'::jsonb, 'comment_id', c.id, 'username', u.name, 'comment_body', c.body, 'comment_likes', c.likes) as jsondata
   FROM comments c
   LEFT JOIN users u ON u.id = c.user_id
)
SELECT 
    parent_id, 
    path,
    jsondata
FROM parent_tree pt 
LEFT JOIN json_objects jo ON pt.id = jo.id
ORDER BY parent_id NULLS FIRST

唯一的递归部分在 CTE parent_tree 中。我在这里寻找 parent 并建立一条道路。稍后在正确位置插入 json 数据需要此路径。

第二个 CTE (json_objects) 为每个带有空子数组的评论构建一个 json 对象,稍后可以在其中插入子数组。

LATERAL 连接在回复表中搜索当前 ID 的 child ,并给出一个包含他们 ID 的数组。

末尾的 ORDER BY 子句很重要。这样可以确保所有上层节点都位于下层节点(它们的子节点)之前。否则,稍后输入到全局 json 对象中可能会失败,因为必要的父对象在正确的时刻不存在。

构建最终的 JSON 对象:

CREATE OR REPLACE FUNCTION json_tree() RETURNS jsonb AS $$
DECLARE
    _json_output jsonb;
    _temprow record;
BEGIN

    SELECT 
        jsonb_build_object('comments', '[]'::jsonb) 
    INTO _json_output;

    FOR _temprow IN
        -- <query above>
    LOOP
        SELECT jsonb_insert(_json_output, _temprow.path, _temprow.jsondata) INTO _json_output;
    END LOOP;

    RETURN _json_output;
END;
$$ LANGUAGE plpgsql;

不可能在递归中构建 json 对象,因为在查询中 jsondata 对象不是全局变量。因此,如果我将 b 作为子项添加到一个递归分支中的 a 中,它就不会存在于我将添加 c 作为的另一个分支中 child 。

所以需要生成一个全局变量。这可以在函数中完成。使用计算出的路径和子对象,一起构建最终的 json 非常简单:遍历结果集并将 json 对象添加到全局对象的路径中。

关于sql - Postgres无限 self 加入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52637916/

相关文章:

php - 需要显示SQL部分空结果

sql - 使用 JSONB 数据查询 Postgres 表

linux - Go 应用程序无法捕获信号

mongodb - 聚合管道返回错误结果与 CLI

php - 我更新时出现错误消息

SQL Server 事务处理

postgresql - postgres csv 日期空导入错误

postgresql - 如何限制PostgresQL服务器可用的内存?

pointers - 如何在 Go 中存储对操作结果的引用?

涉及join的Mysql更新语句