Статьи

neo4j / cypher: поиск путей с одним прыжком

В документах neo4j есть несколько примеров, объясняющих, как писать зашифрованные запросы, относящиеся к диапазонам путей, но интересный вариант, с которым я недавно столкнулся, — это то, где мы хотим найти отдельные переходы в пути.

Я думал, что менеджеры, которые были у «Челси» с момента прихода к власти Романа Абрамовича, послужат полезным набором данных, чтобы показать, как это работает.

Таким образом, мы создаем всех менеджеров и отношения ‘SUCCEEDED_BY’ между ними следующим образом:

CREATE (ranieri {name: "Claudio Ranieri"})
CREATE (mourinho {name: "Jose Mourinho"})
CREATE (grant {name: "Avram Grant"})
CREATE (scolari {name: "Luiz Felipe Scolari"})
CREATE (wilkins {name: "Ray Wilkins"})
CREATE (hiddink {name: "Guus Hiddink"})
CREATE (ancelotti {name: "Carlo Ancelotti"})
CREATE (villasBoas {name: "Andre Villas Boas"})
CREATE (diMatteo {name: "Roberto Di Matteo"})
CREATE (benitez {name: "Rafael Benitez"})
 
CREATE (ranieri)-[:SUCCEEDED_BY]->(mourinho)
CREATE (mourinho)-[:SUCCEEDED_BY]->(grant)
CREATE (grant)-[:SUCCEEDED_BY]->(scolari)
CREATE (scolari)-[:SUCCEEDED_BY]->(wilkins)
CREATE (wilkins)-[:SUCCEEDED_BY]->(hiddink)
CREATE (hiddink)-[:SUCCEEDED_BY]->(ancelotti)
CREATE (ancelotti)-[:SUCCEEDED_BY]->(villasBoas)
CREATE (villasBoas)-[:SUCCEEDED_BY]->(diMatteo)
CREATE (diMatteo)-[:SUCCEEDED_BY]->(benitez)
CREATE (benitez)-[:SUCCEEDED_BY]->(mourinho)

Мы хотим написать запрос, который будет возвращать пары ‘SUCCEEDED_BY’, начиная с Клаудио Раньери и заканчивая вниз.

Мы могли бы начать с этого запроса, который начинается с Раньери и идет вниз по дереву в поисках следующего преемника:

START m = node:node_auto_index(name="Claudio Ranieri") 
MATCH path = (m)-[rel:SUCCEEDED_BY*]->(successor) 
RETURN EXTRACT(n IN NODES(path): n.name)
==> +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
==> | EXTRACT(n IN NODES(path): n.name)                                                                                                                                                               |
==> +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
==> | ["Claudio Ranieri","Jose Mourinho"]                                                                                                                                                             |
==> | ["Claudio Ranieri","Jose Mourinho","Avram Grant"]                                                                                                                                               |
==> | ["Claudio Ranieri","Jose Mourinho","Avram Grant","Luiz Felipe Scolari"]                                                                                                                         |
==> | ["Claudio Ranieri","Jose Mourinho","Avram Grant","Luiz Felipe Scolari","Ray Wilkins"]                                                                                                           |
==> | ["Claudio Ranieri","Jose Mourinho","Avram Grant","Luiz Felipe Scolari","Ray Wilkins","Guus Hiddink"]                                                                                            |
==> | ["Claudio Ranieri","Jose Mourinho","Avram Grant","Luiz Felipe Scolari","Ray Wilkins","Guus Hiddink","Carlo Ancelotti"]                                                                          |
==> | ["Claudio Ranieri","Jose Mourinho","Avram Grant","Luiz Felipe Scolari","Ray Wilkins","Guus Hiddink","Carlo Ancelotti","Andre Villas Boas"]                                                      |
==> | ["Claudio Ranieri","Jose Mourinho","Avram Grant","Luiz Felipe Scolari","Ray Wilkins","Guus Hiddink","Carlo Ancelotti","Andre Villas Boas","Roberto Di Matteo"]                                  |
==> | ["Claudio Ranieri","Jose Mourinho","Avram Grant","Luiz Felipe Scolari","Ray Wilkins","Guus Hiddink","Carlo Ancelotti","Andre Villas Boas","Roberto Di Matteo","Rafael Benitez"]                 |
==> | ["Claudio Ranieri","Jose Mourinho","Avram Grant","Luiz Felipe Scolari","Ray Wilkins","Guus Hiddink","Carlo Ancelotti","Andre Villas Boas","Roberto Di Matteo","Rafael Benitez","Jose Mourinho"] |
==> +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
==> 10 rows

Это дает нам все комбинации, но, к сожалению, включает и всех предыдущих преемников, так что это не совсем то, что мы хотим.

Нам нужно манипулировать запросом таким образом, чтобы у нас был список всех менеджеров, а затем мы могли следовать за одним отношением ‘SUCCEEDED_BY’, чтобы найти их преемника.

Мы могли бы легко получить этот список менеджеров, следуя отношениям «SUCCEEDED_BY» и получая узлы на другой стороне, но это привело бы к исключению Клаудио Раньери, поскольку у него нет входящих отношений.

Чтобы сохранить Ranieri, мы можем использовать путь длины 0 следующим образом:

START m = node:node_auto_index(name="Claudio Ranieri") 
MATCH path = (m)-[rel:SUCCEEDED_BY*0..]->(mm) 
RETURN mm
==> +--------------------------------------+
==> | mm                                   |
==> +--------------------------------------+
==> | Node[8]{name:"Claudio Ranieri"}      |
==> | Node[9]{name:"Jose Mourinho"}        |
==> | Node[10]{name:"Avram Grant"}         |
==> | Node[11]{name:"Luiz Felipe Scolari"} |
==> | Node[12]{name:"Ray Wilkins"}         |
==> | Node[13]{name:"Guus Hiddink"}        |
==> | Node[14]{name:"Carlo Ancelotti"}     |
==> | Node[15]{name:"Andre Villas Boas"}   |
==> | Node[16]{name:"Roberto Di Matteo"}   |
==> | Node[17]{name:"Rafael Benitez"}      |
==> | Node[9]{name:"Jose Mourinho"}        |
==> +--------------------------------------+
==> 11 rows

Если мы теперь следуем отношениям SUCCEEDED_BY из нашего списка менеджеров, мы получим пары менеджеров:

START m = node:node_auto_index(name="Claudio Ranieri")  
MATCH path = (m)-[rel:SUCCEEDED_BY*0..]->(mm)-[:SUCCEEDED_BY]->(successor)
RETURN DISTINCT mm.name, successor.name
==> +-----------------------------------------------+
==> | mm.name               | successor.name        |
==> +-----------------------------------------------+
==> | "Claudio Ranieri"     | "Jose Mourinho"       |
==> | "Jose Mourinho"       | "Avram Grant"         |
==> | "Avram Grant"         | "Luiz Felipe Scolari" |
==> | "Luiz Felipe Scolari" | "Ray Wilkins"         |
==> | "Ray Wilkins"         | "Guus Hiddink"        |
==> | "Guus Hiddink"        | "Carlo Ancelotti"     |
==> | "Carlo Ancelotti"     | "Andre Villas Boas"   |
==> | "Andre Villas Boas"   | "Roberto Di Matteo"   |
==> | "Roberto Di Matteo"   | "Rafael Benitez"      |
==> | "Rafael Benitez"      | "Jose Mourinho"       |
==> +-----------------------------------------------+
==> 10 rows

Код для этого доступен на консоли neo4j, если вы заинтересованы в дальнейшей игре с ним.