Как показать рекурсивные комментарии, основанные на upvotes? - PullRequest
0 голосов
/ 18 ноября 2018

У меня есть раздел комментариев на моем веб-сайте ( 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)
...

Iиметь эту 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 голосов
/ 20 ноября 2018

Решение Javascript может заключаться в индексации комментариев по идентификатору, итерации по ним и их указании на правильного родителя (с помощью индекса).Наконец, мы можем вернуть корневые узлы (с 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);
0 голосов
/ 19 ноября 2018

Рекурсивные запросы описаны в разделе CTE данного руководства.

Вы начинаете с выбора корневых строк (в вашем случае это комментарии верхнего уровня; комментарии, где original_id IS NULL)).

Вторая часть рекурсивного запроса (после UNION в примере ниже) объединяет дочерние комментарии с уже найденными.Это автоматически повторяется до тех пор, пока не будет найдено больше строкВ вашем случае второй выбор должен присоединить дочерние комментарии к родителям на child.original_id = parent.comment_id.

Найти depth каждого узла легко - просто добавьте 1 к глубине родительской строки, когда вы делаете второйвыберите.

Самое сложное - получить требуемый порядок сортировки (по голосам и идентификатору, сохраняя комментарии, сгруппированные по родителям).Это можно сделать, накапливая голоса вместе с идентификаторами предков каждого комментария в массиве (столбец path в следующем примере), а затем сортируя строки по массиву.Обратите внимание, что в этом примере счетчик голосов был сведен на нет, чтобы сначала отсортировать более высокие значения.Этого можно было бы достичь, отсортировав DESC, но тогда нужно было бы отменить идентификаторы комментариев, чтобы сначала отсортировать более ранние комментарии, когда комментарии имеют одинаковое количество голосов.

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;
...