关于Neo4j查询中count(*)及两次查询结果差异的疑问
在Neo4j标准电影数据库中,存在如下标准查询:
MATCH (tom:Person {name:"Tom Hanks"})-[:ACTED_IN]->(m)<-[:ACTED_IN]-(coActors), (coActors)-[:ACTED_IN]->(m2)<-[:ACTED_IN]-(cocoActors) WHERE NOT (tom)-[:ACTED_IN]->()<-[:ACTED_IN]-(cocoActors) AND tom <> cocoActors RETURN cocoActors.name AS Recommended, count(*) AS Strength ORDER BY Strength DESC
该查询用于返回曾与汤姆·汉克斯的合作演员合作过的演员姓名,同时排除汤姆·汉克斯本人及他的直接合作演员。
我原本认为Strength是该间接合作演员与汤姆·汉克斯的合作演员合作的电影数量,但修改查询为:
MATCH (tom:Person {name:"Tom Hanks"})-[:ACTED_IN]->(m)<-[:ACTED_IN]-(coActors), (coActors)-[:ACTED_IN]->(m2)<-[:ACTED_IN]-(cocoActors) WHERE NOT (tom)-[:ACTED_IN]->()<-[:ACTED_IN]-(cocoActors) AND tom <> cocoActors RETURN coActors.name, cocoActors.name AS Recommended, count(*) AS Strength ORDER BY Strength DESC
后得到了完全不同的结果。请问此处Strength的实际含义是什么?为什么两次查询结果会有差异?
回答
Strength的本质
Cypher里的count(*)统计的是当前RETURN语句分组规则下,匹配到的路径总数,不是你误以为的“合作电影数量”——电影数量和路径数的对应关系,完全取决于你分组的维度。
第一次查询的Strength含义
第一次查询只按cocoActors.name(间接演员姓名)分组,所以count(*)统计的是:所有符合条件的「汤姆·汉克斯→参演电影→直接合作演员→参演电影→间接演员」的完整路径总数。
举个例子:如果间接演员A和汤姆的3个直接合作演员分别合作了2部、3部、1部电影,那总路径数就是2+3+1=6,A对应的Strength就是6。这个数值是A和所有汤姆的合作演员合作的电影数量之和,不是单一的电影数量。
第二次查询的Strength含义
第二次查询同时按coActors.name(汤姆的直接合作演员)和cocoActors.name(间接演员)两个字段分组,所以count(*)统计的是:某一个直接合作演员和某一个间接演员之间,共同参演的电影数量——因为每一部共同参演的电影,对应一条「直接合作演员→电影→间接演员」的路径,所以路径数就等于两人合作的电影数。
还是用上面的例子:第二次查询会生成三条记录,分别是(合作演员1, A, 2)、(合作演员2, A, 3)、(合作演员3, A, 1),每条记录的Strength就是对应两人的合作电影数。
结果差异的核心原因
两次查询的分组维度完全不同:
- 第一次是把同一个间接演员的所有关联路径合并统计,得到的是该演员和汤姆所有合作演员的总合作路径数。
- 第二次是把「直接合作演员+间接演员」的每一对组合单独统计,得到的是每一对组合之间的合作电影数。
这就导致两次查询的统计对象、统计范围完全不一样,结果自然会有巨大差异。
内容的提问来源于stack exchange,提问作者user

