В DailyWatch наша панель «похожие видео» начиналась со скромного запроса к SQLite. Он объединял три таблицы, подсчитывал пересекающиеся теги и возвращал ранжированный список. Для небольшого каталога этого было достаточно. Кулинарное видео с тегами «italian» и «pasta» выводило другие ролики с такими же тегами, и пользователи кликали по ним. Результаты казались релевантными, так как метаданные были чистыми, а библиотека — небольшой.

Затем каталог вырос, и ожидания аудитории изменились. Людям не просто нужны были видео с идентичными тегами. Они хотели тот ролик, который сорок процентов зрителей смотрели сразу после текущего. Им нужен был нишевый канал, который постоянно всплывал в тех же ночных сессиях просмотра, даже если в его описании не было ни слова о начальном видео, а теги были скудными. Это поведенческие паттерны, а не совпадения по метаданным. SQLite не мог выразить их, не превращаясь в нечитаемое нагромождение self-join и рекурсивных обобщенных табличных выражений (CTE). Каждый новый сигнал, который мы пытались смоделировать — будь то совместный просмотр или смежность сессий — увеличивал задержку и когнитивную нагрузку. У нас была задача на графы, зажатая в реляционные рамки.

Я перенес слой рекомендаций на Apache AGE.

Apache AGE — это расширение PostgreSQL, которое добавляет графовые запросы openCypher в базу данных, которую вы уже используете. Это не отдельный сервер. Это не sidecar. Он работает внутри Postgres, а это значит, что мы могли построить сеть совместных просмотров, не разворачивая выделенный кластер Neo4j и не изучая совершенно новый регламент эксплуатации. Для небольшой команды без инженера по надежности баз данных это различие имело огромное значение.

Почему расширение лучше новой базы данных

Добавление графовой базы данных в ваш стек легко нарисовать на доске, но дорого поддерживать в продакшене. Вам понадобятся новые дашборды мониторинга, новые процедуры резервного копирования, новые пулы соединений и новая логика отказоустойчивости. AGE позволяет обойти всё это, так как работает внутри вашего существующего экземпляра Postgres.

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

  • Никакой новой инфраструктуры. Поскольку AGE является расширением, ваш текущий график pg_dump, существующие реплики и стандартные проверки состояния (health checks) Postgres продолжают работать. Вам не нужно убеждать команду эксплуатации присматривать за еще одним хранилищем данных.
  • Смешанные нагрузки в одном запросе. AGE позволяет писать Cypher внутри SQL. Вы можете выполнить обход графа для поиска подходящих видео, а затем объединить (join) полученный набор результатов с вашей реляционной таблицей users для соблюдения региональных ограничений на контент или с реляционной таблицей sponsorships, чтобы понизить приоритет определенных каналов. Один запрос (round-trip). Два языка запросов, работающих сообща.
  • Портативность. Cypher — это открытый, хорошо документированный язык запросов к графам. Если DailyWatch перерастет AGE и позже потребуется миграция на Neo4j или Memgraph, логика запросов перенесется с минимальными переработками. Вы не привязаны к проприетарному диалекту.
  • Совместимость. Поскольку данные в конечном итоге хранятся в PostgreSQL, ваши существующие инструменты на PHP или Python не меняются. Вы подключаетесь