Вопрос проверяет умение проектировать структуру данных и алгоритмы для хранения и отображения древовидных комментариев, что важно для масштабируемых приложений.
Основная задача — организовать данные так, чтобы можно было эффективно получать все дочерние комментарии для любого родителя, а также поддерживать операции вставки, удаления и перемещения узлов. Рассмотрим три популярных подхода.
Самая простая модель: каждая запись содержит ссылку на родителя через поле parent_id. Корневые комментарии имеют parent_id = NULL.
CREATE TABLE comments (
id SERIAL PRIMARY KEY,
content TEXT NOT NULL,
parent_id INTEGER REFERENCES comments(id),
created_at TIMESTAMP DEFAULT NOW()
);Для получения всего дерева используется рекурсивный CTE:
WITH RECURSIVE comment_tree AS (
SELECT id, content, parent_id, 1 AS depth
FROM comments
WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.content, c.parent_id, ct.depth + 1
FROM comments c
JOIN comment_tree ct ON c.parent_id = ct.id
)
SELECT * FROM comment_tree ORDER BY depth, id;Плюсы: простота, легкость вставки и удаления. Минусы: для глубоких деревьев рекурсивные запросы могут быть медленными.
Каждый комментарий хранит полный путь от корня в виде строки, например 1/5/12. Это позволяет получать всех потомков простым LIKE-запросом.
CREATE TABLE comments (
id SERIAL PRIMARY KEY,
content TEXT,
path VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
-- Получить всех потомков комментария с id=5
SELECT * FROM comments WHERE path LIKE '1/5/%';Плюсы: быстрое чтение дерева, не требует рекурсии. Минусы: сложность при перемещении узлов (нужно обновлять пути у всех потомков), ограничение на глубину из-за длины строки.
Каждый узел имеет два числа: lft и rgt, которые определяют его положение в дереве. Все потомки узла находятся между его lft и rgt.
CREATE TABLE comments (
id SERIAL PRIMARY KEY,
content TEXT,
lft INTEGER NOT NULL,
rgt INTEGER NOT NULL
);
-- Получить всех потомков узла с lft=2 и rgt=11
SELECT * FROM comments WHERE lft BETWEEN 2 AND 11;Плюсы: очень быстрое чтение поддеревьев. Минусы: сложная вставка/удаление (требуется перенумерация многих узлов), неудобно для частых изменений.
Для большинства современных приложений с частыми вставками (например, социальные сети) лучше всего подходит adjacency list + рекурсивные CTE. Если дерево редко меняется, но часто читается — можно использовать materialized path или nested sets. Для очень глубоких деревьев (более 100 уровней) стоит рассмотреть closure table — отдельную таблицу, хранящую все пары предок-потомок.
При проектировании хранения комментариев с вложенностью важно учитывать баланс между операциями чтения и записи. Для большинства случаев оптимальным является adjacency list с рекурсивными запросами, так как он прост в реализации и поддерживает эффективные изменения. Если требуется максимальная производительность чтения, выбирайте materialized path или nested sets.
Уровень
Рейтинг:
4
Сложность:
6
Навыки
JavaScript
SQL
Ключевые слова
Подпишись на Python Developer в телеграм