Запросы связей между видео с помощью Apache AGE на Postgres
Реляционные базы данных с трудом справляются с глубокими связями.
В TrendVidStream мы управляем каталогом стриминговых проектов в восьми регионах. Изначально мы использовали SQLite с FTS5. Это хорошо подходило для полнотекстового поиска и простых развертываний.
Проблемы начались, когда пользователи стали задавать сложные вопросы. Они хотели знать: «Что еще находится на расстоянии двух или трех связей от этого триллера с этим конкретным режиссером и этими актерами?»
В SQL это требует множества self-join (самосоединений). Каждый дополнительный шаг усложняет поддержку запроса и замедляет его выполнение. Разрастание соединений (join fan-out) превращается в кошмар.
Мы решили эту проблему, добавив графовый слой с помощью расширения Apache AGE для PostgreSQL.
Вот почему такое разделение эффективно:
• Вы сохраняете PostgreSQL. Вы используете те же соединения, транзакции и инструменты резервного копирования. Вам не нужна отдельная база данных, такая как Neo4j. • Вы смешиваете графовые и реляционные данные. Вы можете выполнять запросы Cypher и объединять их результаты со стандартными SQL-таблицами. • Вы легко работаете с переменной глубиной. Поиск связей на расстоянии от одного до трех шагов — это простая команда в Cypher. • Вы сохраняете простоту. Граф является проекцией ваших данных, а не отдельным источником истины.
Наша модель использует четыре метки вершин:
- Video
- Person
- Platform
- Region
Мы используем специфические типы ребер, такие как SIMILAR_TO, WORKED_ON и AVAILABLE_IN.
Один важный урок: всегда индексируйте точки входа. Мы индексируем свойство ext_id у вершин Video. Без этого каждый обход (traversal) начинается с медленного последовательного сканирования.
Результатом является чистый и читаемый запрос для рекомендаций:
SELECT * FROM cypher('discovery', $$
MATCH (seed:Video {ext_id: 'vid_8842'})
MATCH (seed)-[:SIMILAR_TO|WORKED_ON*1..3]-(rec:Video)-[:AVAILABLE_IN]->(:Region {code: 'DE'})
WHERE rec.ext_id <> 'vid_8842'
RETURN DISTINCT rec.ext_id, rec.title, rec.year
LIMIT 24
$$) AS (ext_id agtype, title agtype, year agtype);
Этот запрос находит связанные видео в пределах трех шагов, которые доступны в Германии. В SQL это была бы огромная куча соединений. В Cypher — это простой паттерн.
Мы оставили SQLite для текстового поиска и использовали AGE для связей. Каждый инструмент делает то, что у него получается лучше всего.
