Данные редко остаются такими, какими их создали. Егор наконец сообщил год рождения, Аня стала модератором, Дина передумала и подняла оценку «Обломову». В уроке про 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.
домашка