对多个子句使用匹配会导致奇怪的结果

Using Match with Multiple Clauses Causes Odd Results

我正在 Neo4j 2.0.4 中编写一个 Cypher 查询,试图获取所选节点的入站和出站关系总数。当我一次只使用一个节点查询时,我可以很容易地做到这一点,就像这样:

MATCH (g1:someIndex{name:"name1"})
MATCH g1-[r1]-()
RETURN count(r1);
//Returns 305

MATCH (g2:someIndex{name:"name2"})
MATCH g2-[r2]-()
RETURN count(r2);
//Returns 2334

但是当我尝试 运行 将 2 个节点放在一起进行查询时(即获取 g1 和 g2 的关系总数),我似乎得到了一个 奇怪的 结果。

MATCH (g1:someIndex{name:"name1"}), (g2:someIndex{name:"name2"})
MATCH g1-[r1]-(), g2-[r2]-()
RETURN count(r1)+count(r2);
//Returns 1423740

由于某种原因,这个数字比 305+2334 的总数要大得多。

似乎其他 Neo4j 用户在使用多个 MATCH 子句时遇到了 运行 奇怪的问题,所以我通读了 Michael Hunger 在 https://groups.google.com/d/msg/neo4j/7ePLU8y93h8/8jpuopsFEFsJ 的解释,它建议 Neo4j 用户使用管道使用 WITH 避免 "identifier uniqueness" 的一场比赛的结果。但是,当我 运行 以下查询时,它只是超时:

MATCH (g1:gene{name:"SV422_HUMAN"}),(g2:gene{name:"BRCA1_HUMAN"})
MATCH g1-[r1]-()
WITH r1
MATCH g2-[r2]-()
RETURN count(r1)+count(r2);

我怀疑此查询没有 return,因为有很多记录 return 由 r1 编辑。在这种情况下,我将如何在 2 个节点上操作我的 "get-number-of-relationships" 查询?我只是使用了一些不正确的语法,还是我的“一次 2 个节点”查询的逻辑存在一些基本问题?

你的第一个问题是当你这样做时你返回的是笛卡尔积:

MATCH (g1:someIndex{name:"name1"}), (g2:someIndex{name:"name2"})
MATCH g1-[r1]-(), g2-[r2]-()
RETURN count(r1)+count(r2);

如果 r1 有 305 个实例,r2 有 2334 个实例,您将返回 (305 * 2334) == 711870 行,因为您要对这个 (count(r1)+count(r2)) 你总共得到 711870 + 711870 == 1423740.

你的第二个问题是你没有在这个查询的 WITH 子句中继承 g2:

MATCH (g1:gene{name:"SV422_HUMAN"}),(g2:gene{name:"BRCA1_HUMAN"})
MATCH g1-[r1]-()
WITH r1
MATCH g2-[r2]-()
RETURN count(r1)+count(r2);

您在第一个 MATCH 子句中匹配 g2,但是当您仅在第 3 行的 WITH 子句中保留 r1 时,您将其留在后面. 然后,在第 4 行中,当你在 g2-[r2]-() 上进行匹配时,你实际上是在匹配图中的所有内容,因为 g2 已被解除绑定。

让我通过 Neo4j 浏览器附带的电影数据集解决方案,因为您没有提供示例数据。假设我想获得与 Tom Hanks 和 Hugo Weaving 相关的关系总数。

作为单独的查询:

MATCH (:Person {name:'Tom Hanks'})-[r]-()
RETURN COUNT(r)

=> 13

MATCH (:Person {name:'Hugo Weaving'})-[r]-()
RETURN COUNT(r)

=> 5

如果我按照你的方式去做,我会得到 (13 * 5) * 2 == 90,这是不正确的:

MATCH (:Person {name:'Tom Hanks'})-[r1]-(), 
      (:Person {name:'Hugo Weaving'})-[r2]-()
RETURN COUNT(r1) + COUNT(r2)

=> 90

同样,这是因为我匹配了 r1r2 的所有组合,其中有 65 个 (13 * 5 == 65),然后将其相加得到一共90个(65 + 65 == 90).

解决方法是使用DISTINCT:

MATCH (:Person {name:'Tom Hanks'})-[r1]-(), 
      (:Person {name:'Hugo Weaving'})-[r2]-()
RETURN COUNT(DISTINCT r1) + COUNT(DISTINCT r2)

=> 18

显然,DISTINCT 修饰符只计算每个实体的不同实例。

如果需要,您也可以使用 WITH 完成此操作:

MATCH (:Person {name:'Tom Hanks'})-[r]-()
WITH COUNT(r) AS r1
MATCH (:Person {name:'Hugo Weaving'})-[r]-()
RETURN r1 + COUNT(r)

=> 18

TL;DR - 小心笛卡尔积。 DISTINCT 是你的朋友:

MATCH (:someIndex{name:"name1"})-[r1]-(), 
      (:someIndex{name:"name2"})-[r2]-()
RETURN COUNT(DISTINCT r1) + COUNT(DISTINCT r2);

您看到的结果激增的原因很容易解释:

MATCH (g1:someIndex{name:"name1"}), (g2:someIndex{name:"name2"})
MATCH g1-[r1]-(), g2-[r2]-()
RETURN count(r1)+count(r2);
//Returns 1423740

在第 2 行中,g1 的任何关系的每个组合都与 g2 的任何关系组合,这解释了自 1423740 = 305 * 2334 * 2 以来的数字。所以你正在评估这里基本上是叉积。

计算 name1name2 的所有关系之和的正确方法是:

MATCH (g:someIndex)-[r]-()
WHERE g.name in ["name1", "name2"]
RETURN count(r)