Neo4j. Графовая СУБД для RAG и не только
Графовые СУБД, пожалуй, одни из самых специализированных хранилищ, существующих на корпоративном рынке. Neo4j при этом яркий представитель этой категории.
Немного о графовых БД
C Neo4j я познакомился ещё в далеком 2018-м году, в рамках задачи создания более приятной системы корпоративных знаний чем классические Wiki (некий такой корпоративный Obsidian), ну или основные его части. Это сейчас вы можете радоваться всем благам цивилизации, а в то далёкое время нам надо было очень внимательно относиться к структуре корпоративной базы знаний, т.к. даже поисковые алгоритмы часто оставляли желать лучшего. Никакого вам ранжирования статей в выдаче по просмотрам и времени создания.
Но вцелом с точки зрения базы знаний даже текущие варианты Wiki с ранжированием статей, отображением связанных, последних просмотренных и т.п. всё равно не решает вопрос оперативного поиска информации. А вот граф — уже другая история. Использовали Obsidian? Понравилось представление информации о связанных заметках? Особенно если качественно проставлять связи. Собственно, именно таким образом мы обычно и оперируем информацией. Табличная модель, конечно, удобна, но несколько более синтетическая история, которую придумали, чтобы упростить себе жизнь. Оперировать графами технически всё-таки куда сложнее.

В общем случае вам нужна графовая БД если:
Ваши данные не очень хорошо структурированы
Вам нужны запросы поиска множества связей разных сущностей (первый признак если вы спроектировали реляционную БД и запросы к ней и в запросах 3 и более соединений, включая соединения таблицы с самой собой)
У вас появляются запросы вида: «выбери все в группе», «выбери все в иерархии»
У вас могут быть запросы вида: «все связанные объекты до 5-го колена»
Ваши данные имеют древовидную или похожую на неё структуру изначально
Вам нужно представление ваших данных в виде графа
Есть также антипаттерны графовых БД:
У вас очень много запросов с фильтром сущностей по нескольким полям\реквизитам
Вы редко выбираете данные по связам между сущностями
У вас много запросов аггрегации данных надо считать суммы по сущностям и подобное
У вас есть жесткие требования по соблюдению структуры данных
Графовые БД и RAG
Понятие »Knowledge Graph» существует уже достаточно давно. Но с появлением LLM оно обрело новую жизнь и стало поистине незаменимой фичей. Стоит наверное начать с того, что используя LLM Neo4j может создавать Графы знаний автоматически. Т.е. вы указываете источники, которые надо проанализировать, а на выходе получаете готовую БД. Ну и естественно в БД есть визуализация вроде:

