В прошлом уроке мы правили содержимое узлов. Осталось научиться их удалять. Здесь у графа есть защитное правило, которого нет у таблиц: узел нельзя удалить, пока на нём держатся связи. Разбираемся, зачем оно нужно и как удалять так, чтобы не пожалеть.
Сначала посмотри, потом удаляй
Хороший столяр меряет семь раз. В Cypher «померить» значит выполнить тот же MATCH с RETURN и посмотреть, что найдётся. Только убедившись, что в ответе ровно то, что нужно стереть, меняешь RETURN на DELETE. Удаление в Neo4j не спрашивает подтверждения, и корзины, откуда можно достать удалённое, нет.
Удалить связь
Аня и Глеб поссорились, знакомство нужно убрать. Сначала смотрим:
MATCH (:Person {name: 'Аня'})-[r:KNOWS]->(:Person {name: 'Глеб'})
RETURN r;
+------------------------+
| r |
+------------------------+
| [:KNOWS {since: 2020}] |
+------------------------+
1 row
Связь одна, та самая. Удаляем:
MATCH (:Person {name: 'Аня'})-[r:KNOWS]->(:Person {name: 'Глеб'})
DELETE r;
0 rows
Deleted 1 relationship
Узлы Ани и Глеба остались на месте, исчезла только связь между ними.
Удалить узел без связей
У Яны нет ни одной связи, её узел удаляется обычным DELETE:
MATCH (p:Person {name: 'Яна'})
DELETE p;
0 rows
Deleted 1 node
Узел со связями: защита от висячих связей
С Ильёй так не выйдет, у него знакомство и два отзыва:
MATCH (p:Person {name: 'Илья'})
DELETE p;
G1001: dependent object error - edges still exist
Ошибка edges still exist значит «у узла ещё есть связи». Это правило из урока про property graph: связь не может висеть без конца. Если удалить узел и оставить связи, они указывали бы в пустоту. Neo4j отказывается, и запрос не меняет ничего.
У защиты есть смысл: она ловит ошибку «удалил не тот узел». Если ты собирался удалить участника-дубликат без связей, а MATCH нашёл настоящего, ошибка спасёт его вместе со всеми знакомствами.
DETACH DELETE: вместе со связями
Если связи действительно нужно удалить вместе с узлом, пишут DETACH DELETE:
MATCH (p:Person {name: 'Илья'})
DETACH DELETE p;
0 rows
Deleted 1 node, deleted 3 relationships
Один узел и три связи: знакомство с Зоей и два отзыва на книги. Сами книги и Зоя остались.
Можно удалять и вручную, перечислив связи и узел в одном DELETE. Это то же самое, но явное:
MATCH (p:Person {name: 'Егор'})-[r]-()
DELETE r, p;
0 rows
Deleted 1 node, deleted 4 relationships
Удалить по условию
DELETE работает с каждой найденной строкой. Удалим все отзывы на «Обломова», а заодно посчитаем их:
MATCH (b:Book {title: 'Обломов'})<-[r:READ]-()
DELETE r
RETURN count(r) AS removed;
+---------+
| removed |
+---------+
| 2 |
+---------+
1 row
Deleted 2 relationships
Удалённую связь ещё можно посчитать в том же запросе, но читать её свойства после DELETE уже не стоит.
Мягкое удаление
Часто данные удалять жалко: участник ушёл из клуба, но его отзывы ещё пригодятся для рекомендаций, а историю хочется восстановить, если он вернётся. Тогда вместо DELETE узел помечают. Такой приём называют мягким удалением:
MATCH (p:Person {name: 'Зоя'})
SET p:Archived, p.archivedAt = date('2026-10-24')
RETURN p;
+----------------------------------------------------------------------+
| p |
+----------------------------------------------------------------------+
| (:Person:Archived {name: "Зоя", archivedAt: 2026-10-24, born: 1999}) |
+----------------------------------------------------------------------+
1 row
Set 1 property, added 1 label
Связи Зои на месте, а запросы приложения пропускают архивных:
MATCH (p:Person)
WHERE NOT p:Archived
RETURN count(p) AS active;
+--------+
| active |
+--------+
| 6 |
+--------+
1 row
Вернуть участника можно одним REMOVE p:Archived. Цена приёма в том, что каждый запрос теперь должен помнить про метку, иначе архивные участники всплывут в ответах.
Стереть всё
На учебной базе иногда нужно начать с нуля. Запрос «удалить все узлы вместе со связями» короткий и страшный:
MATCH (n)
DETACH DELETE n;
0 rows
Deleted 15 nodes, deleted 23 relationships
MATCH (n)
RETURN count(n) AS nodes;
+-------+
| nodes |
+-------+
| 0 |
+-------+
1 row
В рабочей базе так не делают никогда. А на большой учебной базе такой запрос пытается удалить всё одной транзакцией и может упереться в память. Для больших объёмов удаляют порциями через CALL { ... } IN TRANSACTIONS, его мы увидим в уроке про LOAD CSV. Индексы и ограничения при этом не удаляются, их убирают отдельно, об этом следующий урок.
Попробуй сам
Песочница начинает с полного графа клуба. Запрос удаляет Егора вместе со связями и возвращает число оставшихся участников. Нажми «сбросить базу», если захочешь вернуть всё как было.
Посмотри на картинку: после удаления Егора от Дины больше нельзя дойти до второй половины клуба. Попробуй удалить Глеба обычным DELETE и прочитай ошибку.
Проверь себя
Задание
Дальше
Удаление, как и любая запись, опирается на поиск. Когда узлов миллионы, искать участника по имени перебором долго. В следующем уроке разберём индексы, которые ускоряют поиск, и ограничения, которые не пускают в базу дубли.
домашка