如何让Neo4j以优化的方式考虑周期

时间:2017-02-18 05:18:26

标签: neo4j cypher

这是一个后续问题:

How to ask Neo4j to take cycles into account

在我之前的问题中,@ stdob--帮助我找到了以下查询:

MATCH (n1:S)
OPTIONAL MATCH (n1)-[:R]->(n2:R)<-[:R]-(n3:E)
OPTIONAL MATCH (n3t)-[:R]->(n4:R:L)
WHERE n3t = n3
RETURN labels(n1), labels(n3t), labels(n4);

以上查询可替代以下内容:

MATCH (n1:S)
OPTIONAL MATCH (n1)-[:R]->(n2:R)<-[:R]-(n3:E)-[:R]->(n4:R:L)
RETURN labels(n1), labels(n3t), labels(n4);

我必须使用第一个,因为在我的数据中,n2n4可能是相同的节点,并且由于Neo4j拒绝两次使用同一节点,因此它将返回null。

虽然第一个查询有效且有效,但它的性能非常糟糕。它强制数据库重新开始搜索整个数据,最后,它将使用n3t = n3匹配所选节点。只是为了给你一个关于其性能有多糟糕的提示,在一个200k幅度的数据集上,返回结果需要5秒,而如果我省略第二个OPTIONAL MATCH并且它的WHERE结果生成了在同一查询中不到10毫秒。如果有人感兴趣,这是查询的执行计划:

enter image description here

正确的分支是我之前提到的部分(我试图欺骗Neo4j第二次接受一个节点)。正如您所看到的那样,为了使Neo4j第二次占用一个节点,会发生2M db命中。此执行计划的实际查询是:

PROFILE MATCH (n5_1:Revision:`Account`)<-[:RevisionOf]-(n5_2:Entity:`Account`) 
WITH n5_2, n5_1
ORDER BY n5_1.customer_number ASC
LIMIT 100
OPTIONAL MATCH (n5_1)-[:`Main Contact`]->(n4_1:Wrapper)<-[:Wrapper]-(:Revision:`Contact`)<-[:RevisionOf]-(n4_2:Entity:`Contact`) 
OPTIONAL MATCH (n4_4)-[:RevisionOf]->(n4_3:Revision:Latest:`Contact`:Active) 
WHERE (n4_2) = (n4_4) 
RETURN n5_1, n5_2, n4_1, n4_2, n4_3

所以我的问题是,如何编写一个Cypher查询,其中第二次获取节点,而性能不会受到影响?

对于一些示例数据和测试平台,请转到另一个问题。

2 个答案:

答案 0 :(得分:1)

您可以尝试另外收集尾巴:

PROFILE 
MATCH (n1:S)
OPTIONAL MATCH (n1)-[:R]->(n2:R)<-[:R]-(n3:E)
WITH n1, [null] + ( (n3)-[:R]->(:R:L) ) as tail
WITH n1, tail, size(tail) as tailSize
UNWIND tail as t
WITH n1, tailSize, t WHERE (tailSize = 2 AND NOT t is NULL) OR tailSize = 1
WITH n1, nodes(t) as nds
WITH n1, nds[0] as n3t, nds[1] as n4
RETURN labels(n1), labels(n3t), labels(n4) 

答案 1 :(得分:1)

我在你的链接问题上发布了一个应该给你描述的结果表的答案。如果这符合您的要求,此查询使用相同的方法,可能是此问题的解决方案:

PROFILE MATCH (n5_1:Revision:`Account`)<-[:RevisionOf]-(n5_2:Entity:`Account`) 
WITH n5_2, n5_1
ORDER BY n5_1.customer_number ASC
LIMIT 100
OPTIONAL MATCH (n5_1)-[:`Main Contact`]->(n4_1:Wrapper)<-[:Wrapper]-(:Revision:`Contact`)<-[:RevisionOf]-(n4_2:Entity:`Contact`)
WHERE (n4_2)-[:RevisionOf]->(:Revision:Latest:`Contact`:Active)
OPTIONAL MATCH (n4_2)-[:RevisionOf]->(n4_3:Revision:Latest:`Contact`:Active) 
RETURN n5_1, n5_2, n4_1, n4_2, n4_3

这使n4_2保持在上一个可选择的匹配中,这可以解决您观察到的性能问题。

正如您在上一个问题中所提到的,您希望避免第一个OPTIONAL MATCH成功的情况,但第二个失败,当您不希望它们出现时,将变量保留为第一个OPTIONAL MATCH中的非空值。

我们通过在第一个OPTIONAL MATCH之后添加一个WHERE来解决这个问题,只有当你在第二个OPTIONAL MATCH中寻找的模式存在于最后一个节点之外时才强制匹配成功(即使这样,这也会有效) pattern重用了OPTIONAL MATCH中的关系和节点。