Сам GraphBuilder можете потестировать тут: https://llm-graph-builder.neo4jlabs.com/
Обратное, конечно, тоже возможно. И в отличие от SQL диалектов, диалог с Neo4j путём генерации Cypher запросов уже вполне себе налажен и неплохо работает: https://neo4j.com/labs/genai-ecosystem/neoconverse/
Очень часто в RAG системах пытаются обойтись только стандартными векторными БД что вцелом правильно, но есть некоторые нюансы:
Если просто разложить корпоративные знания в вектор иногда на выходе получается «корпоративный Google», а RAG это как бы не совсем про поисковик
Получить ответ на основе только похожих слов и сочетаний может быть не всегда лучшим решением, иногда надо затрагивать смежные области знаний
Также иногда ответы требуют большей аккуратности в работе с данными, ошибки могут «дорого» стоить
Прямые запросы к данным БД тоже часто бывают нужны, ну и может потребоваться редактирование информации, может даже ручное вмешательство. С векторными данными это достаточно проблематично
Всё это подводит к мысли, что графовые БД становятся более актуальными во времена тотального засилья LLM. Вцелом любые запросы вида «найти связи» очень хорошо ложатся на графовую БД. К таким данными кроме Графа знаний из практического опыта можно отнести, к примеру: социальные сети, Blockchain аналитику, Анализ конкурентов.
Neo4j. Общая информация.
Neo4j, наверное, можно назвать старейшей графовой СУБД ну или одной из таковых. Поэтому конечно есть ряд издержек, которые можно отнести к недостаткам:
Java. Соответственно оверхед, возня с JVM и прочие прелести, но тут Neo4j не одинока конечно.
Классическая транзакционность. Кому то покажется достоинством. Но как по мне, всё-таки банковские транзакции хранить в графе вряд ли кто-то собрался и соблюдать полноценный ACID вряд ли есть необходимость. При этом весь оверхед от него мы несём
Как следствие предыдущего пункта, есть определенные трудности с шардингом и масштабированием.
Enterprise редакция. В большинстве случаев нужна и весьма не дешева. Конечно более лояльная и менее жадная модель лицензирования была бы поприятнее
Ну и приятные достоинства, которые подкупают меня использовать эту СУБД, несмотря на всё, описанное выше:
Cypher — свой язык графовых запросов. Наверное на текущий момент времени можно считать что он «победил» среди языков запросов для графов. В любом случае, это в разы лучше, чем пытаться прикрутить к графу SQL или сделать какой-нибудь программный API интерфейс для популярных языков программирования.
Множество инструментария. Это может малозаметно на этапе выбора СУБД, но сильно недооцененный фактор:
Инструментарий менеджмента сеансов, транзакций, индексов
Инструментарий проверки БД
Инструменты анализа производительности запросов
Инструментарий загрузки, репликации резервного копирования БД
Инструментарий мониторинга (только в Enterprise)
Возможность создания расширения и куча готовых расширений во встроенной библиотеке. Что конечно очень удобно
Приятный интерфейс «из коробки» позволяющий решать базовые аналитические задачи.
Собственное облако Aura
Всё это делает neo4j неким таким PostgreSQL в мире графов.
Neo4j. Установить и попробовать.
Самый простой вариант взять просто Neo4j Aura триалку и попробовать. Но я эту статью пишу на хабре, так что это не наш вариант.
Далее есть опция https://neo4j.com/download/neo4j-desktop/
Neo4j Desktop. Специально разработанная песочница превращает Neo4j в некое подобие SQLite с красивым интерфейсом. Для того? чтобы написать пару Cypher запросов на демо базе подойдёт, но всё таки для более менее нормального использования возможностей мало подходит
Так что всё по классике — более мене нормальная инсталляция для изучения — dockerhub https://hub.docker.com/_/neo4j
Ну, а для полноценной инсталляции есть и пакеты под Debian\Ubuntu и под что-нибудь RPM based.
С 2025 года Neo4j ещё изменили нумерацию версий.
Текущие версии это:
4.4 — стабильная история, достаточно давно существующая.
Версия 5.x — стабильной версии чуть больше года.
2025 — по сути тоже 5.x редакция, но всё таки лучше использовать LTS. СУБД это про данные, а данные надо хранить аккуратно
Neo4j. Основные настройки.
Вцелом всё достаточно просто, основные настройки про которые надо не забывать:
dbms.memory.heap.initial_size=...
dbms.memory.heap.max_size=...
dbms.memory.pagecache.size=...
Как и в PostgreSQL настройки памяти — основное что должно конфигурироваться под сервер. И также для этого, к счастью, есть команда утилиты администрирования:
neo4j-admin server memory-recommendation
Ещё по классике как и в PostgreSQL надо включить возможность удаленных соединений (если нужно) и также включить BOLT (нативный протокол, как бы это не звучало) и при необходимости включить секъюрность. Т.е. вот эти параметры:
dbms.connectors.default_listen_address=0.0.0.0
dbms.connector.bolt.type=BOLT
dbms.connector.bolt.enabled=true
dbms.connector.bolt.tls_level=OPTIONAL
dbms.connector.bolt.address=0.0.0.0:7687
dbms.ssl.policy.bolt.base_directory=certificates/bolt
dbms.ssl.policy.bolt.private_key=private.key
dbms.ssl.policy.bolt.public_certificate=public.crt
Собственно, назначение понятно — адрес, порт, ключи шифрования. Включение дефолтного протокола.
Ещё несколько настроек, которые менее очевидны, но по опыту необходимы, по крайней мере на начальном этапе:
dbms.security.procedures.unrestricted=*
dbms.security.procedures.allowlist=*
dbms.security.allow_csv_import_from_file_urls=true
apoc.export.file.enabled=true
apoc.import.file.enabled=true
Этим мы разрешаем использование внешних библиотек (всех. На Production желательно конечно ограничить только apoc.* и/или gds.*). Также разрешаем импорт и экспорт из файлов. Скорее всего его будете делать средствами apoc (настоятельно рекомендую).
APOC надо скачать отсюда https://github.com/neo4j/apoc и разместить в домашнем каталоге neo4j в директории plugins (если её нет — создать, если переименовали — в директории которая указана в конфиге). В завершение надо ещё создать нового пользователя ну или хотя бы сменить пароль дефолтному:
neo4j-admin set-initial-password your_new_password
или уже в cypher:
ALTER USER neo4j SET PASSWORD 'new_password';
CREATE USER and GRANT ROLE тоже работают и в принципе как в классическом SQL.
Следующее, что может потребоваться — это загрузить начальные данные.
Делать это лучше через neo4j-admin import — скорость загрузки в десятки раз быстрее. Потому что транзакционный режим не поддерживается.
Надо чуть разобраться с редакциями:
Neo4j. Community и Enterprise
Community лицензия не самая приятная — GPLv3.
Так или иначе, для определенных кейсов вполне подходит. Для разработки тоже.
Production ограничения, конечно, более существенные полный перечень тут https://neo4j.com/pricing
Если коротко наиболее существенные это:
Ролевая модель доступа. Её нет.
4 процессора (не ядра!) На поверку кажется это ограничение не работает
34 миллиарда нод на граф. Достичь этого ограничения по факту очень трудно. Объём такой БД будет более 10 ТБ как минимум. Возможно это ограничение тоже не реализовано технически.
Средства мониторинга. Частично я эту историю исправлял https://github.com/comol/neo4jexporter добавляя также проверки качества данных
Кластеризация. Можно тоже сделать ручками через neo4j streams.
Итак, в чем сила Neo4j
Cypher
Давайте разберём пример из документации. Кстати, классический. Если видели 1С, то номенклатура в ней обычно расположена в иерархии соответственно чтобы получить иерархию всех «папок» нужен весьма себе нетривиальный запрос (который 1С, конечно, упрощает) в SQL он выглядит как то так:
SELECT p.ProductName
FROM Product AS p
JOIN ProductCategory pc ON (p.CategoryID = pc.CategoryID AND pc.CategoryName = "Dairy Products")
JOIN ProductCategory pc1 ON (p.CategoryID = pc1.CategoryID)
JOIN ProductCategory pc2 ON (pc2.ParentID = pc2.CategoryID AND pc2.CategoryName = "Dairy Products")
JOIN ProductCategory pc3 ON (p.CategoryID = pc3.CategoryID)
JOIN ProductCategory pc4 ON (pc3.ParentID = pc4.CategoryID)
JOIN ProductCategory pc5 ON (pc4.ParentID = pc5.CategoryID AND pc5.CategoryName = "Dairy Products")
;
А вот так, для сравнения, он выглядит в Cypher:
MATCH (p:Product)-[:PART_OF]->(l:Category)-[:PARENT*0..]-(:Category {name:'Dairy Products'})
RETURN p.name;
Как вы наверное догадались вся «магия» находится тут: [: PARENT*0…]
По сути это отбор всех нод по связям с меткой «PARENT», но вот от 0-го уровня до любого который будет найден. Конечно можно ограничить число уровней поиска, но в данном случае без ограничений. Но не только простота записи и краткость подкупает. Сама запись в разы понятнее (для подобных кейсов).
Ещё один «мощный» запрос:
MATCH(p:Product {name: 'pen'})-[]-(a) RETURN a;
Собственно говоря, данным запросом мы получаем вообще всё с чем связан (напрямую одной связью) продукт 'pen'. И да, вы правильно поняли, простым добавление *0… данный запрос можно заставить получить не прямые связи.
Весь туториал по Cypher я здесь пожалуй приводить не собрался, но думаю общий смысл вы уловили. Язык ориентирован именно на графы. В круглых скобках ноды, в квадратных — связи. Это достаточно удобно, а если быть более точным — очень удобно, в случае если ваши данные имеют графовую структуру.
При этом от SQL на самом деле данный язык отошел не сильно далеко. Работают всё те же самые WHERE, GROUP BY, ORDER BY, SUM, AVG, UNION и много что ещё. Но злоупотреблять этим не стоит. В теории можно использовать ноды с определенной меткой как «таблицы», но если вы к нодам и их свойствам пишете запросы как к таблицам и колонкам, значит Neo4j был не самым правильным выбором.
Вся сила Neo4j в очень быстрых «джойнах». Выбрать связанные ноды — очень простая, быстрая и дешевая операция. Существенно быстрее чем джойн таблиц с фильтром. Т.е. если у вас в реляционной БД куча джойнов в запросах с фильтром по одной записи по ключу — надо задуматься, а не графовая ли у вас структура…
Гибкость структуры
С одной стороны в neo4j у каждой ноды может быть свой набор свойств, с другой — по ним можно делать индексы, выборки, а самое главное соединения. По сути язык запросов ничем не уступает SQL по своей мощности, просто несколько адаптирован под графы. Мало какая докуметориентированная БД может предоставить такие возможности, а если говорить про соединения то уже точно никакая
Графический клиент
Кажется что в современном мире есть такие штуки вроде dBeaver и DataGrip это не так важно. Но для графовых СУБД это не совсем так. Оцените ещё раз:

