Графы знаний
SPARQL и Cypher
Wikidata знает, что Достоевский родился в Москве, учился в Петербурге, написал «Братьев Карамазовых» в 1880 году. Как задать вопрос «все русские писатели XIX века, родившиеся в Москве»? SQL не подходит - данные хранятся в 100 миллионах троек. SPARQL и Cypher - языки, созданные именно для таких запросов.
- **Wikidata Query Service** - миллиарды запросов в день на SPARQL к крупнейшей открытой базе знаний
- **Neo4j** используется в LinkedIn (граф связей), eBay (рекомендации), NASA (управление данными миссий)
- **Фармацевтика:** поиск путей взаимодействия лекарств через граф белок-белок взаимодействий
SPARQL: SELECT, WHERE, FILTER
Граф знаний Wikidata содержит 100 миллионов утверждений в виде троек (субъект, предикат, объект). Как спросить «все города с населением больше миллиона»? SQL тут не поможет - данные хранятся как RDF-триплы, и для них придумали **SPARQL** (SPARQL Protocol and RDF Query Language).
Структура запроса SPARQL похожа на SQL, но переменные начинаются с `?`, а паттерны матчатся по триплам. `SELECT` выбирает переменные, `WHERE` задаёт граф-паттерн, `FILTER` добавляет условия на значения.
Каждая строка в `WHERE` - это граф-паттерн (тройка). Переменные `?city`, `?population` будут подставлены из реального графа. `wdt:P31` и `wd:Q515` - это URI-идентификаторы из онтологии Wikidata. Несколько паттернов соединяются по AND: все условия должны выполняться одновременно.
**OPTIONAL** в SPARQL - аналог LEFT JOIN: `OPTIONAL { ?book schema:isbn ?isbn }` вернёт книги без ISBN тоже, просто с пустым значением. Без OPTIONAL паттерн обязателен.
В SPARQL-запросе строка `?person wdt:P19 wd:Q649 .` означает:
Cypher: MATCH, CREATE, WITH
Neo4j хранит граф как узлы и рёбра с метками. Для него создали **Cypher** - язык запросов с визуальным синтаксисом: паттерны пишутся как ASCII-рисунки графа. `(node)` - узел, `-[rel]->` - направленное ребро.
Паттерн `(actor:Person)-[:ACTED_IN]->(movie:Movie)` читается буквально: найди узел с меткой Person, соединённый ребром ACTED_IN с узлом Movie. Фигурные скобки задают фильтр свойств прямо в паттерне. Это намного читабельнее SQL JOIN-ов для графовых данных.
`WITH` - ключевое слово Cypher для конвейерной обработки: агрегирует или трансформирует данные и передаёт их в следующую часть запроса. Без `WITH` нельзя применить WHERE после агрегации.
**SPARQL vs Cypher:** SPARQL - стандарт W3C для RDF, работает с Wikidata, DBpedia. Cypher - проприетарный язык Neo4j, теперь открытый как openCypher. Семантика похожа, но синтаксис и модели данных разные.
Что делает паттерн `(a)-[:KNOWS]->(b)-[:KNOWS]->(c)` в Cypher-запросе?
Агрегация в SPARQL и Cypher
Граф знаний редко нужен для получения одной тройки - обычно нужна статистика: «сколько фильмов снял режиссёр», «среднее население городов по стране». Оба языка поддерживают агрегатные функции: `COUNT`, `SUM`, `AVG`, `MIN`, `MAX`.
Cypher имеет полезную функцию `collect()` - она собирает все значения в список. Это позволяет за один запрос получить и агрегат, и примеры данных. В SPARQL аналог - `GROUP_CONCAT(?value; separator=",")`.
В SPARQL агрегация через `GROUP BY` + `HAVING` - как в SQL. В Cypher агрегатные функции в `RETURN` или `WITH` автоматически группируют по остальным переменным - без явного `GROUP BY`.
В Cypher-запросе `RETURN actor.name, count(movie)` - по чему происходит группировка?
Кратчайшие пути и traversal
Граф знаний LinkedIn содержит связи «работал в одной компании», «учился в одном университете». Вопрос «как я связан с Тимом Куком через коллег?» - это задача поиска пути в графе. Именно здесь графовые языки запросов показывают превосходство над реляционными.
Синтаксис `[:KNOWS*]` - переменная длина пути. `*1..3` означает от 1 до 3 рёбер. `shortestPath()` - встроенная функция Cypher для поиска кратчайшего пути по BFS. Для взвешенных путей используется `apoc.algo.dijkstra` из библиотеки APOC.
SPARQL 1.1 Property Paths позволяют писать регулярные выражения над рёбрами: `*` (ноль и более), `+` (один и более), `/` (последовательность), `|` (альтернатива). Один оператор заменяет рекурсивные подзапросы при обходе иерархий.
**Производительность:** путевые запросы могут быть дорогими. Cypher `shortestPath` использует BFS и эффективен. SPARQL `*` на глубоком графе может вернуть миллионы троек - обязателен `LIMIT`.
Что означает `wdt:P279*` в SPARQL Property Path?
SPARQL и Cypher
- SPARQL: стандарт W3C для RDF-графов. SELECT + граф-паттерны в WHERE + FILTER + GROUP BY/HAVING
- Cypher: визуальный синтаксис Neo4j - (node)-[:EDGE]->(node). MATCH ищет, CREATE создаёт, WITH передаёт в конвейер
- Агрегация: COUNT, SUM, AVG работают в обоих. В Cypher GROUP BY неявный - по всем неагрегированным полям
- Пути: Cypher shortestPath() и [:REL*1..N]. SPARQL Property Paths с квантификаторами *, +, /
Связанные темы
SPARQL и Cypher - языки запросов поверх конкретных графовых хранилищ:
- RDF и триплсторы — Модель данных, над которой работает SPARQL
- Онтологии и OWL — Словари предикатов и классов, используемые в SPARQL-запросах
- Graph Neural Networks — ML поверх тех же графов, которые запрашиваются через Cypher/SPARQL
Вопросы для размышления
- В каких сценариях SPARQL Property Paths предпочтительнее рекурсивных CTE в SQL для обхода иерархий?
- Почему Cypher не требует явного GROUP BY, и какие ошибки это может спровоцировать у новичков?
- Как бы вы реализовали запрос «найти людей, связанных с Алисой через не более трёх рукопожатий» в обоих языках?