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

#neo4j #cypher

#neo4j #шифр

Вопрос:

Я рассматриваю neo4j как базу данных графов, и запросы путей переменной длины будут очень важным вариантом использования. Теперь я думаю, что нашел пример запроса, который Cypher не будет поддерживать.

Основная проблема заключается в том, что я хочу рассматривать составные отношения как единое отношение. Позвольте мне привести пример: поиск соучастников. Я сделал это, используя стандартную базу данных фильмов. Цель состоит в том, чтобы найти всех участников, которые действовали вместе с Томом Хэнксом. Это можно найти с помощью запроса:

 MATCH (tom {name: "Tom Hanks"})-[:ACTED_IN]->()<-[:ACTED_IN]-(a:Person) return a
  

Теперь, что, если мы хотим рекурсивно найти соучастников соучастников.
Мы можем переписать приведенный выше запрос на:

 MATCH (tom {name: "Tom Hanks"})-[:ACTED_IN*2]-(a:Person) return a
  

И тогда становится ясно, что мы можем сделать это с

 MATCH (tom {name: "Tom Hanks"})-[:ACTED_IN*]-(a:Person) return a
  

Примечательно, что все пути нечетной длины исключены, поскольку они не заканчиваются на Person .

Теперь я нашел запрос, который я не могу понять, как сделать рекурсивным:

 MATCH (tom {name: "Tom Hanks"})-[:ACTED_IN]->()<-[:DIRECTED]-()-[:DIRECTED]->()<-[:ACTED_IN]-(a:Person) return DISTINCT a
  

Другими словами, все актеры, у которых есть режиссер, общий с Томом Хэнксом.

Чтобы сделать это рекурсивным, я попытался:

 MATCH (tom {name: "Tom Hanks"})-[:ACTED_IN|DIRECTED*]-(a:Person) return DISTINCT a
  

Однако (кроме того, что, похоже, вообще не завершено). Это также позволит охватить участников.
То есть он будет соответствовать путям вида

 ()-[:ACTED_IN]->()<-[:ACTED_IN]-()
  

Итак, что мне интересно, так это:
можем ли мы каким-либо образом ограничить порядок, в котором отношения возникают в многолучевом запросе?
Что-то вроде:

 MATCH (tom {name: "Tom Hanks"}){-[:ACTED_IN]->()<-[:DIRECTED]-()-[:DIRECTED]->()<-[:ACTED_IN]-}*(a:Person) return DISTINCT a
  

Где * применяется ко всему, что заключено в фигурные скобки.

Ответ №1:

Здесь должны помочь процедуры расширения пути из APOC Procedures, поскольку мы добавили возможность выражать повторяющиеся последовательности меток, отношений или того и другого.

В этом случае, поскольку вы хотите сопоставить актера шаблона, а не режиссера (или любой из фильмов в пути), нам нужно указать, какие узлы в пути вы хотите вернуть, для чего требуется либо использовать labelFilter в дополнение к relationshipFilter , либо просто использовать объединенное sequence свойство config для указания ожидаемых переменных меток / отношений, и убедиться, что мы используем фильтр конечного узла на узле :Person в нужной вам точке шаблона.

Вот как вы могли бы это сделать после установки APOC:

 MATCH (tom:Person {name: "Tom Hanks"})
CALL apoc.path.expandConfig(tom, {sequence:'>Person, ACTED_IN>, *, <DIRECTED, *, DIRECTED>, *, <ACTED_IN', maxLevel:12}) YIELD path
WITH last(nodes(path)) as person, min(length(path)) as distance
RETURN person.name
  

Обычно мы используем subgraphNodes() для этого, поскольку это эффективно при расширении и сокращении путей к узлам, которые мы уже видели, но в этом случае мы хотим сохранить возможность повторного посещения уже посещенных узлов, поскольку они могут встречаться в дальнейших итерациях последовательности, поэтому для получения правильного ответа мы не можем использовать этот или любой из процедур, которые используют NODE_GLOBAL unique.

Из-за этого нам нужно остерегаться изучения слишком большого количества путей, поскольку количество перестановок отношений для исследования, которые соответствуют пути, будет стремительно расти, даже после того, как мы уже нашли все возможные отдельные узлы. Чтобы избежать этого, нам придется добавить максимальный уровень, поэтому в данном случае я использую 12.

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

Свойство конфигурации sequence позволяет нам указывать чередующиеся фильтры типа метки и отношения для каждого шага в последовательности, начиная с начального узла. Мы используем символ фильтра конечного узла, > перед меткой первого лица ( >Person ), указывающий, что нам нужны пути только к узлу Person в этой точке последовательности (как первый элемент в последовательности, он также будет последним элементом в последовательности, поскольку он повторяется). Мы используем подстановочный знак * для фильтра меток всех других узлов, что означает, что узлы занесены в белый список и будут пройдены независимо от того, какая у них метка, но мы не хотим возвращать какие-либо пути к этим узлам.

Комментарии:

1. Спасибо, это было именно то, что я искал. Итак, чтобы уточнить, это не выражается в чистом Cypher, но для правильного захвата требуется APOC?

2. Правильно, в настоящее время шаблоны переменной длины не могут в достаточной степени выражать явные повторяющиеся последовательности различных типов отношений и / или меток узлов. Вы могли бы использовать Cypher для фильтрации путей после их расширения, но это может быть очень дорогостоящим в зависимости от количества возможных путей перед фильтрацией.

Ответ №2:

Если вы хотите увидеть всех актеров, которые снимались в фильмах режиссеров, которые снимали Тома Хэнкса, но которые никогда не снимались с Томом, вот один из способов:

 MATCH (tom {name: "Tom Hanks"})-[:ACTED_IN]->(m)
MATCH (m)<-[:ACTED_IN]-(ignoredActor)
WITH COLLECT(DISTINCT m) AS ignoredMovies, COLLECT(DISTINCT ignoredActor) AS ignoredActors
UNWIND ignoredMovies AS movie
MATCH (movie)<-[:DIRECTED]-()-[:DIRECTED]->(m2)
WHERE NOT m2 IN ignoredMovies
MATCH (m2)<-[:ACTED_IN]-(a:Person)
WHERE NOT a IN ignoredActors
RETURN DISTINCT a
  

2 верхних MATCH предложения намеренно не объединены в одно предложение, чтобы узел Тома Хэнкса был захвачен как ignoredActor . ( MATCH Предложение отфильтровывает любой результат, в котором дважды используется одно и то же отношение.)