Как мне реализовать резьбовые комментарии? - PullRequest
26 голосов
/ 28 февраля 2009

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

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

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

Comment
    id
    parent_post
    parent_comment
    author
    points

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

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

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

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

Ответы [ 4 ]

14 голосов
/ 28 февраля 2009

Хранение деревьев в базе данных - это тема, которая имеет много разных решений. Это зависит от того, хотите ли вы также получить подчиненную иерархию (т.е. все дочерние элементы элемента 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 - Комментарий. Теперь вы просматриваете набор результатов и строите дерево на лету: при каждом комментарии вы можете найти его родительский комментарий в словаре, а затем сохранить обработанный в данный момент комментарий также в этом словаре.

5 голосов
/ 02 марта 2009

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

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

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

Я искал модуль 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" с комментарием_ид "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';

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

3 голосов
/ 28 февраля 2009

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

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

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

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

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

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

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

. / Alex

3 голосов
/ 28 февраля 2009

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

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

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

...