用于 Neo4j 的 Cypher 查询以获得所需的遍历

Posted

技术标签:

【中文标题】用于 Neo4j 的 Cypher 查询以获得所需的遍历【英文标题】:Cypher Query for neo4j to get the desired traversal 【发布时间】:2015-09-21 19:25:37 【问题描述】:

我正在修改问题以便可以轻松测试

用于测试的图表

绿色节点是组织,蓝色节点是个人。这是创建此图表的脚本:

CREATE (A:Organization PRID:'A', Name:'Organization-A')
CREATE (B:Organization PRID:'B', Name:'Organization-B')
CREATE (C:Organization PRID:'C', Name:'Organization-C')
CREATE (D:Organization PRID:'D', Name:'Organization-D')
CREATE (E:Organization PRID:'E', Name:'Organization-E')
CREATE (F:Organization PRID:'F', Name:'Organization-F')
CREATE (G:Organization PRID:'G', Name:'Organization-G')
CREATE (H:Organization PRID:'H', Name:'Organization-G')
CREATE (I:Organization PRID:'I', Name:'Organization-I')


CREATE (P1:Person PRID:'P1', Name:'Person-P1')
CREATE (P2:Person PRID:'P2', Name:'Person-P2')
CREATE (P3:Person PRID:'P3', Name:'Person-P3')
CREATE (P4:Person PRID:'P4', Name:'Person-P4')
CREATE (P5:Person PRID:'P5', Name:'Person-P5')
CREATE (P6:Person PRID:'P6', Name:'Person-P6')

CREATE
  (B)-[:CONTROL]->(A),
  (C)-[:CONTROL]->(A),
  (D)-[:CONTROL]->(C),
  (E)-[:CONTROL]->(C),
  (G)-[:CONTROL]->(F),
  (H)-[:CONTROL]->(F),

  (D)-[:EMPLOYS]->(P1),
  (P1)-[:SPOUSE]->(P2),
  (P2)-[:CONSULTS]->(E),

  (B)-[:EMPLOYS]->(P3),
  (P3)-[:SPOUSE]->(P4),
  (P4)-[:CONSULTS]->(I),

  (H)-[:EMPLOYS]->(P5),
  (P5)-[:SPOUSE]->(P6)
;

我正在尝试编写一个需要完成以下任务的密码查询:

a) 从 PRID = 'C' 的节点开始 b) 路径 p1 = 连接到具有关系类型 CONTROL(递归)的起始节点的所有节点 - 与方向无关 c) 路径 p2= 可选匹配以下关系模式 (x1)-[:EMPLOYS]->()-[:SPOUSE]->()-[:CONSULTS]->(x2) 其中 (x1) 和 (x2) 是路径 p1 中的节点 - 在步骤 (b) 中找到。

返回 p1 和 p2

到目前为止,已经尝试了以下三个查询(在 Brian 的帮助下)

Query1:
MATCH p1=(x:Organization PRID: 'C')-[r:CONTROL*]-(y)
OPTIONAL MATCH p2=(node1)-[:EMPLOYS]->()-[:SPOUSE]->()-[:CONSULTS]->(node2)
WHERE node1 in nodes(p1) and node2 in nodes(p1)
RETURN p1,p2;

Quesry2:
MATCH p1=(x:Organization PRID: 'C')-[r:CONTROL*]-(y)
WITH p1, nodes(p1) AS p1_nodes
UNWIND p1_nodes AS node1
UNWIND p1_nodes AS node2
WITH p1, node1, node2
OPTIONAL MATCH p2=(node1)-[:EMPLOYS]->()-[:SPOUSE]->()-[:CONSULTS]->(node2)    
WHERE p2 IS NOT NULL
RETURN p1, p2;

Query3:
MATCH p1=(x:Organization PRID: 'C')-[r:CONTROL*]-(y)
WITH p1, EXTRACT(node IN nodes(p1) | ID(node)) AS p1_node_ids
UNWIND p1_node_ids AS id1
UNWIND p1_node_ids AS id2
OPTIONAL MATCH p2=(node1)-[:EMPLOYS]->()-[:SPOUSE]->()-[:CONSULTS]->(node2)
WHERE ID(node1) = id1 AND ID(node2) = id2 AND p2 IS NOT NULL
RETURN p1, p2;

我期望的是用关系取回节点 A、B、C、D、E、P1、P2 的子图,但是所有三个都只给我 A、B、C、D、E 和关系(只是 p1,p2 没有)

