Cypher - Neo4j查询性能分析

3
我可以帮您翻译成中文。以下是需要翻译的内容:

我对Neo4j的查询分析有一些问题。 考虑以下简单的Cypher查询:

PROFILE 
MATCH (n:Consumer {mobileNumber: "yyyyyyyyy"}),
      (m:Consumer {mobileNumber: "xxxxxxxxxxx"}) 
WITH n,m 
MATCH (n)-[r:HAS_CONTACT]->(m) 
RETURN n,m,r;

输出结果为:

enter image description here

根据Neo4j的文档,如下所示:

3.7.2.2. Expand Into

When both the start and end node have already been found, expand-into is used to find all connecting relationships between the two nodes.

Query.

MATCH (p:Person { name: 'me' })-[:FRIENDS_WITH]->(fof)-->(p) RETURN
> fof
在上面的查询中(在我的情况下),首先它应该找到StartNode和EndNode,然后再查找任何关系。但不幸的是,它只找到了StartNode,然后展开了所有连接的:HAS_CONTACT关系,这导致没有使用"Expand Into"操作符。为什么会这样呢?两个节点之间只有一个:HAS_CONTACT关系。在:Consumer{mobileNumber}上有一个唯一索引约束。为什么上述查询会展开所有7个关系?
另一个问题是关于Filter操作符:为什么它需要12个数据库访问,尽管所有节点/关系已经被检索到了?为什么这个操作需要12个数据库调用才能得到6行结果?
我正在查询的是完整的图形: Graph Data 此外,我已经测试了同一查询的不同版本,但返回的是相同的查询概要结果。

1

PROFILE
 MATCH (n:Consumer{mobileNumber: "yyyyyyyyy"})
 MATCH (m:Consumer{mobileNumber: "xxxxxxxxxxx"}) 
 WITH n,m 
 MATCH (n)-[r:HAS_CONTACT]->(m) 
 RETURN n,m,r;

2

PROFILE
 MATCH (n:Consumer{mobileNumber: "yyyyyyyyy"}), (m:Consumer{mobileNumber: "xxxxxxxxxxx"}) 
 WITH n,m 
 MATCH (n)-[r:HAS_CONTACT]->(m) 
 RETURN n,m,r;

3

PROFILE 
MATCH (n:Consumer{mobileNumber: "yyyyyyyyy"}) 
WITH n 
MATCH (n)-[r:HAS_CONTACT]->(m:Consumer{mobileNumber: "xxxxxxxxxxx"}) 
RETURN n,m,r;

你能否在这里发布一些数据,以便我们更好地理解你的图形?这可能是由于查询规划器试图减少通过生成笛卡尔积所创建的负载(即,在第一个匹配子句中)。你是否尝试过将查询的第一部分更改为使用两个MATCH子句,而不是一个带有逗号的子句? - Dom Weldon
@DomWeldon 请检查更新后的问题。 - Afridi
2个回答

2

为什么会这样?

看起来这种行为与查询规划器如何响应您的Cypher查询中的数据库搜索有关。 Cypher提供了一个接口来搜索和执行图形操作(其他选择包括Java API等),查询由查询规划器处理,然后由neo4j的内部转换为图形操作。查询规划器将找到最有效地搜索图形的方法(因此我们喜欢neo),因此仅仅因为Cypher查询以某种方式编写,它并不一定会按照我们想象中的方式搜索图形。

对此的文档似乎有点稀少(或者说我无法正确找到它),如果有任何链接或进一步的解释,将不胜感激。

检查您的查询,我认为您想表达的是:

“使用mobileNumber索引找到两个具有:Consumer标签的节点n和m,其联系电话分别为x和y。如果找到它们,请尝试从nm查找一个-[:HAS_CONTACT]->关系。如果找到关系,则返回两个节点和关系,否则返回空。”

以这种方式运行此查询需要创建笛卡尔积(即所有nm组合的小表格 - 在这种情况下只有一行 - 但对于其他查询可能会有更多),然后在每个这些行之间搜索关系。

与其这样做,由于必须满足MATCH子句才能继续查询,因此neo知道如果要查询返回任何内容,则两个节点nm必须通过-[:HAS_CONTACT]->关系相连。因此,运行查询的最有效方法(并避免笛卡尔积)如下所示,这是您的查询可以简化为的内容。

“使用索引mobileNumber找到具有:Consumer标签的值为x的节点n,它通过-[:HAS_CONTACT]->关系连接到具有:Consumer标签的值为y的节点m的属性mobileNumber。返回两个节点和关系,否则返回空。”

因此,与其执行两次索引搜索、一个笛卡尔积和一组展开操作,neo只执行一次索引搜索、一个展开所有和一个过滤器。

您可以通过查询分析器中AUTOSTRING参数的存在来查看此简化的结果。

如何更改查询以实现所需的搜索

如果您想更改查询以便必须使用扩展关系,可以将关系要求设置为可选,或者使用显式迭代执行。以下这两个查询都会产生最初预期的查询剖面。

可选示例:

PROFILE
 MATCH (n:Consumer{mobileNumber: "xxx"})
 MATCH (m:Consumer{mobileNumber: "yyy"}) 
 WITH n,m 
 OPTIONAL MATCH (n)-[r:HAS_CONTACT]->(m) 
 RETURN n,m,r;

迭代示例:

PROFILE
 MATCH (n1:Consumer{mobileNumber: "xxx"})
 MATCH (m:Consumer{mobileNumber: "yyy"}) 
 UNWIND COLLECT(n1) AS n
 MATCH (n)-[r:HAS_CONTACT]->(m) 
 RETURN n,m,r;

谢谢解释。但是我不能使用 OPTIONAL MATCH,因为我只想在两个节点之间存在给定关系的情况下返回 endNode,并且想要在扩展所有关系之前找到 endNode,以摆脱不需要的关系/节点并减少数据库访问次数。此外,当使用第二个带 UNWIND 的查询时,它会显示错误,说:“不应该像这样使用聚合。” - Afridi

2
您正在执行的查询与Neo4j文档中关于“扩展到”(Expand Into)的示例不同。示例查询从同一节点开始和结束。
如果您希望规划程序先找到两个节点并查看它们之间是否存在关系,则可以使用长度为1的 shortestPath 来最小化DB访问次数。
PROFILE 
MATCH (n:Consumer {mobileNumber: "yyyyyyyyy"}),
  (m:Consumer {mobileNumber: "xxxxxxxxxxx"}) 
WITH n,m 
MATCH Path=shortestPath((n)-[r:HAS_CONTACT*1]->(m))
RETURN n,m,r;

谢谢你的回答。就“Expand Into”而言,根据文档所述:“当起始节点和结束节点都已找到时,使用expand-into来查找两个节点之间的所有连接关系。”所以我认为无论起始节点和结束节点是否相同,只要在扩展之前找到它们就可以了,对吗? - Afridi
我也读到了这个。我只能期望它被误解了。如果你创建了一个支持查询的数据集,就像例子中的循环一样,它会使用Expand Into - Dave Bennett

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接