我在我的网站(privacyfirstproducts.com)上有一个评论部分,我希望以这种结构显示评论(就像黑客新闻一样):
comment 1 (10 upvotes)
comment 4 (reply on comment 1, 7 upvotes)
comment 5 (reply on comment 1, 5 upvotes)
comment 8 (reply on comment 5, 8 upvotes)
...
comment 9 (reply on comment 1, 3 upvotes)
comment 3 (reply on comment 1, 0 upvotes)
comment 10 (reply on comment 1, 0 upvotes)
...
comment 6 (2 upvotes)
comment 7 (reply on comment 3, 2 upvotes)
comment 2 (0 upvotes)
...
我有这张PostgreSQL comments
-表:
comment_id | original_id | upvotes | text | ...
------------------------------------------------------
1 | NULL | 10 | Hi.. | ...
2 | NULL | 0 | Je.. | ...
3 | 1 | 0 | Di.. | ...
4 | 1 | 7 | Si.. | ...
5 | 1 | 5 | Op.. | ...
6 | NULL | 2 | Op.. | ...
7 | 6 | 2 | Op.. | ...
8 | 5 | 8 | Op.. | ...
9 | 1 | 3 | Op.. | ...
10 | 1 | 0 | Th.. | ...
我希望将此作为Postgresql的输出:
comment_id | original_id | upvotes | deep | text | ...
------------------------------------------------------
1 | NULL | 10 | 0 | Hi.. | ...
4 | 1 | 7 | 1 | Si.. | ...
5 | 1 | 5 | 1 | Op.. | ...
8 | 5 | 8 | 2 | Op.. | ...
9 | 1 | 3 | 1 | Op.. | ...
3 | 1 | 0 | 1 | Di.. | ...
10 | 1 | 0 | 1 | Th.. | ...
6 | NULL | 2 | 0 | Op.. | ...
7 | 6 | 2 | 1 | Op.. | ...
2 | NULL | 0 | 0 | Je.. | ...
我认为应该通过递归来完成,但是我不知道该怎么做。
答案 0 :(得分:1)
递归查询记录在手册的CTE部分。
首先选择根行(在您的情况下为顶层注释;注释在original_id IS NULL
处)。
递归查询的第二部分(在下面的示例中,UNION
之后)将子注释与已找到的注释合并在一起。它会自动重复直到找不到更多行。在您的情况下,第二个选择者需要将孩子的评论与child.original_id = parent.comment_id
上的父母一起加入。
查找每个节点的depth
很容易-进行第二次选择时,只需在父行的深度处加1。
棘手的部分是获得所需的排序顺序(通过投票和ID,将评论按父分组)。可以通过在数组(以下示例中的path
列)中累积注释以及每个注释的祖先ID,然后按数组对行进行排序来完成此操作。请注意,示例中否定了投票计数,以便首先对较高的值进行排序。这可以通过对DESC
进行排序来完成,但是如果注释具有相同的投票数,则必须取消注释ID才能首先对较早的注释进行排序。
WITH RECURSIVE comment_tree AS (
-- First select performed to get top level rows
SELECT
comment_id,
original_id,
upvotes,
text,
0 depth, -- depth in the tree
ARRAY[-upvotes, comment_id] path -- used to sort by vote then ID
FROM comment WHERE original_id IS NULL
UNION
-- Self referential select performed repeatedly until no more rows are found
SELECT
c.comment_id,
c.original_id,
c.upvotes,
c.text,
ct.depth + 1,
ct.path || ARRAY[-c.upvotes, c.comment_id]
FROM comment c
JOIN comment_tree ct ON c.original_id = ct.comment_id
)
SELECT * FROM comment_tree ORDER BY path;
答案 1 :(得分:0)
Javascript解决方案可以按id索引注释,遍历它们,并使其指向正确的父对象(通过使用索引)。最后,我们可以从索引返回根节点(使用original_id === null
)
const comments = [
{ id: 1, original_id: null, upvotes: 10, text: 'Hi..' },
{ id: 2, original_id: null, upvotes: 0, text: 'Je..' },
{ id: 3, original_id: 1, upvotes: 0, text: 'Di..' },
{ id: 4, original_id: 1, upvotes: 7, text: 'Si..' },
{ id: 5, original_id: 1, upvotes: 5, text: 'Op..' },
{ id: 6, original_id: null, upvotes: 2, text: 'Op..' },
{ id: 7, original_id: 6, upvotes: 2, text: 'Op..' },
{ id: 8, original_id: 5, upvotes: 3, text: 'Op..' },
{ id: 9, original_id: 1, upvotes: 3, text: 'Op..' }
];
let index = comments.reduce((a, c) => {
let comment = Object.assign({}, c);
comment.children = [];
a.set(c.id, comment);
return a;
}, new Map());
Array.from(index.values()).forEach(comment => {
if (comment.original_id) index.get(comment.original_id).children.push(comment)
});
const res = Array.from(index.values()).filter(c => c.original_id === null);
console.log(res);