查询结果不一致取决于neo4j中的查询顺序

Inconsistent query results depending on the order of the query in neo4j

[图片 1][1]

根据我查询 2 个关系的顺序,尽管查询相同(据我了解),但我得到了 2 个不同的答案。查询显然不一样,但我不知道为什么。

MATCH p1=(:Barrier {code: 'B2'})-[:REL1]->()
WITH count(DISTINCT p1) AS failed_B2
MATCH p2=(:Barrier {code: 'B2'})-[:REL2]->()
RETURN count(DISTINCT p2) AS worked_B2, failed_B2

Returns 1 和 0 - 这是正确的

但反过来说:

MATCH p1=(:Barrier {code: 'B2'})-[:REL2]->()
WITH count(DISTINCT p1) AS failed_B2
MATCH p2=(:Barrier {code: 'B2'})-[:REL1]->()
RETURN count(DISTINCT p2) AS worked_B2, failed_B2

Returns 0 和 0 - 这是不正确的

我想合并多个查询的结果,但 UNION 不起作用,因为它需要将结果分组在同一列下,这在我的情况下是不正确的。我需要不同列中的结果。

所以这是一件有趣的事情,它围绕着当行被过滤掉时发生的事情(例如当 MATCH 失败或 WHERE 条件过滤行时)。

但首先我们需要解决您在第二种情况下观察到的问题:Returns 0 and 0。我不认为那是真的,我想知道您在这里使用的是哪个版本的 Neo4j。在这种特殊情况下,我本来希望没有行被 returned,这与行被 returned 完全不同,两者都是 0 值。

当 Cypher 查询执行时,它们会建立数据记录(或行)。并且每行执行 Cypher 操作。因此,当您在查询的某个点执行 MATCH 时,这是按行执行的,并且当 MATCH 失败时,当不存在此类模式时(如果存在,则遵循您的 WHERE 子句),则该行将被过滤掉。这很重要,因为这意味着该记录中的任何其他数据都已消失且无法再寻址。

要记住的第二件事是,我们允许某些聚合,例如 count()collect() 执行 ,即使没有行存在 ,因为可以想象,您可能有一个没有任何匹配项的查询,并且获得 0 计数(或收集时的空集合)是一个完全有效的情况,应该被允许。在这些情况下,在 MATCH 或过滤器之后可能根本没有留下任何行(并且由于没有行,其他任何东西都无法执行,因为每行执行 Cypher 操作,所以它是 no-op 如果没有行),count()collect() 会导致出现一个新行,该行的计数为 0,或者是空集合。由于现在有一行,查询中的其余运算符可以执行某些操作,查询可以继续执行。

这是第一种情况,模式 p1 不存在,但模式 p2 存在(一次)。这是发生的事情的细目:

  1. 第一个匹配项没有找到任何内容。行变为 0。没有任何东西可以执行后续操作。
  2. 您执行独立的 count() 聚合(范围内没有其他变量,这很重要)。这会发出计数为 0 的单行,这是正确的:图中没有出现该模式。
  3. 你执行第二个 MATCH,有一个 record/row 供它执行(值为 {failed_B2:0}),它找到单个事件,并得到它的计数 (1) , 并且能够输出预期的答案 (1, 0), 其中 1 是查询末尾模式匹配的计数, p2, 0 是前两行模式匹配的计数查询,p1.

现在让我们看看当我们反转这个时会发生什么。

在您的第二个查询中,现在模式 p1 在图表中存在一次,而模式 p2 不存在。这是发生的故障:

  1. 第一个 MATCH 成功并找到了模式。
  2. 您得到找到的模式数: 1. 您现在有一个 record/row,其值为 {failed_B2:1}
  3. 您执行了第二个 MATCH,但未找到模式。 record/row 被过滤掉了。您现在没有 records/rows,因此不仅没有任何可操作的东西,而且之前在 record/row 中的任何内容都消失了。没有 failed_B2 值可供参考。
  4. 您尝试获取 p2failed_B2 的计数。但是 Cypher 不允许这样做,我们只允许在独立的 count() 或 collect() 时跨 0 行聚合,没有 failed_B2 可引用,当 record/row 时它被清除了包含它的被过滤掉了。没有办法理智地处理它,因为以前存在的数据不存在(这是正确的行为)。查询 应该 没有行 return...这与 0, 0 不同,因为这意味着您得到了行 returned (这就是为什么我有兴趣与您澄清这一点)。

至于你应该如何正确执行它,当你必须像这样聚合并且你知道某些模式可能不存在时,请改用可选匹配。

当您进行可选匹配时,如果未找到匹配项,它不会过滤掉该行。相反,模式中新引入的变量变为空值,当您对空值进行 count() 或 collect() 时,它会忽略它们,为您提供正确的计数 0,但不会清除包含 record/row 的 failed_B2 值你也要return 结尾。