如何显示基于投票的递归评论?

时间:2018-11-18 16:01:33

标签: javascript node.js postgresql recursion recursive-query

我在我的网站(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.. | ...

我认为应该通过递归来完成,但是我不知道该怎么做。

2 个答案:

答案 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);