考虑以下模式,其中橙色节点为 类型Person
,棕色节点为 类型Movie
。(这是来自 Neo4j 附带的“电影”数据集)。
我试图编写的查询如下:
\n\n\n\n\n查找所有审阅者对,一对一地查找,然后返回两位审阅者的姓名\n。如果他们都评论了同一部电影,\n 也返回电影的标题。限制查询,使两位审稿人姓名的首字母为 \xe2\x80\x99J\xe2\x80\x99
\n
现在,考虑以下 CYPHER 查询:
\n\nMATCH (a:Person)-[:REVIEWED]->(:Movie),\n (b:Person)-[:REVIEWED]->(:Movie),\n (a:Person)-[:FOLLOWS]->(b:Person)\nOPTIONAL MATCH (a:Person)-[:REVIEWED]->(m:Movie)<-[:REVIEWED]-(b:Person)\n WHERE a.name STARTS WITH \'J\'\n AND b.name STARTS WITH \'J\'\nRETURN DISTINCT a.name, b.name, m.title\n
Run Code Online (Sandbox Code Playgroud)\n\n这将返回以下(不正确)结果:
\n\n\n\n为什么?
\n\n到目前为止我收集到的内容:
\n\nWHERE
于 ( OPTIONAL
)MATCH
于其前面WHERE
考虑约束条件寻找匹配时而不是事后OPTIONAL MATCH
不完全适用时,null
将用于图案的缺失部分我仍然不明白,为什么“Angela Scope”出现在结果中。无论如何,如果谓词应该禁止它出现。
\n\nPS:我知道以下查询返回正确的结果
\n\nMATCH (a:Person)-[:REVIEWED]->(:Movie),\n (b:Person)-[:REVIEWED]->(:Movie),\n (a:Person)-[:FOLLOWS]->(b:Person)\n WHERE a.name STARTS WITH \'J\'\n AND b.name STARTS WITH \'J\'\nOPTIONAL MATCH (a:Person)-[:REVIEWED]->(m:Movie)<-[:REVIEWED]-(b:Person)\nRETURN DISTINCT a.name, b.name, m.title\n
Run Code Online (Sandbox Code Playgroud)\n\n但是,我想知道为什么这两个查询返回不同的结果,特别是为什么第一个查询返回的结果正是这个结果。
\n当然,你已经差不多找到答案了:
WHERE 适用于紧邻其之前的(可选)匹配
这个很重要。您不应将 WHERE 子句视为独立的,因为它与前面的子句相关联并修改了前面的子句。MATCH ... WHERE ...
所以把它作为一个整体OPTIONAL MATCH ... WHERE ...
来读WITH ... WHERE ...
。
请记住,OPTIONAL MATCH 永远不会过滤掉行。它将保留现有行,并且对于任何新引入的变量,将尝试使用传递其 WHERE 子句的模式来查找匹配项。如果没有找到匹配项,新引入的变量将被设置为 null。再说一次……没有过滤。
所以对于这个片段:
OPTIONAL MATCH (a:Person)-[:REVIEWED]->(m:Movie)<-[:REVIEWED]-(b:Person)
WHERE a.name STARTS WITH 'J'
AND b.name STARTS WITH 'J'
Run Code Online (Sandbox Code Playgroud)
安吉拉·斯科普 (Angela Scope) 和杰西卡·汤普森 (Jessica Thompson) 之间存在以下关系,并且他们评论了同一部电影《The Replacements》,但他们未通过 WHERE 子句,因为安吉拉 (Angela) 的名字不是以“J”开头。因此 OPTIONAL MATCH 没有找到任何内容,因此新引入的变量m
将返回为null
. 什么都不会被过滤。
为了让谓词过滤行,WHERE 子句需要与 MATCH 或WITH 关联。因此,我们可以按照您稍后添加的正确查询来修复它,或者像这样:
MATCH (a:Person)-[:REVIEWED]->(:Movie),
(b:Person)-[:REVIEWED]->(:Movie),
(a:Person)-[:FOLLOWS]->(b:Person)
OPTIONAL MATCH (a:Person)-[:REVIEWED]->(m:Movie)<-[:REVIEWED]-(b:Person)
WITH a, m, b
WHERE a.name STARTS WITH 'J'
AND b.name STARTS WITH 'J'
RETURN DISTINCT a.name, b.name, m.title
Run Code Online (Sandbox Code Playgroud)
这效率较低,因为过滤发生在我们完成可选匹配之后。最好尽早过滤,因此我们仅在已经获得过滤结果时才执行 OPTIONAL MATCH。
另请注意,由于您在开始时匹配了这些模式,因此您会遇到重复问题:(a:Person)-[:REVIEWED]->(:Movie)
。虽然这确实找到了评论者,但您将在每个路径上得到一行与该模式匹配的行...例如,对于杰西卡·汤普森,您可以看到她评论了 2 部电影,因此有两条路径与该模式匹配,这就是为什么她在您的结果中每个其他评论者至少出现两次(并且它将是乘数,具体取决于其他评论者评论的电影数量。
要解决此问题,不要查找 :Person 评论 :Movie 的所有路径,而是查找 :Person 评论过电影的位置:
MATCH (a:Person)
WHERE (a)-[:REVIEWED]->()
Run Code Online (Sandbox Code Playgroud)
因为模式变成了谓词,所以 Cypher 只需要从 :Person 中找到至少一个 :REVIEWED 关系,然后它就可以停止查找,并且您不会得到那些重复的结果。