我们尝试了更多查询,这些查询适用于某些锚节点,但不适用于第一层次结构中的任何节点

Query-4
MATCH p1=(x:Organization PRID: 'C')-[r:CONTROL*]-(y)
WITH collect(path: p1, node: y) AS paths_and_nodes
UNWIND paths_and_nodes AS paths_and_node1
UNWIND paths_and_nodes AS paths_and_node2
WITH
  paths_and_node1.node AS node1,
  paths_and_node2.node AS node2,
  paths_and_node1.path AS path1,
  paths_and_node2.path AS path2
OPTIONAL MATCH p2=(node1)-[:EMPLOYS]->()-[:SPOUSE]->()-[:CONSULTS]->(node2)
RETURN path1, path2, p2, node1, node2

这适用于 x 被指定为 A、B 或 C。但是如果 x 指向 D 或 E 则不起作用

Query 5
MATCH
  p1=(org1)-[:CONTROL*]-(x:Organization PRID: 'C')-[:CONTROL*]-(org2)
OPTIONAL MATCH p2=(org1)-[:EMPLOYS]->()-[:SPOUSE]->()-[:CONSULTS]->(org2)
RETURN p1, p2, org1, org2

这适用于 x 被指定为 C、D 或 E。但是如果 x 指向 A 或 B 则不起作用

一个想法 - 所以如果我们有查询

MATCH p1=(x:Organization PRID: 'E')-[r:CONTROL*]-(y)
OPTIONAL MATCH p2=(node1)-[:EMPLOYS]->()-[:SPOUSE]->()-[:CONSULTS]->(node2)
WHERE node1.PRID in ['A','B','C','D','E'] AND 
      node2.PRID in ['A','B','C','D','E']
RETURN p1,p2;

那么它显然可以正常工作。所以我们不能以某种方式使用 COLLECT 等创建这个数组并将其传递给下一个查询。问题似乎是 - 如果在第一场比赛之后我使用 WITH p1, COLLECT (y.PRID) AS p1_prids p1_prids 不是 ['A','B','C','D' ,'E'] 而是一个多行集合,每个集合只有一个元素

我可以让它始终如一地工作的一种方法是

MATCH (x:Organization PRID: 'C')-[r:CONTROL*0..]-(y)
WITH COLLECT (y.PRID) AS p1_prids
MATCH p1=(x:Organization PRID: 'C')-[r:CONTROL*0..]-(y)
WITH p1,p1_prids
OPTIONAL MATCH p2=(node1)-[:EMPLOYS]->()-[:SPOUSE]->()-[:CONSULTS]->(node2)
WHERE node1.PRID in p1_prids AND  node2.PRID in p1_prids

但我认为这是非常不优雅和性能噩梦,因为它会查询两次 - 所以仍在寻找解决方案......

我在这个查询中做错了什么? 有没有更好的方法来解决这个问题

提前致谢...

【问题讨论】:

【参考方案1】:

好的,让我为您的新数据集提供一个不同的答案(谢谢,顺便说一句,这真的很有帮助!)

我没有意识到的问题是,您想要匹配在一起的节点将位于 p1 路径的不同结果中,因为它们位于起始节点的任一侧。所以你可以这样做:

MATCH p1=(x:Organization PRID: 'C')-[r:CONTROL*]-(y)
WITH collect(path: p1, node: y) AS paths_and_nodes
UNWIND paths_and_nodes AS paths_and_node1
UNWIND paths_and_nodes AS paths_and_node2
WITH
  paths_and_node1.node AS node1,
  paths_and_node2.node AS node2,
  paths_and_node1.path AS path1,
  paths_and_node2.path AS path2
MATCH p2=(node1)-[:EMPLOYS]->()-[:SPOUSE]->()-[:CONSULTS]->(node2)
RETURN path1, path2, p2, node1, node2

或者像这样更简单的东西:

MATCH
  p1=(org1)-[:CONTROL*]-(x:Organization PRID: 'C')-[:CONTROL*]-(org2),
  p2=(org1)-[:EMPLOYS]->()-[:SPOUSE]->()-[:CONSULTS]->(org2)
RETURN p1, p2, org1, org2

编辑 所以我想我看到了问题所在。我没有考虑从任何Organization 节点开始。我很确定第二个查询永远不会起作用,因为 Cypher 永远不会在路径中两次命中同一个节点。因此,查看第一个查询,它不适用于 D 和 E 的原因是因为默认的 [*] 变量关系定义用于一个或多个跃点。如果我们让它是零个或多个跃点,那么它似乎可以工作:

