Паттерны мы научились читать. Теперь будем ими писать. Клауза 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: как написать запрос, который можно запускать сколько угодно раз без лишних узлов.
домашка