Глава 14. От SQL к Cypher

Это руководство для тех людей, которые понимают SQL. Вы можете использовать эти знания, чтобы быстро освоить Cypher и начать исследовать Neo4j.

14.1 START

SQL начинает с результата, который вы хотите получить  — мы ВЫБИРАЕМ (SELECT) то, что нам нужно, а затем описываем источники данных. В Cypher, предложение START имеет отличную концепцию, которая определяет стартовые точки в графе, откуда должен выполняться запрос.

С точки зрения SQL, идентификаторы в START подобны именам таблиц, которые указывают на набор узлов или связей. Набор может быть перечислен литрально, передан через параметры или, как я показываю в следующем примере, быть определен индексным поиском.

Фактически предложение START больше похоже не на SELECT, а на что-то среднее между предложениями FROM и WHERE в SQL.

SQL-запрос

SELECT *
FROM "Person"
WHERE name = 'Anakin'
NAME IDAGEHAIR
Anakin1 20 blonde

Cypher-запрос.

START person=node:Person(name = 'Anakin')
RETURN person
person
Node[0]{name:"Anakin",id:1,age:20,hair:"blonde"}

Cypher позволяет определить несколько стартовых точек. Это не должно показаться необычным с точки зрения SQL — каждая таблица в предложении FROM представляет собой еще одну стартовую точку.

14.2 MATCH

В отличие от SQL, который оперирует множествами, Cypher в основном работает с подграфами. Реляционным эквивалентом является текущее множество кортежей, оцениваемых во время запроса SELECT.

Форма подграфа специфицируется в предложении MATCH. Предложение MATCH аналогично JOIN в SQL. Обычная связь a→b есть внутреннее соединение (inner join) между узлами a и b  — обе стороны должны иметь, по меньшей мере, одно совпадение, в противном случае ничего не возвращается.

Мы начнём с простого примера, в котором вы найдём все адреса электронной почты, которые связаны с лицом “Anakin”. Это обычная связь один-ко-многим.

SQL-запрос

SELECT "Email".*
FROM "Person"
JOIN "Email" ON "Person".id = "Email".person_id
WHERE "Person".name = 'Anakin'
ADDRESSCOMMENTPERSON_ID
anakin@example.comhome1
anakin@example.orgwork1

Cypher-запрос

START person=node:Person(name = 'Anakin')
MATCH person-[:email]->email
RETURN email
email
Node[6]{address:"anakin@example.com",comment:"home"}
Node[7]{address:"anakin@example.org",comment:"work"}

Здесь нет соединенной таблицы, но если она необходима, следующий пример покажет, как это сделать с помощью написания связи образца типа:

-[r:belongs_to]-> 

которая вводит (эквивалент) соединенной таблицы, доступной через переменную r. В действительности это – именованная связь в Cypher, поэтому мы говорим «соединить Person с Group через belongs_to». Посмотрите иллюстрацию, на которой сравнивается модель SQL и Neo4j/Cypher.

А вот примеры запросов:

SQL-запрос

SELECT "Group".*, "Person_Group".*
FROM "Person"
JOIN "Person_Group" ON "Person".id = "Person_Group".person_id
JOIN "Group" ON "Person_Group".Group_id="Group".id
WHERE "Person".name = 'Bridget'
NAME IDBELONGS_TO_GROUP_IDPERSON_IDGROUP_ID
Admin4324

Cypher-запрос

START person=node:Person(name = 'Bridget')
MATCH person-[r:belongs_to]->group
RETURN group, r
groupr
Node[5]{name:"Admin",id:4}:belongs_to[0]{}

Внешнее соединение выполняются очень просто. Добавьте OPTIONAL перед match, и эта необязательная связь между узлами и есть внешнее соединение в Cypher.

Будет ли это левым внешним соединением или же правым внешним соединением зависит от того, с какой стороны образца определена стартовая точка. Этот пример на левое внешнее соединение, поскольку связанный узел находится слева:

SQL-запрос

SELECT "Person".name, "Email".address
FROM "Person" LEFT
JOIN "Email" ON "Person".id = "Email".person_id
NAMEADDRESS
Anakinanakin@example.com
Anakinanakin@example.org
Bridgetnull

Cypher-запрос

START person=node:Person('name: *')
OPTIONAL MATCH person-[:email]->email
RETURN person.name, email.address
person.nameemail.address
"Anakin""anakin@example.com"
"Anakin""anakin@example.org"
"Bridget"null

Связи в Neo4j являются гражданами первого класса – это подобно таблицам SQL, предварительно соединенным друг с другом. Поэтому, естественно, Cypher спроектирован так, чтобы быть в состоянии легко обрабатывать сильно связанные данные.

Одной из таких областей являются древовидные структуры – каждый, кто пытался сохранять древовидные структуры в SQL, знает, что это было непросто, т.к. приходилось обходить ограничения реляционной модели. Существуют даже книги на эту тему.

Чтобы найти все группы и подгруппы, в которые входит Бриджит (Bridget), достаточно выполнить такой запрос в Cypher:

Cypher-запрос

START person=node:Person('name: Bridget')
MATCH person-[:belongs_to*]->group
RETURN person.name, group.name
person.namegroup.name
"Bridget""Admin"
"Bridget""Technichian"
"Bridget""User"

Предыдущая | Следующая