Раздел 39 · Neo4j и графовые базы

CREATE: создаём узлы и связи

junior~20 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Паттерны мы научились читать. Теперь будем ими писать. Клауза CREATE берёт тот же рисунок, что и MATCH, но не ищет его в графе, а создаёт. В этом уроке начинаем с пустой базы и собираем в ней маленький клуб своими руками. Песочница внизу урока тоже пустая, все примеры можно повторить в ней по порядку.

Первый узел

CREATE (:Person {name: 'Аня', born: 1994});
0 rows
Created 1 node, set 2 properties, added 1 label

Запрос ничего не вернул, поэтому вместо таблицы 0 rows. Зато под ним итог изменений: один узел, два свойства, одна метка. Привыкай читать эту строку после каждой записи. Она сразу показывает, сделал ли запрос то, что ты задумал.

Если созданный узел нужен в ответе, дай ему переменную и верни её:

CREATE (b:Person {name: 'Борис', born: 1990})
RETURN b;
+---------------------------------------+
| b                                     |
+---------------------------------------+
| (:Person {name: "Борис", born: 1990}) |
+---------------------------------------+

1 row
Created 1 node, set 2 properties, added 1 label

Связь между существующими узлами

Связать Аню и Бориса можно, только найдя их. Сначала MATCH, потом CREATE для связи между найденными узлами:

MATCH (a:Person {name: 'Аня'}), (b:Person {name: 'Борис'})
CREATE (a)-[r:KNOWS {since: 2015}]->(b)
RETURN r;
+------------------------+
| r                      |
+------------------------+
| [:KNOWS {since: 2015}] |
+------------------------+

1 row
Created 1 relationship, set 1 property

Обрати внимание: в CREATE переменные a и b уже связаны с найденными узлами, поэтому новых узлов не появилось, создана только связь. Если бы MATCH ничего не нашёл, строк для CREATE не было бы, и связь тоже не создалась бы.

Целый путь за раз

Паттерн в CREATE может быть длинным. Новые узлы и связи между ними создаются вместе:

CREATE (v:Person {name: 'Вера', born: 1998})-[:LIVES_IN]->(:City {name: 'Казань'});
0 rows
Created 2 nodes, created 1 relationship, set 3 properties, added 2 labels

Два узла, одна связь, две метки и три свойства, всё одним запросом.

Какие значения бывают у свойств

Свойство может хранить целое и дробное число, строку, логическое значение, дату и время, а ещё список из значений одного типа:

CREATE (b:Book {
  title: 'Обломов',
  year: 1859,
  rating: 4.5,
  tags: ['классика', 'роман'],
  inStock: true,
  bought: date('2026-10-24')
})
RETURN b;
+---------------------------------------------------------------------------------------------------------------------+
| b                                                                                                                   |
+---------------------------------------------------------------------------------------------------------------------+
| (:Book {rating: 4.5, inStock: TRUE, title: "Обломов", year: 1859, bought: 2026-10-24, tags: ["классика", "роман"]}) |
+---------------------------------------------------------------------------------------------------------------------+

1 row
Created 1 node, set 6 properties, added 1 label

Порядок свойств в ответе не совпадает с тем, как ты их написал. Свойства узла не упорядочены, клиент печатает их в своём внутреннем порядке, и полагаться на него не стоит.

Вложенный объект в свойство положить нельзя. Если нужен адрес из нескольких частей, это либо отдельные свойства, либо отдельный узел:

CREATE (:Person {name: 'Глеб', address: {city: 'Москва'}});
22N01: data exception - invalid type. Expected the value Map{city -> String("Москва")} to be of type BOOLEAN, STRING, INTEGER, FLOAT, DATE, LOCAL TIME, ZONED TIME, LOCAL DATETIME, ZONED DATETIME, DURATION or POINT, but was of type MAP NOT NULL.
  22G03: data exception - invalid value type

Ошибка длинная, но её суть в последних словах: ожидался простой тип, а пришла карта MAP. Ещё одно правило: тип связи в CREATE обязателен и он ровно один.

CREATE (:Person {name: 'Глеб'})-[]->(:City {name: 'Москва'});
42I14: syntax error or access rule violation - invalid number of relationship types. Exactly one relationship type must be specified for `CREATE`. (line 1, column 32 (offset: 31))
"CREATE (:Person {name: 'Глеб'})-[]->(:City {name: 'Москва'})"
                                ^
  42001: syntax error or access rule violation - invalid syntax

Ловушка: повторный запуск

Что будет, если выполнить первый запрос урока ещё раз?

CREATE (:Person {name: 'Аня', born: 1994});
0 rows
Created 1 node, set 2 properties, added 1 label
MATCH (p:Person {name: 'Аня'})
RETURN count(p) AS anyas;
+-------+
| anyas |
+-------+
| 2     |
+-------+

1 row

Теперь в базе две Ани. CREATE не проверяет, есть ли уже такой узел, он всегда создаёт новый. Для базы это два разных объекта, у каждого свой внутренний номер. В скриптах загрузки, которые запускают повторно, это превращается в тихие дубли. Лекарство от дублей это MERGE, ему посвящён следующий урок, а ещё ограничение уникальности из урока про индексы.

Посмотрим, что получилось в итоге:

MATCH (n)
RETURN labels(n) AS labels, n.name AS name, n.title AS title;
+-----------------------------------+
| labels     | name     | title     |
+-----------------------------------+
| ["Person"] | "Аня"    | NULL      |
| ["Person"] | "Борис"  | NULL      |
| ["Person"] | "Вера"   | NULL      |
| ["City"]   | "Казань" | NULL      |
| ["Book"]   | NULL     | "Обломов" |
| ["Person"] | "Аня"    | NULL      |
+-----------------------------------+

6 rows

Попробуй сам

Песочница этого урока начинает с пустой базы. Запрос создаёт двух участников, их знакомство и книгу, а потом возвращает все пути, чтобы ты увидел результат на картинке.

Выполни запрос дважды и посмотри на картинку: сколько теперь Бориса? Кнопка «сбросить базу» вернёт песочницу к пустому графу.

Проверь себя

Задание

Дальше

Дубли от повторного CREATE это первая настоящая проблема графа. В следующем уроке разберём MERGE: как написать запрос, который можно запускать сколько угодно раз без лишних узлов.

домашка

Домашка