Как реализовать цепочки комментариев?

Я разрабатываю веб-приложение, которое может поддерживать цепочки комментариев. Мне нужна возможность переставлять комментарии в зависимости от количества полученных голосов. (Аналогично тому, как цепочки комментариев работают в Reddit )

Я хотел бы услышать комментарии сообщества SO о том, как это сделать.

Как мне оформить таблицу комментариев ? Вот структура, которую я сейчас использую:

Comment
    id
    parent_post
    parent_comment
    author
    points

Какие изменения нужно внести в эту структуру?

Как мне получить данные из этой таблицы, чтобы отобразить их правильно? (Реализация на любом языке приветствуется. Я просто хочу знать, как это сделать наилучшим образом)

О чем мне нужно позаботиться при реализации этой функции, чтобы снизить нагрузку на ЦП / базу данных?

Заранее спасибо.

Ответов (4)

Решение

Хранение деревьев в базе данных - это предмет, который имеет множество различных решений. Это зависит от того, хотите ли вы также получить субиерархию (то есть все дочерние элементы элемента X) или просто хотите захватить весь набор иерархий и построить дерево в памяти способом O (n) с использованием словаря.

У вашей таблицы есть то преимущество, что вы можете получить все комментарии к посту за один раз, отфильтровав его по родительскому посту. Поскольку вы определили родительский элемент комментария учебным / наивным способом, вам нужно построить дерево в памяти (см. Ниже). Если вы хотите получить дерево из БД, вам понадобится другой способ хранения дерева: см. Мое описание подхода, основанного на предварительном вычислении, здесь: http://www.llblgen.com/tinyforum/GotoMessage.aspx?MessageID = 17746 & ThreadID = 3208 или с помощью сбалансированных деревьев, описанных CELKO здесь :

или еще один подход: http://www.sqlteam.com/article/more-trees-hierarchies-in-sql

Если вы извлечете все в иерархии в памяти и построите там дерево, это может быть более эффективным из-за того, что запрос довольно прост: выберите .. из комментария, где ParentPost = @id ORDER BY ParentComment ASC

После этого запроса вы строите дерево в памяти всего с одним словарем, которое отслеживает кортеж CommentID - Comment. Теперь вы просматриваете набор результатов и строите дерево на лету: для каждого комментария, с которым вы сталкиваетесь, вы можете найти его родительский комментарий в словаре, а затем сохранить комментарий, который в настоящее время обрабатывается, также в этом словаре.

Я бы добавил следующие новые поля в указанную выше таблицу:

  • thread_id: идентификатор для всех комментариев, прикрепленных к определенному объекту

  • date: дата комментария (позволяет получать комментарии по порядку)

  • rank: рейтинг комментария (позволяет получить порядок комментариев по ранжированию)

Используя эти поля, вы сможете:

  1. получить все комментарии в цепочке за одну операцию
  2. упорядочить комментарии в цепочке либо по дате, либо по рангу

К сожалению, если вы хотите сохранить базу данных запросов, близкую к стандарту SQL, вам придется воссоздавать дерево в памяти. Некоторые БД предлагают специальные запросы для иерархических данных (например, Oracle)

./Алекс

Ваш текущий дизайн в основном подходит для небольших иерархий (менее тысячи элементов)

Если вы хотите получить на определенном уровне или глубине, добавьте элемент уровня в свою структуру и вычислите его как часть сохранения.

Если производительность является проблемой, используйте приличный кеш

Пара вещей, которые также следует учитывать ...

1) Когда вы говорите «сортировать как Reddit» по рангу или дате, вы имеете в виду верхний уровень или все в целом?

2) Когда вы удаляете узел, что происходит с ветвями? Вы их повторно воспитываете? В моей реализации я думаю, что редакторы будут решать - либо скрыть узел и отобразить его как «скрытый комментарий» вместе с видимыми дочерними элементами, либо скрыть комментарий и его дочерние элементы, либо уничтожить все дерево. Повторное родительство должно быть простым (просто установите родительский элемент ребенка на удаленного родителя), но все, что связано со всем деревом, кажется сложным для реализации в базе данных.

Я смотрел модуль ltree для PostgreSQL. Это должно немного ускорить операции с базой данных с участием частей дерева. Это в основном позволяет вам настроить поле в таблице, которое выглядит так:

ltreetest=# select path from test where path <@ 'Top.Science';
                path                
------------------------------------
 Top.Science
 Top.Science.Astronomy
 Top.Science.Astronomy.Astrophysics
 Top.Science.Astronomy.Cosmology

Однако сам по себе он не обеспечивает какой-либо ссылочной целостности. Другими словами, вы можете иметь записи для «Top.Science.Astronomy» без записи для «Top.Science» или «Top». Но то, что он позволяет вам делать, это такие вещи, как:

-- hide the children of Top.Science
UPDATE test SET hide_me=true WHERE path @> 'Top.Science';

или

-- nuke the cosmology branch
DELETE FROM test WHERE path @> 'Top.Science.Cosmology';

Я думаю, что в сочетании с традиционным подходом "comment_id" / "parent_id" с использованием хранимых процедур вы сможете получить лучшее из обоих миров. Вы можете быстро перемещаться по дереву комментариев в базе данных, используя свой «путь», и при этом обеспечивать ссылочную целостность с помощью «comment_id» / «parent_id». Я представляю себе что-то вроде:

CREATE TABLE comments (
comment_id SERIAL PRIMARY KEY,
parent_comment_id int REFERENCES comments(comment_id) ON UPDATE CASCADE ON DELETE CASCADE,
thread_id int NOT NULL  REFERENCES threads(thread_id) ON UPDATE CASCADE ON DELETE CASCADE,
path ltree NOT NULL,
comment_body text NOT NULL,
hide boolean not null default false
);

Строка пути для комментария выглядит так:

<thread_id>.<parent_id_#1>.<parent_id_#2>.<parent_id_#3>.<my_comment_id>

Таким образом, корневой комментарий потока "102" с comment_id "1" будет иметь путь:

102.1

И дочерний элемент, у которого comment_id равен "3", будет:

102.1.3

Некоторые дочерние элементы из числа «3» с идентификаторами «31» и «54» будут:

102.1.3.31
102.1.3.54

Чтобы скрыть узел «3» и его дочерние элементы, выполните следующее:

UPDATE comments SET hide=true WHERE path @> '102.1.3';

Я не знаю - это может добавить ненужные накладные расходы. К тому же я не знаю, в каком состоянии ltree.