【问题标题】:Cypher - Neo4j Query ProfilingCypher - Neo4j 查询分析
【发布时间】:2017-12-24 14:06:26
【问题描述】:

我对 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;

输出是:

所以根据Neo4j's Documentation

3.7.2.2。展开成

当开始和结束节点都已经找到时,展开到 用于查找两个节点之间的所有连接关系。

查询。

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 db 命中,尽管所有节点/关系都已检索到?为什么这个操作只需要 6 行的 12 db 调用?

已编辑

这是我要查询的完整图表:

我也测试了上述相同查询的不同版本,但返回相同的查询配置文件结果:

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 子句而不是一个带逗号的子句?
  • @DomWeldon 请检查更新的问题

标签: neo4j cypher


【解决方案1】:

为什么会这样?

此行为似乎与查询规划器如何执行数据库搜索以响应您的密码查询有关。 Cypher 提供了在图中搜索和执行操作的接口(替代方案包括 Java API 等),查询由查询计划器处理,然后由 neo4j 内部转换为图操作。查询规划器会找到可能是搜索图的最有效方法是有道理的(因此我们为什么喜欢 neo),因此仅仅因为密码查询是用一种方式编写的,它不一定会搜索图就像我们想象的那样。

这方面的文档似乎有点稀疏(或者,我无法正确找到它),任何链接或进一步的解释将不胜感激。

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

“使用mobileNumber索引找到两个节点,每个节点都有:Consumer标签,n和m,联系号码分别为x和y。如果找到它们,请尝试找到-[:HAS_CONTACT]->关系从nm。如果找到关系,则返回两个节点和关系,否则不返回任何内容。"

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

而不是这样做,因为必须满足 MATCH 子句才能继续查询,neo 知道如果查询必须通过 -[:HAS_CONTACT]-> 关系连接两个节点 nm是回报任何东西。因此,运行查询(并避免使用笛卡尔积)的最有效方法如下,您的查询可以简化为。

“找到一个带有:Consumer 标签的节点n,以及索引mobileNumber 的值x,它通过-[:HAS_CONTACT]-> 关系连接到带有:Consumer 的节点m标签,其属性为 y 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 的第二个查询时,它会显示一条错误消息:“不应像这样使用聚合。”
【解决方案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 用于查找两个节点之间的所有连接关系。”所以我认为开始和结束节点是否相同并不重要,只是你必须在扩展之前找到它们,不是吗?
  • 我也读过。我只能期待它被误解了。如果您创建一个支持查询的数据集,如示例(即循环),它将使用 Expand Into
猜你喜欢
  • 1970-01-01
  • 2015-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-04
  • 2021-10-30
相关资源
最近更新 更多