密码查询以找到多个节点之间的所有关系

时间:2018-12-08 16:01:53

标签: neo4j cypher neo4j-apoc

我有一个复杂的图形,如下图所示:

enter image description here

这里每个关系都有一个类型值。我需要编写一个密码查询来查找给定节点集(两个或更多)之间的所有关系(及其类型值)。可以按任何顺序输入节点,例如x64-> Linux-> Oracle或Oracle-> Linux-> 10.2。

编辑

我期望这样的输出。带有链接名称的关系名称的所有节点组合。

  1. 对于输入:x64-> Linux-> Oracle

enter image description here

  1. 用于输入:Linux-> 64-> Oracle-> 12c

enter image description here

数据

可以从https://www.dropbox.com/s/w28omkdrgmhv7ud/Neo4j%20Queries.txt?dl=0

访问数据

编辑 输入x64-> Linux-> Oracle

的新输出格式

enter image description here

2 个答案:

答案 0 :(得分:1)

备注

在提出解决方案和结果之前,我想建议您对模型进行修订。

  • 如果尚未完成,请以标签形式(1ViewArchitecture等)引入不同的节点类型,这将使​​您的数据检索更加容易。
  • li>
  • 取决于源数据库Software的数量以及由此产生的并行SoftwareVersion关系,here的使用可能更清晰,性能更高。在这种情况下,大量的关系将是多余的。
  • Neo4j并不专注于搜索和过滤关系属性。将这些属性建模为节点或节点属性的性能明显更高,尤其是对于大型图形。
  • 考虑multiple labels for a node

您的图表/初始情况

为便于进一步解答和解决方案,我注意到了我的图形创建语句:

domain_database_n

解决方案

SUPPORTS

带有参数:

  • “ nodeNames”: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);
  • “ relationshipName”: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;

说明:

  • 第1-2行:标识给定的起始节点
  • 3-5行:为起始节点创建两组单独的行
  • 第6-7行:每种关系仅取一个方向,并排除节点自引用
  • 第8行:计算两组起始节点行之间的所有最短路径,直到长度为10个关系
  • 第9-10行:过滤所有与指定类型的关系有关的结果路径(参数RelationshipName)
  • 11-15行:过滤最长的“最短路径”,省略零件路径
  • 第16行:呈现结果路径

结果

['Oracle', 'Linux', '10.2']

Neo4j naming conventions

答案 1 :(得分:1)

假设您仅在查找直接连接集合中每对节点的关系(而不是查找集合中节点对之间的所有多跳路径),APOC Procedures为此apoc.algo.cover()用例:

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

编辑

正如我在评论中提到的那样,您对要求的更改将大大改变问题的性质。

您似乎想要完整的路径结果(定向),包括不在您输入中的节点,并且想要确保路径中的所有关系都具有相同的type属性。

这要求我们找到那些节点的顺序,以便我们可以确定所有节点之间的路径。虽然我们可以找到输入节点的所有可能排列(按路径的遍历顺序),但我认为只要找到起始节点和结束节点的排列2(可以取消对集合的两次缠绕并删除其中的行),我们就可以摆脱困境。起始节点和结束节点相同)。我们首先要找到所有的输入和输出关系类型,以便我们可以使用一些设置操作(起始节点的输出类型与结束节点的输入类型相交,结束节点的输入类型与其他节点的所有(相交的)输入和输出类型相交节点)以查找可能存在于可以连接所有节点的关系中的潜在类型。

在此过滤之后的其余行中,我们可以匹配可变长度路径,该路径可以连接所有这些节点,仅使用提供的类型,以便每个路径仅遍历遍历具有单个类型的关系。然后我们进行过滤以确保所有输入节点都在路径中。

我们假设节点的类型为:Node,其属性为“名称”。

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

要同时处理类型和级别,我们需要在查询中较早地收集它们两者,因此,我们不仅要处理类型,还要处理类型和级别的映射。这确实使查询有些复杂,但是必须确保所提供的路径对于路径中的所有关系具有相同的类型和级别。

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