执行匹配交集
匹配交集是一个常见的用例,您需要查找与一组所有输入节点都有关系的节点。
本文的其余部分将使用内置的电影图谱进行演示。示例用例如下:
给定演员姓名列表,找出包含所有给定演员的电影。
一个常见的首次尝试(且错误)的方法是这样的:
WITH ['Keanu Reeves', 'Hugo Weaving', 'Emil Eifrem'] as names
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name in names
RETURN m
上述查询返回的是包含至少一位给定演员的电影,而不是包含所有给定演员的电影。
我们需要其他方法来获取正确的结果。
通过匹配中输入节点的数量来过滤公共节点
我们可以对上述查询进行一些更改,以获取相关的 :Movie 节点集。
这里的想法是,在我们的匹配中,我们想要的 :Movie 节点将具有与输入集合大小相同数量的唯一匹配演员。
WITH ['Keanu Reeves', 'Hugo Weaving', 'Emil Eifrem'] as names
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name in names
WITH m, size(names) as inputCnt, count(DISTINCT p) as cnt
WHERE cnt = inputCnt
RETURN m
通过根据匹配到的唯一节点进行过滤,即使匹配中的节点之间存在多个相同类型的关系,我们也能够得到正确的结果。
这通常是查找匹配交集最有效的方法,但它要求输入列表中的所有输入都是唯一的。
使用 WHERE ALL() 确保列表中所有节点都与其他节点存在关系
另一种(但通常效率较低的)方法是收集输入节点,并使用 WHERE ALL()
谓词来确保所有收集到的节点都与公共节点存在关系。
WITH ['Keanu Reeves', 'Hugo Weaving', 'Emil Eifrem'] as names
MATCH (p:Person)
WHERE p.name in names
WITH collect(p) as persons
MATCH (m:Movie)
WHERE ALL(p in persons WHERE (p)-[:ACTED_IN]->(m))
RETURN m
这种方法的问题在于,它会导致与 :Movie 节点数量成比例的性能损失,因为最后的 MATCH 是从所有 :Movie 节点开始的。
我们可以通过从我们一个输入节点匹配到的 :Movie 节点开始,来稍微改进这个查询,尽管这会增加查询的复杂性。
WITH ['Keanu Reeves', 'Hugo Weaving', 'Emil Eifrem'] as names
MATCH (p:Person)
WHERE p.name in names
WITH collect(p) as persons
WITH head(persons) as head, tail(persons) as persons
MATCH (head)-[:ACTED_IN]->(m:Movie)
WHERE ALL(p in persons WHERE (p)-[:ACTED_IN]->(m))
RETURN m
使用 APOC 交集结果列表
某些用例可能会引入额外的复杂性。例如,我们可能希望结果节点与另一个匹配模式的结果相交,或与不同的结果集合相交。在这些情况下,我们可以简单地使用 WHERE 子句来强制执行附加模式或列表成员关系。
但是当有多个列表需要交集时,仅凭 Cypher 执行起来可能会变得更困难。
使用 Neo4j 3.0.x 或更高版本时,结合 reduce()
函数和来自 APOC Procedures 的交集函数,我们可以对多个列表执行交集操作。
WITH ['Keanu Reeves', 'Hugo Weaving', 'Emil Eifrem'] as names
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name in names
WITH p, collect(m) as moviesPerActor
WITH collect(moviesPerActor) as movies
WITH reduce(commonMovies = head(movies), movie in tail(movies) |
apoc.coll.intersection(commonMovies, movie)) as commonMovies
RETURN commonMovies
此页面有帮助吗?