Cypher запрос, чтобы найти все отношения между несколькими узлами - PullRequest
0 голосов
/ 08 декабря 2018

У меня есть сложный график, как показано на изображении ниже:

enter image description here

Здесь каждое отношение имеет значение типа.Мне нужно написать запрос шифра, чтобы найти все отношения (с их значениями типа) среди заданного набора узлов (два или более).Узлы могут быть введены в любом порядке, например, x64-> Linux-> Oracle или Oracle-> Linux-> 10.2.

РЕДАКТИРОВАТЬ

Я ожидаю вывод, как это.Вся комбинация узлов с именем отношения, которое связывает их.

  1. Для ввода: x64-> Linux-> Oracle

enter image description here

Для ввода: Linux-> 64-> Oracle-> 12c

enter image description here

DATA

Доступ к данным возможен из https://www.dropbox.com/s/w28omkdrgmhv7ud/Neo4j%20Queries.txt?dl=0

РЕДАКТИРОВАТЬ Новый формат вывода для ввода x64-> Linux-> Oracle

enter image description here

Ответы [ 2 ]

0 голосов
/ 09 декабря 2018

При условии, что вы ищете только отношения, напрямую соединяющие каждую пару узлов в наборе (в отличие от поиска всех многоскачковых путей между парами узлов в наборе), процедуры APOC имеют apoc.algo.cover() для именно этого варианта использования:

...
// assume `nodes` is the collection of nodes
CALL apoc.algo.cover(nodes) YIELD rel
RETURN rel

РЕДАКТИРОВАТЬ

Как уже упоминалось в моем комментарии, ваше изменение требований кардинально меняет природу вопроса.

Похоже, вам нужны полные результаты пути (направленные), включая узлы, которых нет в ваших входных данных, и вы хотите убедиться, что один и тот же атрибут type присутствует для всех отношений в пути.

Это требует, чтобы мы нашли порядок этих узлов, чтобы мы могли определить путь между ними.В то время как мы могли бы найти все возможные перестановки входных узлов (для порядка обхода путей), я думаю, что мы можем обойтись, просто найдя перестановки 2 для начального и конечного узлов (УДАЛЯЯ коллекцию дважды и удаляя строки, гденачальный и конечный узлы одинаковы).Сначала нужно найти все типы отношений ввода и вывода, чтобы мы могли использовать некоторые операции над множествами (типы вывода начального узла пересекались с типами ввода конечного узла, пересекающегося со всеми (пересекающимися) типами входа и выхода другогоузлы), чтобы найти потенциальные типы, которые могут присутствовать в отношениях, которые могут соединить все узлы.

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

Предположим, что узлы имеют тип: Node со свойством name.

MATCH (entity:Entity) 
WHERE entity.key in ['Product','Version','BinaryType'] AND entity.value in ['pc','10.2','64']
WITH collect(entity) as nodes
UNWIND nodes as node
WITH nodes, node, [()-[r]->(node) | r.type] as inputTypes, [(node)-[r]->() | r.type] as outputTypes
WITH nodes, node, apoc.coll.toSet(inputTypes) as inputTypes, apoc.coll.toSet(outputTypes) as outputTypes
WITH nodes, collect({node:node, inputTypes:inputTypes, outputTypes:outputTypes}) as nodeData
UNWIND nodeData as start
UNWIND nodeData as end
WITH nodes, start, end, nodeData
WHERE start <> end
WITH nodes, start, end, apoc.coll.subtract(nodeData, [start, end]) as theRest
WITH nodes, start.node as start, end.node as end, apoc.coll.intersection(start.outputTypes, end.inputTypes) as possibleTypes, [data in theRest | apoc.coll.intersection(data.inputTypes, data.outputTypes)] as otherTypes
WITH nodes, start, end, reduce(possibleTypes = possibleTypes, types in otherTypes | apoc.coll.intersection(possibleTypes, types)) as possibleTypes
WHERE size(possibleTypes) > 0
UNWIND possibleTypes as type
MATCH path = (start)-[*]->(end)
WHERE all(rel in relationships(path) WHERE rel.type = type) 
 AND length(path) >= size(nodes) - 1 
 AND all(node in nodes WHERE node in nodes(path))
RETURN nodes(path) as pathNodes, type

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