Собственно, таких возможностей по отображению, интерактивным фильтрам и даже редактированию графов ни один универсальный клиент не предоставит.
Классический Browser для написания Cypher запросов и отображения их результата (включая, кстати, визуализацию даже плана выполнения). От Bloom он несколько отличается:

Внешние библиотеки
Не уверен что в данном случае это сильная сторона именно Neo4j. Возможности расширения тут достаточно хорошие, но относительно того же ClickHouse — существенно меньше, сложнее и не так удобно. Да и PostgreSQL, к примеру, тоже может похвастаться достаточно внушительным набором расширений.
Но если сравнивать с другими графовыми БД, то практически нигде таких возможностей нет.
Тем не менее, поверьте на слово. Скорее всего сколь-нибудь внушительный проект с использованием Neo4j без использования библиотеки APOC не обойдётся.
Документация по ней тут: https://neo4j.com/labs/apoc/5/
Советую обязательно обратить внимание на функцию: apoc.periodic.iterate.
Без неё массовая модификация данных в БД практически невозможна. А на начальных этапах она часто очень требуется.
Также очень нужные и полезные функции:
apoc.load.csv и apoc.export.csv — штатными функциями Cypher работать со сколь-нибудь значимыми объёмами внешних данных вряд ли получится.
Библиотека поистине очень обширная. Но остальные функции имеет смысл смотреть уже от задач. Будь то расширенные математические вычисления или интеграция с какими-либо СУБД и ещё множество разных задач. В любом случае, знать об этой библиотеке точно нужно, если использовать Neo4j.
Немного о грустном. Neo4j известные проблемы.
1) Community версия
Neo4j не совсем классический OpenSource конечно. Community версия имеет некоторые вышеперечисленные ограничения. Относительно популярных OpenSource СУБД эти ограничения весьма существенные
2) Составные индексы
Классический паттерн реляционных БД — вам нужно использовать индекс при соединении. В Neo4j нет соединений в общем смысле этого слова. Составные индексы есть — их можно создать для ноды, но вот если хочется выбирать и ноду с фильтром по свойству и связи по ней, то это может вызвать проблемы на больших объёмах данных.
3) Аггрегация
Данная операция в Neo4j вообще хуже некуда. Выполняется строго в памяти и только при считывании всех данных. В промежуточных результатах получается ещё хуже. Вообщем если у вас много запросов аггрегации — это не к Neo4j.
4) Connection Pooler
Не отличается стабильностью работы. Откровенно говоря, если у вас реальный HighLoad по запросам, то надо придумывать что то своё в промежутке между бэкендом и БД. Ну по крайней мере это мой опыт.
5) Транзакционность
Кроме того что она есть и её нобходимость в графовой БД достаточно сомнительно, мы мало чем можем в ней управлять, особенно при массовых операциях. Кажется что если у нас есть нода то её изменение должно быть атомарным с точки зрения блокировки транзакцией, но на поверку это не совсем так. Более того, в отличие от реляционных СУБД, в Neo4j не всегда очевидно откуда взялась та или иная блокировка и по ним меньше информации. В итоге, массовые операции выполняемые параллельно или при работе пользователей, могут превратится в настоящий ад.
6) Гибкая структура
Это конечно очень хорошо в общем случае, но напрочь лишает защиты. Констрейнты можно создавать только в Enterprise версии и то они не всегда спасут от того что ноду можно внезапно сделать в 10 раз больше и напрочь убить эффективность некоторых запросов. В классических NoSQL изменение структуры мало влияет на эффективность запросов и они обычно куда более простые, а в реляционных изменения структуры существенно сложнее и как правило более продуманные. Так что гибкость тут скорее во вред, чем для пользы.
Заключение
На этом у меня всё. Надеюсь статья помогла познакомитсья с основными достоинствами и паттернами использования графовых БД, и Neo4j в частности. А также позволила определиться с чего начать и какие есть «подводные камни». Конечно у меня есть ТГ канал, если подобный контент кажется интересным — подписывайтесь: https://t.me/comol_it_does_matter
Habrahabr.ru прочитано 6139 раз