【问题标题】:How to ask Neo4j to take cycles into account in an optimised way如何让 Neo4j 以优化的方式考虑周期
【发布时间】:2017-02-18 05:18:26
【问题描述】:

这是一个后续问题:

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相同查询的毫秒数。如果有人感兴趣,这里是查询的执行计划:

右分支是我前面提到的部分(我试图欺骗Neo4j第二次取一个节点)。如您所见,为了使 Neo4j 第二次获取节点,产生了 2M 的数据库命中。这个执行计划的实际查询是:

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 查询,其中第二次获取一个节点而性能不会受到影响?

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

【问题讨论】:

  • 您是否考虑过为此编写自定义程序?您可能希望查看 APOC 的 PathExplorer 的代码作为起点。这将使您能够更好地控制遍历唯一性,允许您在遍历期间重用边。
  • 您的最后一个 OPTIONAL MATCH 有点奇怪,因为您在这里使用了一个新变量 n4_4,但是您在说它必须等于 n4_2 之后有一个 where 子句。此可选匹配是您的性能下降的原因。如果你在这场比赛中使用n4_2,它应该可以工作。但是,在您的链接问题中,这产生了一些不良结果,但看起来应该很容易过滤您必须确保 n_43 不为空的结果。

标签: neo4j cypher


【解决方案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 中查找的模式存在于最后一个节点之外时才强制匹配成功(即使这样模式重用了 OPTIONAL MATCH 中的关系和节点)。

【讨论】:

  • 谢谢,这个解决方案很完美。
  • 很高兴听到这个消息!当你有机会时,你能接受这个答案吗?
  • 对不起,我以为我做到了
【解决方案2】:

你可以尝试额外收集尾巴:

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) 

【讨论】:

    猜你喜欢
    • 2021-05-27
    • 1970-01-01
    • 2012-06-21
    • 2011-08-25
    • 1970-01-01
    • 2019-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多