Graph: property graph и RDF-тройки
Мы разобрали три модели NoSQL — документную, key-value, wide-column. Все они, как и реляционная, по-своему хранят сущности и их атрибуты. Четвёртая модель — графовая — устроена принципиально иначе. Её фокус — не сущности, а связи между ними. Там, где остальные модели делают связи второстепенными (foreign key, ссылка, partition key), графовая ставит связь в центр и делает её первоклассным гражданином.
Главная идея урока: графовая модель предназначена для данных, где связи так же важны, как сами данные, а часто и важнее. Социальные сети, рекомендательные системы, графы знаний, обнаружение мошенничества — везде, где главный вопрос «кто с кем и как связан». Мы разберём, как устроена графовая модель, и сравним две её разновидности: property graph (Neo4j) и RDF triple store.
Когда реляционная модель пасует на связях
Начнём с проблемы. Возьмём социальную сеть и вопрос: «друзья друзей моих друзей» — кого порекомендовать в знакомые на третьем уровне связей.
В реляционной модели дружба — это таблица friendships(user_a, user_b). Один уровень связей — один JOIN. «Друзья друзей» — два JOIN. «Друзья друзей друзей» — три JOIN таблицы саму на себя. Каждый уровень умножает объём промежуточного результата, и на глубине в несколько уровней запрос становится неподъёмным. Реляционная модель отлично отвечает на «какие атрибуты у этой сущности», но плохо — на «как глубоко связаны эти две сущности».
Графовая модель проектировалась ровно под второй вопрос. Обход связей в ней — родная, дешёвая операция, не зависящая от глубины так драматично, как цепочка JOIN. Если ваши главные запросы — про пути и связи, графовая модель подходит лучше реляционной.
Property graph: узлы, связи, свойства
Самая распространённая графовая модель — property graph (граф со свойствами), её флагман — Neo4j. Она состоит из двух видов элементов.
Узел (node) — это сущность: пользователь, товар, фильм, компания. У узла есть label (тип — User, Product) и набор свойств в формате key-value (имя, возраст, цена).
Связь (relationship) — это ребро между двумя узлами. И вот ключевое: в property graph связь — полноценный объект. У неё есть свой тип (FRIENDS_WITH, PURCHASED, RATED), направление (от одного узла к другому) и — что важно — собственные свойства. Связь «оценил» может нести свойство rating: 5; связь «дружит с» — свойство since: 2024.
// Property graph: узлы и связи со свойствами
(Анна:User {age: 28}) -[:FRIENDS_WITH {since: 2024}]-> (Борис:User {age: 31})
(Анна:User) -[:RATED {rating: 5}]-> (Фильм:Movie {title: "Дюна"})
(Анна:User) -[:PURCHASED {date: "2026-05"}]-> (Товар:Product {name: "Книга"})
То, что связь имеет свойства, — принципиально. В реляционной модели атрибут связи (рейтинг, дата) пришлось бы хранить в junction-таблице как отдельную строку. В property graph он лежит прямо на ребре, где ему семантически и место.
Запросы к property graph пишут на языке Cypher (в Neo4j). Cypher выразителен именно для связей — «друзья друзей» в нём это короткий и читаемый шаблон пути, а не три вложенных JOIN. Property graph эргономична: модель «узлы и рёбра со свойствами» интуитивна и близка к тому, как человек рисует схему на доске.
RDF triple store: всё есть тройка
Вторая разновидность графовой модели — RDF triple store. RDF (Resource Description Framework) — стандарт консорциума W3C, и устроен он иначе. В RDF нет узлов со свойствами и рёбер со свойствами. Есть тройки (triples), и всё знание — это набор троек.
Тройка — это три элемента: subject — predicate — object (субъект — предикат — объект). Читается как простое предложение:
# RDF: каждый факт — отдельная тройка subject-predicate-object
:Анна :hasAge 28 .
:Анна :friendsWith :Борис .
:Анна :rated :ФильмДюна .
:ФильмДюна :hasTitle "Дюна" .
:ФильмДюна :releasedIn 2021 .
Заметьте разницу. В property graph «возраст Анны» — это свойство внутри узла. В RDF «возраст Анны» — это отдельная тройка Анна — hasAge — 28. В RDF вообще нет понятия «свойство узла»: и атрибут, и связь — одинаково тройки. Анна — hasAge — 28 (атрибут) и Анна — friendsWith — Борис (связь) структурно идентичны. Всё единообразно разложено на мельчайшие факты-тройки.
Ещё особенность RDF: элементы идентифицируются через URI — глобальные идентификаторы, как веб-адреса. Это сделано намеренно: RDF проектировался для обмена данными между организациями и системами. Если каждый факт — стандартная тройка с глобальными идентификаторами, графы из разных источников можно сливать в один. Запросы к RDF пишут на языке SPARQL — стандарте W3C.
Property graph против RDF: что выбрать
Две модели решают похожую задачу по-разному. Сравним.
| Аспект | Property graph (Neo4j) | RDF triple store |
|---|---|---|
| Базовая единица | Узел и связь, у обоих свойства | Тройка subject-predicate-object |
| Атрибут сущности | Свойство внутри узла | Отдельная тройка |
| Свойства на связи | Есть, прямо на ребре | Нет напрямую (связь — это тройка без своих свойств) |
| Идентификаторы | Внутренние | Глобальные URI |
| Язык запросов | Cypher | SPARQL |
| Стандартизация | Зависит от вендора | Стандарт W3C |
| Сильная сторона | Эргономика, близость к ООП-мышлению | Единообразие, обмен и интеграция данных |
Практический выбор. Property graph берут, когда нужно строить приложение с богатой логикой связей — рекомендации, соцграф, fraud detection: модель эргономична, свойства на рёбрах удобны, Cypher выразителен. RDF берут, когда главная задача — интеграция и обмен: графы знаний, объединение данных из многих источников, семантический веб, открытые данные; единообразие троек и глобальные URI делают слияние графов естественным.
Грубое правило различения. Если вы строите продукт, и граф — его внутренняя структура данных, обычно удобнее property graph. Если вы интегрируете и обмениваете данные между организациями, и важна стандартизация, — RDF. Property graph эргономичнее для разработчика; RDF единообразнее и расширяемее для интеграции.
Где графовая модель в общей картине
Подведём место графовой модели. Это не «лучшая» и не «худшая» модель — это модель для определённого класса задач. Как и остальной NoSQL, графовая база — специализированный инструмент.
Графовую модель выбирают, когда связи — первоклассная сущность предметной области и главные запросы идут по путям: «как связаны A и B», «найди сообщества», «обойди иерархию любой глубины», «есть ли подозрительная цепочка переводов». Если же ваши запросы — это в основном «дай атрибуты сущности» и агрегации, графовая база избыточна: реляционная или документная подойдут лучше и проще.
Это завершает обзор NoSQL-моделей. Документная, key-value, wide-column, графовая — четыре разных ответа на четыре разных класса задач. Объединяет их одно: ни одна не «лучше» реляционной, каждая делает осознанный компромисс ради своей сильной стороны. В финальном уроке модуля мы соберём это в общий принцип — как фундаментальный выбор хранилища (через теорему CAP и модель консистентности) диктует модель данных.
Попробуй сам
Возьмите рекомендательную систему фильмов: пользователи, фильмы, оценки, актёры, жанры.
- Спроектируйте фрагмент как property graph: какие узлы (с label и свойствами), какие связи (с типом и свойствами)? Куда положите рейтинг оценки — на узел или на связь?
- Запишите те же 4-5 фактов как набор RDF-троек subject-predicate-object. Сравните: сколько троек ушло на то, что в property graph было одним узлом со свойствами?
- Сформулируйте запрос «фильмы, которые понравились друзьям моих друзей». Объясните, почему такой запрос тяжёл для реляционной модели и естественен для графовой.
- Решите, что вы выбрали бы для этой системы — property graph или RDF — и обоснуйте через различие «продукт против интеграции».