/Comment-tree

Comment tree

Primary LanguagePython

Древовидные комментарии

Структура реляционных таблиц представляет из себя простые списки. Комментарии должны храниться в базе данных в виде дерева: «родитель-наследники», что не реализовано в реляционной структуре. Существуют подходы в организации хранения деревьев в реляционных БД. В ходе решения задачи были рассмотрены подходы, плюсы и минусы которых опишу ниже.

Список смежных вершин (Adjacency List)

Не совсем удобен для чтения — и это его основной недостаток. Чтение дерева большой вложенности - большая рекурсия. В дерево легко вносить изменения, менять местами и удалять узлы.

Вывод — данный алгоритм хорошо применим, если вы оперируете с небольшими древовидными структурами, которые часто поддаются изменениям.

С другой стороны, этот алгоритм также довольно уверенно себя чувствует и с большими деревьями, если считывать их порциями вида «знаю родителя — прочитать всех наследников». Хороший пример такого случая — динамически подгружаемые деревья. В этом случае алгоритм практически оптимизирован для такого поведения.

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

Вложенное множество (Nested Set)

Очень хорош, когда требуется часто и много обращаться к иерархическим данным на чтение. Значительные трудности начинаются когда нам необходимо внести изменения в Nested Set дерево или удалить какую-либо из его ветвей. Для этого необходимо пересчитать все ключи той части дерева, которая находится справа от изменяемого узла, а также обновить информацию об уровнях вложенности. И все это не сделать одним простым запросом.

Вывод — Nested Set действительно хорош, когда нам необходимо считывать структуру деревьев из БД. При этом он одинаково хорош для деревьев любого объема.

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

Материализованный путь (Materialized Path)

По сравнению с Nested Set, он более поддается изменениям. В то же время остается достаточно удобным для выборки деревьев целиком и их частей. Но, и он не идеален. Особенно по части поиска предков ветки. Использование именно этого алгоритма может быть заметно удобнее, для деревьев, над которыми часто выполняются как операции чтения, так и изменения. Удаление, добавление в конец или изменение узла — это операции довольно простые, и, как правило, не вызывают сложностей в данной модели.

Мой выбор

Для нашей задачи необходмо как чтение дерева целиком, так и частая вставка. В связи с этим было решено выбрать подход - Materialized Path. Для устранения проблемы поиска предков скомбинируем данный подход с Adjacency List добавив поле parent_id.

Технологии

Раньше не писал на Python 3.5 с Asyncio - решил попрбывать.
Также использовал aiohttp.
База данных - PosgreSQL 9.5 и расширение ltree с помошью которого и был реализован подход - Materialized Path.

API

Комментарии

Method URL Desc
GET /comment/{comment_id} Выбока комментария
PUT /comment/{comment_id} Обновление комментария
DELETE /comment/{comment_id} Удаление комментрия, у которого нет дочерних
POST /comment/ Вставка комментария
GET /comment/history/{comment_id}/ Выборка истории комментрия
GET /comments/{user_id}/ Выборка комментариев пользователя

Поля:

  • user_id - id пользователя
  • parent_id - id родительского комментария
  • entity_type - тип сущности
  • entity_id - id сущности
  • text - текст комментария

Пока не валидирую entity_type, потому что не знаю какие могут быть значениея. Можно было просто добвить список типов, но пока отказался.

Уведомления о добавлении/изменении/удалении комментария

WEBSOCKET: /comment/ws/{entity_type}/{entity_id}/

Дерево комментариев

Method URL Desc
GET /tree/{comment_id}/ Получения дерева по id коментрия
GET /tree/{entity_type}/{entity_id}/ Получения дерева по сущности
Получение комментариев первого уровня для определенной сущности с пагинацией

GET: /comments/{entity_type}/{entity_id}/?page={num}

Экспорт комментариев в файл

Экспорт было решено разделить на два этапа:

  1. запрашивается создание выгрузки в файл, возвращается uuid заявки
  2. по uuid заявки проверяется состояние и в случае успеха возвращается файл
Method URL Desc
GET /export/request/{user_id}/ создание заявки на экспорт по пользователю
GET /export/request/{entity_type}/{entity_id}/ создание заявки на экспорт по сущности
GET /export/file/{req_id}/ проверка состояния заявки(экспрота)

Установка

./install.sh

Тесты

Пока их мало.

pytest tests