У меня была проблема с графиком, над которым я недавно работал, когда мне удавалось создавать дублирующиеся узлы, потому что я не применял никаких уникальных ограничений .
Я хотел удалить дубликаты и наткнулся на отличный пост Джимми Рутса, в котором показаны некоторые способы сделать это.
Давайте сначала создадим граф с несколькими дублирующимися узлами для игры:
|
01
02
03
04
05
06
07
08
09
10
|
UNWIND range(0, 100) AS idCREATE (p1:Person {id: toInteger(rand() * id)})MERGE (p2:Person {id: toInteger(rand() * id)})MERGE (p3:Person {id: toInteger(rand() * id)})MERGE (p4:Person {id: toInteger(rand() * id)})CREATE (p1)-[:KNOWS]->(p2)CREATE (p1)-[:KNOWS]->(p3)CREATE (p1)-[:KNOWS]->(p4) Added 173 labels, created 173 nodes, set 173 properties, created 5829 relationships, completed after 408 ms. |
Как нам найти дубликаты узлов?
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
MATCH (p:Person)WITH p.id as id, collect(p) AS nodes WHERE size(nodes) > 1RETURN [ n in nodes | n.id] AS ids, size(nodes)ORDER BY size(nodes) DESCLIMIT 10 ╒══════════════════════╤═════════════╕│"ids" │"size(nodes)"│╞══════════════════════╪═════════════╡│[1,1,1,1,1,1,1,1] │8 │├──────────────────────┼─────────────┤│[0,0,0,0,0,0,0,0] │8 │├──────────────────────┼─────────────┤│[17,17,17,17,17,17,17]│7 │├──────────────────────┼─────────────┤│[4,4,4,4,4,4,4] │7 │├──────────────────────┼─────────────┤│[2,2,2,2,2,2] │6 │├──────────────────────┼─────────────┤│[5,5,5,5,5,5] │6 │├──────────────────────┼─────────────┤│[19,19,19,19,19,19] │6 │├──────────────────────┼─────────────┤│[11,11,11,11,11] │5 │├──────────────────────┼─────────────┤│[25,25,25,25,25] │5 │├──────────────────────┼─────────────┤│[43,43,43,43,43] │5 │└──────────────────────┴─────────────┘ |
Давайте рассмотрим всех людей с «id: 1» и выясним, сколько у них отношений. Наш план — сохранить узел, который имеет наибольшее количество соединений, и избавиться от других.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
MATCH (p:Person)WITH p.id as id, collect(p) AS nodes WHERE size(nodes) > 1WITH nodes ORDER BY size(nodes) DESCLIMIT 1UNWIND nodes AS n RETURN n.id, id(n) AS internalId, size((n)--()) AS relsORDER BY rels DESC ╒══════╤════════════╤══════╕│"n.id"│"internalId"│"rels"│╞══════╪════════════╪══════╡│1 │175 │1284 │├──────┼────────────┼──────┤│1 │184 │721 │├──────┼────────────┼──────┤│1 │180 │580 │├──────┼────────────┼──────┤│1 │2 │391 │├──────┼────────────┼──────┤│1 │195 │361 │├──────┼────────────┼──────┤│1 │199 │352 │├──────┼────────────┼──────┤│1 │302 │5 │├──────┼────────────┼──────┤│1 │306 │1 │└──────┴────────────┴──────┘ |
Таким образом, в этом примере мы хотим сохранить узел с 210 связями и удалить остальные.
Чтобы упростить задачу, нам нужен узел с наибольшим количеством элементов, чтобы быть первым или последним в нашем списке. Мы можем убедиться в этом, упорядочив узлы, прежде чем сгруппировать их.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
MATCH (p:Person)WITH p ORDER BY p.id, size((p)--()) DESCWITH p.id as id, collect(p) AS nodes WHERE size(nodes) > 1RETURN [ n in nodes | {id: n.id,rels: size((n)--()) } ] AS ids, size(nodes)ORDER BY size(nodes) DESCLIMIT 10 ╒══════════════════════════════════════════════════════════════════════╤═════════════╕│"ids" │"size(nodes)"│╞══════════════════════════════════════════════════════════════════════╪═════════════╡│[{"id":1,"rels":1284},{"id":1,"rels":721},{"id":1,"rels":580},{"id":1,│8 ││"rels":391},{"id":1,"rels":361},{"id":1,"rels":352},{"id":1,"rels":5},│ ││{"id":1,"rels":1}] │ │├──────────────────────────────────────────────────────────────────────┼─────────────┤│[{"id":0,"rels":2064},{"id":0,"rels":2059},{"id":0,"rels":1297},{"id":│8 ││0,"rels":1124},{"id":0,"rels":995},{"id":0,"rels":928},{"id":0,"rels":│ ││730},{"id":0,"rels":702}] │ │├──────────────────────────────────────────────────────────────────────┼─────────────┤│[{"id":17,"rels":153},{"id":17,"rels":105},{"id":17,"rels":81},{"id":1│7 ││7,"rels":31},{"id":17,"rels":15},{"id":17,"rels":14},{"id":17,"rels":1│ ││}] │ │├──────────────────────────────────────────────────────────────────────┼─────────────┤│[{"id":4,"rels":394},{"id":4,"rels":320},{"id":4,"rels":250},{"id":4,"│7 ││rels":201},{"id":4,"rels":162},{"id":4,"rels":162},{"id":4,"rels":14}]│ │├──────────────────────────────────────────────────────────────────────┼─────────────┤│[{"id":2,"rels":514},{"id":2,"rels":329},{"id":2,"rels":318},{"id":2,"│6 ││rels":241},{"id":2,"rels":240},{"id":2,"rels":2}] │ │├──────────────────────────────────────────────────────────────────────┼─────────────┤│[{"id":5,"rels":487},{"id":5,"rels":378},{"id":5,"rels":242},{"id":5,"│6 ││rels":181},{"id":5,"rels":158},{"id":5,"rels":8}] │ │├──────────────────────────────────────────────────────────────────────┼─────────────┤│[{"id":19,"rels":153},{"id":19,"rels":120},{"id":19,"rels":84},{"id":1│6 ││9,"rels":53},{"id":19,"rels":45},{"id":19,"rels":1}] │ │├──────────────────────────────────────────────────────────────────────┼─────────────┤│[{"id":11,"rels":222},{"id":11,"rels":192},{"id":11,"rels":172},{"id":│5 ││11,"rels":152},{"id":11,"rels":89}] │ │├──────────────────────────────────────────────────────────────────────┼─────────────┤│[{"id":25,"rels":133},{"id":25,"rels":107},{"id":25,"rels":98},{"id":2│5 ││5,"rels":15},{"id":25,"rels":2}] │ │├──────────────────────────────────────────────────────────────────────┼─────────────┤│[{"id":43,"rels":92},{"id":43,"rels":85},{"id":43,"rels":9},{"id":43,"│5 ││rels":5},{"id":43,"rels":1}] │ │└──────────────────────────────────────────────────────────────────────┴─────────────┘ |
Теперь пришло время удалить дубликаты:
|
1
2
3
4
5
6
7
8
9
|
MATCH (p:Person)WITH p ORDER BY p.id, size((p)--()) DESCWITH p.id as id, collect(p) AS nodes WHERE size(nodes) > 1UNWIND nodes[1..] AS nDETACH DELETE n Deleted 143 nodes, deleted 13806 relationships, completed after 29 ms. |
Теперь, если мы запустим наш дубликат запроса:
|
1
2
3
4
5
6
7
8
|
MATCH (p:Person)WITH p.id as id, collect(p) AS nodes WHERE size(nodes) > 1RETURN [ n in nodes | n.id] AS ids, size(nodes)ORDER BY size(nodes) DESCLIMIT 10 (no changes, no records) |
Что делать, если мы удалим предложение WHERE?
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
MATCH (p:Person)WITH p.id as id, collect(p) AS nodes RETURN [ n in nodes | n.id] AS ids, size(nodes)ORDER BY size(nodes) DESCLIMIT 10 ╒═════╤═════════════╕│"ids"│"size(nodes)"│╞═════╪═════════════╡│[23] │1 │├─────┼─────────────┤│[86] │1 │├─────┼─────────────┤│[77] │1 │├─────┼─────────────┤│[59] │1 │├─────┼─────────────┤│[50] │1 │├─────┼─────────────┤│[32] │1 │├─────┼─────────────┤│[41] │1 │├─────┼─────────────┤│[53] │1 │├─────┼─────────────┤│[44] │1 │├─────┼─────────────┤│[8] │1 │└─────┴─────────────┘ |
Ура, больше нет дубликатов! Наконец, давайте проверим, сохранили ли мы ожидаемый узел. Мы ожидаем, что его внутренний идентификатор равен 175:
|
1
2
3
4
5
6
7
8
|
MATCH (p:Person {id: 1})RETURN size((p)--()), id(p) AS internalId ╒═══════════════╤════════════╕│"size((p)--())"│"internalId"│╞═══════════════╪════════════╡│242 │175 │└───────────────┴────────────┘ |
Что это делает! Отношений намного меньше, чем раньше, потому что многие из этих отношений должны были дублировать узлы, которые мы сейчас удалили.
Если мы хотим пойти еще дальше, мы можем «объединить» отношения дубликата узла с узлами, которые мы сохранили, но это для другого поста!
| Опубликовано на Java Code Geeks с разрешения Марка Нидхэма, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Neo4j: Cypher — Удаление дублирующихся узлов
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |