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

SET и REMOVE: меняем свойства и метки

junior~20 мин

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

Данные редко остаются такими, какими их создали. Егор наконец сообщил год рождения, Аня стала модератором, Дина передумала и подняла оценку «Обломову». В уроке про MERGE мы уже встречали SET внутри веток ON CREATE и ON MATCH. Сегодня разбираем его отдельно, вместе с парной клаузой REMOVE.

Карандаш и ластик

Карточка участника в картотеке клуба заполнена карандашом. SET дописывает или исправляет запись, REMOVE стирает её ластиком. Сама карточка при этом остаётся той же: меняется содержимое узла, а не узел.

Схема всегда одна: сначала MATCH находит, что менять, потом SET или REMOVE меняет найденное.

SET: записать свойство

MATCH (p:Person {name: 'Егор'})
SET p.born = 1995
RETURN p;
+--------------------------------------+
| p                                    |
+--------------------------------------+
| (:Person {name: "Егор", born: 1995}) |
+--------------------------------------+

1 row
Set 1 property

Свойства born у Егора не было, SET его создал. Если свойство есть, SET перезапишет значение. Несколько свойств пишут через запятую:

MATCH (p:Person {name: 'Егор'})
SET p.born = 1996, p.email = 'egor@example.com'
RETURN p;
+-----------------------------------------------------------------+
| p                                                               |
+-----------------------------------------------------------------+
| (:Person {name: "Егор", email: "egor@example.com", born: 1996}) |
+-----------------------------------------------------------------+

1 row
Set 2 properties

Значение можно вычислить из текущего. Дина подняла оценку «Обломову» на балл:

MATCH (:Person {name: 'Дина'})-[r:READ]->(b:Book)
SET r.rating = r.rating + 1
RETURN b.title, r.rating;
+----------------------+
| b.title   | r.rating |
+----------------------+
| "Обломов" | 3        |
+----------------------+

1 row
Set 1 property

SET работает и со связями: здесь мы изменили свойство связи READ.

null как удаление

В Neo4j у свойства не бывает значения null: свойство либо есть, либо его нет. Поэтому запись null удаляет свойство:

MATCH (p:Person {name: 'Егор'})
SET p.email = null
RETURN p;
+--------------------------------------+
| p                                    |
+--------------------------------------+
| (:Person {name: "Егор", born: 1996}) |
+--------------------------------------+

1 row
Set 1 property

Строка итога сообщает Set 1 property: удаление тоже считается записью свойства.

Сразу много свойств: += и =

Оператор += дописывает свойства из карты, не трогая остальные. Это удобно, когда новые данные приходят пачкой:

MATCH (p:Person {name: 'Аня'})
SET p += {born: 1995, city: 'Москва'}
RETURN p;
+-----------------------------------------------------+
| p                                                   |
+-----------------------------------------------------+
| (:Person {name: "Аня", city: "Москва", born: 1995}) |
+-----------------------------------------------------+

1 row
Set 2 properties

А = с картой заменяет все свойства узла целиком: чего нет в карте, то исчезнет. Это сильный и опасный инструмент, им легко стереть лишнее:

MATCH (p:Person {name: 'Аня'})
SET p = {name: 'Аня', born: 1994}
RETURN p;
+-------------------------------------+
| p                                   |
+-------------------------------------+
| (:Person {name: "Аня", born: 1994}) |
+-------------------------------------+

1 row
Set 3 properties

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

Метки: SET и REMOVE

Метку добавляют тем же SET, через двоеточие:

MATCH (p:Person {name: 'Аня'})
SET p:Moderator
RETURN p, labels(p);
+-------------------------------------------------------------------------+
| p                                             | labels(p)               |
+-------------------------------------------------------------------------+
| (:Person:Moderator {name: "Аня", born: 1994}) | ["Person", "Moderator"] |
+-------------------------------------------------------------------------+

1 row
Added 1 label

Повторный SET уже существующей метки ничего не меняет, итога изменений не будет. Метку можно повесить сразу на многих: всем участникам, родившимся до 1990 года.

MATCH (p:Person)
WHERE p.born < 1990
SET p:Senior
RETURN p.name, labels(p) AS labels
ORDER BY p.name;
+-------------------------------+
| p.name | labels               |
+-------------------------------+
| "Глеб" | ["Person", "Senior"] |
| "Илья" | ["Person", "Senior"] |
+-------------------------------+

2 rows
Added 2 labels

REMOVE стирает метки и свойства:

MATCH (p:Senior)
REMOVE p:Senior;
0 rows
Removed 2 labels
MATCH (p:Person {name: 'Аня'})
REMOVE p:Moderator, p.born
RETURN p;
+-------------------------+
| p                       |
+-------------------------+
| (:Person {name: "Аня"}) |
+-------------------------+

1 row
Set 1 property, removed 1 label

Обрати внимание на итог: удаление свойства снова посчитано как Set 1 property. Если удалять нечего, REMOVE молча ничего не делает.

Если MATCH ничего не нашёл

SET и REMOVE работают только с найденными строками. Если MATCH пуст, менять нечего, и ошибки не будет:

MATCH (p:Person {name: 'Никто'})
SET p.born = 2000
RETURN p;
+---+
| p |
+---+
+---+

0 rows

Это удобно в скриптах, но коварно: опечатка в имени превращает запрос в тихое «ничего не сделал». Привычка читать строку итога изменений спасает от таких сюрпризов.

Попробуй сам

Запрос вешает метку Reader на всех, кто прочитал две книги и больше, и возвращает их вместе с книгами. На картинке видно, кто попал в активные читатели.

Проверь MATCH (r:Reader) RETURN r.name, labels(r). Потом сними метку запросом с REMOVE и убедись, что итог изменений показывает removed 6 labels.

Проверь себя

Задание

Дальше

Менять мы научились. Осталось научиться удалять, и здесь у графа есть своё правило безопасности. В следующем уроке разберём DELETE и DETACH DELETE.

домашка

Домашка