为什么我会收到 "Cartesian Product" 警告?

Why do I get a "Cartesian Product" warning?

我仍在尝试理解为什么我在 neo4j 中收到针对特定格式查询的笛卡尔乘积警告,而不是其他格式。这就是我设置数据库的方式:

CREATE (q:Form {version: "1.0"})
CREATE (q:Question {text: "Sector de la empresa", active: true})

然后我尝试了以下查询:

MATCH
(f:Form {version: "1.0"}),
(q:Question {text: "Sector de la empresa"})
CREATE (f)-[:asks]->(q)
RETURN f, q

但是,我收到以下警告:

This query builds a cartesian product between disconnected patterns.
If a part of a query contains multiple disconnected patterns,
this will build a cartesian product between all those parts.
This may produce a large amount of data and slow down query processing.
While occasionally intended, it may often be possible to reformulate the
query that avoids the use of this cross product, perhaps by adding a
relationship between the different parts or by using OPTIONAL MATCH
(identifier is: (q))

当我使用以下查询时,它没有给我这个警告:

MATCH (f:Form {version: "1.0"})
WITH f
(q:Question {text: "Sector de la empresa"})
CREATE (f)-[:asks]->(q)
RETURN f, q

也不是当我使用这个查询时:

MATCH (f:Form {version: "1.0"})
MATCH (q:Question {text: "Sector de la empresa"})
CREATE (f)-[:asks]->(q)
RETURN f, q

我使用以下这篇文章作为资源,但它仍然没有完全回答我的问题:

为什么我得到的是某些格式的查询的笛卡尔积而不是其他格式的?另外,我不完全理解笛卡尔积警告是什么。

如果您MATCH使用两个不同的标签,但它们之间没有任何关系,那么您会收到此警告。原因是因为如果你这样做:

MATCH (a:Foo), (b:Bar)

Neo4j 的工作是找到这两个节点的所有可能组合。因此,对于 a 的第一场比赛,b 的每场比赛都会 return 一行,对于 a 的第二场比赛,它将再次 return 一行对于 b 的每场比赛,依此类推。因此,您将在结果中获得 (number of Foo nodes) x (number of Bar nodes) 行。随着数据库的增长,这对性能非常不利。

我可以看到您在 version 上过滤 Form,在 text 上过滤 Question,这会有所帮助。这甚至可能只给你一个 Form 节点和一个 Question 节点。因此,只要您在 Form(version)Question(text) 上有索引,查询就应该非常快。 Neo4j 不能告诉(或者至少,目前还没有实现能够告诉)有多少行将被 returned,所以它会发出警告,说你的查询可能会很慢。

他们都是笛卡尔

读完你的问题,我的密码世界瞬间崩溃了——所有三个查询都应该涉及笛卡尔积。

检查过(在 the console 和本地数据库上 - 都是 3.3.0 版本),结果证明我很正常 - 它们都涉及笛卡尔积:

为什么在第一种情况下只有一个警告(仍然是 3.3.0 版,我不知道 - 你只需要 运行 规划器来解决这个问题,如果它没有触发警告是什么?一些愚蠢的密码逻辑?

密码基础知识

Cypher 查询由多个部分组成,每个部分都可以更新(写入)或读取。

就阅读部分而言,情况是这样的:

  • Neo4j 选择了一个 starting point,它估计会产生最少的 'hits'。它将通过这些点击中的每一个...
  • ...使用 node/relationship 模式遍历图形。
  • 它会重复执行直到没有更多的模式匹配。

如果你遇到这样的事情:

(a {name:'Bill'})-->(b:Dog)

该计划可能看起来像这样。

  • 对于每个节点(又名 AllNodeScan):
    • 根据谓词过滤 (name == 'bill')
    • 获取所有传出 --> 关系
    • 对于每个关系:
      • 获取结束节点
      • 基于谓词过滤 (:Dog)

重要的是,在找到 (a) 的同时,我们需要扫描每个节点。但是我们只是简单地遍历图来找到 (b)s - 后者没有 AllNodeScan

(有 AllNodeScan 的变体,见 Starting Node Operators

当您的查询是这样的:

MATCH (f:Form {version: "1.0"}), (q:Question {text: "Sector de la empresa"})

Neo 被迫为 fq 执行 AllNodeScan - 它们之间没有可遍历的模式。这可能会创建一个 f * q 大小的结果集,这可能会很大。