MATCH (entity:Entity) 
WHERE entity.key in ['Product','Version','BinaryType'] AND entity.value in ['pc','10.2','64']
WITH collect(entity) as nodes
UNWIND nodes as node
WITH nodes, node, [()-[r]->(node) | {type:r.type, level:r.level}] as inputs, [(node)-[r]->() | {type:r.type, level:r.level}] as outputs
WITH nodes, collect({node:node, inputs:apoc.coll.toSet(inputs), outputs:apoc.coll.toSet(outputs)}) as nodeData
UNWIND nodeData as start
UNWIND nodeData as end
WITH nodes, start, end, nodeData
WHERE start <> end
WITH nodes, start, end, apoc.coll.subtract(nodeData, [start, end]) as theRest
WITH nodes, start.node as start, end.node as end, apoc.coll.intersection(start.outputs, end.inputs) as possibles, [data in theRest | apoc.coll.intersection(data.inputs, data.outputs)] as others
WITH nodes, start, end, reduce(possibles = possibles, data in others | apoc.coll.intersection(possibles, data)) as possibles
WHERE size(possibles) > 0
UNWIND possibles as typeAndLevel
MATCH path = (start)-[*]->(end)
WHERE all(rel in relationships(path) WHERE rel.type = typeAndLevel.type AND rel.level = typeAndLevel.level) 
 AND length(path) >= size(nodes) - 1 
 AND all(node in nodes WHERE node in nodes(path))
RETURN nodes(path) as pathNodes, typeAndLevel.type as type, typeAndLevel.level as level
0 голосов
/ 08 декабря 2018

Замечания

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

  • Если это еще не сделано, введите в форму различные типы узловметок (Architecture, Software, SoftwareVersion и т. д.), которые значительно упростят поиск данных.
  • В зависимости от количества исходных баз данных domain_database_n и параллельных отношений SUPPORTSв результате использование нескольких меток для узла может быть более понятным и более производительным.В этом случае было бы излишним большее количество отношений.
  • Neo4j не фокусируется на поиске и фильтрации свойств отношений.Моделирование этих атрибутов как узла или свойства узла значительно более производительно, особенно для больших графов.
  • Примите во внимание Соглашения об именах Neo4j .

Ваш граф /исходная ситуация

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

CREATE
  (pc:UntypedNode {name: 'PC'})-[:SUPPORTS {type: 'domain_database_1'}]->(tenDotTwo:UntypedNode {name:'10.2'}),
  (pc)-[:SUPPORTS {type: 'domain_database_2'}]->(tenDotTwo),
  (pc)-[:SUPPORTS {type: 'domain_database_3'}]->(tenDotTwo),
  (pc)-[:SUPPORTS {type: 'domain_database_4'}]->(tenDotTwo),
  (pc)-[:SUPPORTS {type: 'domain_database_5'}]->(tenDotTwo),
  (pc)-[:SUPPORTS {type: 'domain_database_6'}]->(tenDotTwo),
  (pc)-[:SUPPORTS {type: 'domain_database_7'}]->(tenDotTwo),
  (tenDotTwo)-[:SUPPORTS {type: 'domain_database_1'}]->(linux:UntypedNode {name:'Linux'}),
  (tenDotTwo)-[:SUPPORTS {type: 'domain_database_2'}]->(linux),
  (tenDotTwo)-[:SUPPORTS {type: 'domain_database_3'}]->(linux),
  (tenDotTwo)-[:SUPPORTS {type: 'domain_database_4'}]->(linux),
  (tenDotTwo)-[:SUPPORTS {type: 'domain_database_5'}]->(linux),
  (tenDotTwo)-[:SUPPORTS {type: 'domain_database_6'}]->(linux),
  (tenDotTwo)-[:SUPPORTS {type: 'domain_database_7'}]->(linux),
  (linux)-[:SUPPORTS {type: 'domain_database_1'}]->(sevenDotZero:UntypedNode {name:'7.0'}),
  (linux)-[:SUPPORTS {type: 'domain_database_2'}]->(sevenDotZero),
  (linux)-[:SUPPORTS {type: 'domain_database_3'}]->(sevenDotZero),
  (linux)-[:SUPPORTS {type: 'domain_database_4'}]->(sevenDotZero),
  (linux)-[:SUPPORTS {type: 'domain_database_5'}]->(sevenDotZero),
  (linux)-[:SUPPORTS {type: 'domain_database_6'}]->(sevenDotZero),
  (linux)-[:SUPPORTS {type: 'domain_database_7'}]->(sevenDotZero),
  (sevenDotZero)-[:SUPPORTS {type: 'domain_database_1'}]->(x64:UntypedNode {name:'x64'}),
  (sevenDotZero)-[:SUPPORTS {type: 'domain_database_2'}]->(x64),
  (sevenDotZero)-[:SUPPORTS {type: 'domain_database_3'}]->(x64),
  (sevenDotZero)-[:SUPPORTS {type: 'domain_database_4'}]->(x64),
  (sevenDotZero)-[:SUPPORTS {type: 'domain_database_5'}]->(x64),
  (sevenDotZero)-[:SUPPORTS {type: 'domain_database_6'}]->(x64),
  (sevenDotZero)-[:SUPPORTS {type: 'domain_database_7'}]->(x64),
  (x64)-[:SUPPORTS {type: 'domain_database_1'}]->(sixtyFour:UntypedNode {name:'64'}),
  (x64)-[:SUPPORTS {type: 'domain_database_2'}]->(sixtyFour),
  (x64)-[:SUPPORTS {type: 'domain_database_3'}]->(sixtyFour),
  (x64)-[:SUPPORTS {type: 'domain_database_4'}]->(sixtyFour),
  (x64)-[:SUPPORTS {type: 'domain_database_5'}]->(sixtyFour),
  (x64)-[:SUPPORTS {type: 'domain_database_6'}]->(sixtyFour),
  (x64)-[:SUPPORTS {type: 'domain_database_7'}]->(sixtyFour),
  (sixtyFour)-[:SUPPORTS {type: 'domain_database_1'}]->(sqlServer:UntypedNode {name:'SQL Server'}),
  (sixtyFour)-[:SUPPORTS {type: 'domain_database_2'}]->(sqlServer),
  (sixtyFour)-[:SUPPORTS {type: 'domain_database_3'}]->(sqlServer),
  (sqlServer)-[:SUPPORTS {type: 'domain_database_1'}]->(year2014:UntypedNode {name:'2014'}),
  (sqlServer)-[:SUPPORTS {type: 'domain_database_2'}]->(year2016:UntypedNode {name:'2016'}),
  (sqlServer)-[:SUPPORTS {type: 'domain_database_3'}]->(year2017:UntypedNode {name:'2017'}),
  (year2014)-[:SUPPORTS {type: 'domain_database_1'}]->(s:UntypedNode {name:'S'}),
  (year2016)-[:SUPPORTS {type: 'domain_database_2'}]->(s),
  (year2017)-[:SUPPORTS {type: 'domain_database_3'}]->(s),
  (sixtyFour)-[:SUPPORTS {type: 'domain_database_4'}]->(oracle:UntypedNode {name:'Oracle'}),
  (sixtyFour)-[:SUPPORTS {type: 'domain_database_5'}]->(oracle),
  (sixtyFour)-[:SUPPORTS {type: 'domain_database_6'}]->(oracle),
  (sixtyFour)-[:SUPPORTS {type: 'domain_database_7'}]->(oracle),
  (oracle)-[:SUPPORTS {type: 'domain_database_4'}]->(release12c:UntypedNode {name:'12c'}),
  (oracle)-[:SUPPORTS {type: 'domain_database_5'}]->(release12gr2:UntypedNode {name:'12gR2'}),
  (oracle)-[:SUPPORTS {type: 'domain_database_6'}]->(release12cr:UntypedNode {name:'12cR'}),
  (oracle)-[:SUPPORTS {type: 'domain_database_7'}]->(release12cr1:UntypedNode {name:'12cR1'}),
  (release12c)-[:SUPPORTS {type: 'domain_database_4'}]->(s),
  (release12gr2)-[:SUPPORTS {type: 'domain_database_5'}]->(s),
  (release12cr)-[:SUPPORTS {type: 'domain_database_6'}]->(s),
  (release12cr1)-[:SUPPORTS {type: 'domain_database_7'}]->(s);