MATCH p1=(x:Organization PRID: 'C')-[r:CONTROL*0..]-(y)
WITH collect(path: p1, node: y) AS paths_and_nodes
UNWIND paths_and_nodes AS paths_and_node1
UNWIND paths_and_nodes AS paths_and_node2
WITH
  paths_and_node1.node AS node1,
  paths_and_node2.node AS node2,
  paths_and_node1.path AS path1,
  paths_and_node2.path AS path2
OPTIONAL MATCH p2=(node1)-[:EMPLOYS]->()-[:SPOUSE]->()-[:CONSULTS]->(node2)
RETURN path1, path2, p2, node1, node2

怎么样?

【讨论】:

【参考方案2】:

让我试一试! ;)

所以首先你可以像这样简化你的第一个 MATCH/WHERE 组合:

MATCH p1=(x:Organization PID: '27762230')-[r:`10006`|`10010`*]-(y) 

让我们接受它并尝试做你想做的事:

MATCH p1=(x:Organization PID: '27762230')-[r:`10006`|`10010`*]-(y) 
WITH p1, nodes(p1) AS p1_nodes
UNWIND p1_nodes AS node1
UNWIND p1_nodes AS node2
WITH p1, node1, node2
OPTIONAL MATCH p2=(node1)-[:`10004`]->()-[:`10051`]->()-[:`10052`]->(node2)
WHERE p2 IS NOT NULL
RETURN p1, p2

也可能是当您调用nodes(path) 时,您获得的对象不是节点属性的Maps 那么多。如果是这样,我们应该能够通过 ID 进行匹配:

MATCH p1=(x:Organization PID: '27762230')-[r:`10006`|`10010`*]-(y) 
WITH p1, EXTRACT(node IN nodes(p1) | ID(node)) AS p1_node_ids
UNWIND p1_node_ids AS id1
UNWIND p1_node_ids AS id2
OPTIONAL MATCH p2=(node1)-[:`10004`]->()-[:`10051`]->()-[:`10052`]->(node2)
WHERE ID(node1) = id1 AND ID(node2) = id2 AND p2 IS NOT NULL
RETURN p1, p2

【讨论】:

感谢您的建议,但它对我不起作用。有几件事(a)它在 WHERE 处给出了一个语法错误——我认为你放它是为了避免路径 p2 循环回同一个节点——这实际上不是我需要的——所以我删除了它。然而,在删除它之后 - 我没有得到任何结果。然后我将第二个 MATCH 更改为一个 OPTINAL MATCH - 之后它返回子图 p1 但不返回 p2 - 即使在我拥有的数据中我确实有满足该条件的数据...... 我在测试中注意到的另一件事 - 所以在这个路径 p2 的查询中,我们试图将 p2 的起始节点和结束节点限制在 p1 中找到的节点集 - 我观察到的是,如果您一次只限制一个 - 也就是说,如果我们将 node1 更改为 xxx 或我们将 node2 更改为 xxx,则在您上面的查询中,如果我们将 node1 更改为 xxx,它会在结果中显示 p1 和 p2 ...但这显然不是我们想要什么 - 因为我们的要求是将起始节点和结束节点都限制在 p1 中找到的节点集 ... 关于WHEREMATCH 的优点。我已经编辑以反映这些。我不太清楚为什么OPTIONAL MATCH 不起作用。我已经编辑了一些建议 再次感谢您的建议布赖恩,不幸的是仍然不行,我正在使用一个简单的 Cypher 脚本编辑上面的帖子来测试这个查询,以便您/某人可以轻松尝试... 感谢您的所有帮助,我尝试了您提供的查询 - 它们适用于某些条件但并非适用于所有条件,因为很难将查询放入 cmets 我正在使用结果更新我的问题您在上一个答案中提供的查询...

以上是关于用于 Neo4j 的 Cypher 查询以获得所需的遍历的主要内容,如果未能解决你的问题,请参考以下文章

如何使用Cypher只在Neo4j中获得朋友的朋友

编写不区分大小写的Cypher查询以匹配Neo4j中字符串的开头

Neo4j 第九篇:查询数据(Match)

Neo4j Cypher - 有条件的 With 子句查询

Neo4j 第三篇:Cypher查询入门

Neo4j - Cypher vs Gremlin 查询语言