Решение

MATCH (n:UntypedNode)
  WHERE n.name IN $names
WITH collect(n) AS nodes
UNWIND nodes AS n
UNWIND nodes AS m
WITH *
  WHERE id(n) < id(m)
MATCH path = allShortestPaths((n)-[relation*..10]-(m))
  WHERE ALL(x IN relation
    WHERE x.type = $relationshipName)
WITH
  collect({ path: path, pathLength: length(path) }) AS data,
  max(length(path)) AS maxLength
WITH [row IN data WHERE row.pathLength = maxLength] AS rows
UNWIND rows AS row
RETURN row.path AS path;

с параметрами:

  • "имя-узла": ['Oracle', 'Linux', '10.2']
  • "имя-отношения": 'domain_database_4'

Объяснение:

  • строка 1-2:идентификация заданных начальных узлов
  • строка 3-5: создайте два набора отдельных строк для начальных узлов
  • строка 6-7: выберите только одно направление каждого отношения и исключите собственные ссылки на узлы
  • строка 8: вычислить все кратчайшие пути между обоими наборами строк начального узла до длины отношений 10
  • строка 9-10: отфильтровать все результирующие пути относительно отношений с указанным типом (параметр RelationshipName)
  • строка 11-15: фильтр для самого длинного «кратчайшего пути», пропуская пути деталей
  • строка 16: визуализация результирующего пути (путей)

Результат

╒═══════════════════════════════════════════════════════════╕
│"path"                                                     │
╞═══════════════════════════════════════════════════════════╡
│[{"name":"10.2"},{"type":"domain_database_4"},{"name":"Linu│
│x"},{"name":"Linux"},{"type":"domain_database_4"},{"name":"│
│7.0"},{"name":"7.0"},{"type":"domain_database_4"},{"name":"│
│x64"},{"name":"x64"},{"type":"domain_database_4"},{"name":"│
│64"},{"name":"64"},{"type":"domain_database_4"},{"name":"Or│
│acle"}]                                                    │
└───────────────────────────────────────────────────────────┘

graph

...