我正在研究neo4j作为Graph数据库,可变长度路径查询将是一个非常重要的用例。我现在认为我已经找到了Cypher不支持的示例查询。
主要问题是我想将组合关系视为单一关系。让我举个例子:寻找合作演员。我使用电影的标准数据库完成了这项工作。目标是找到与汤姆汉克斯并肩作战的所有演员。这可以通过查询找到:
MATCH (tom {name: "Tom Hanks"})-[:ACTED_IN]->()<-[:ACTED_IN]-(a:Person) return a
现在,如果我们想要递归地找到共同演员的共同演员,那该怎么办?我们可以将上面的查询重写为:
MATCH (tom {name: "Tom Hanks"})-[:ACTED_IN*2]-(a:Person) return a
然后很明显我们可以做到这一点
MATCH (tom {name: "Tom Hanks"})-[:ACTED_IN*]-(a:Person) return a
值得注意的是,所有奇数路径都被排除在外,因为它们不会以Person
结尾。
现在,我找到了一个无法弄清楚如何进行递归的查询:
MATCH (tom {name: "Tom Hanks"})-[:ACTED_IN]->()<-[:DIRECTED]-()-[:DIRECTED]->()<-[:ACTED_IN]-(a:Person) return DISTINCT a
用语言来说,所有与汤姆汉克斯有共同导演的演员。
为了使这个递归我试过:
MATCH (tom {name: "Tom Hanks"})-[:ACTED_IN|DIRECTED*]-(a:Person) return DISTINCT a
但是,(除了看起来根本不完整)。这也将抓住共同参与者。也就是说,它将匹配表单的路径
()-[:ACTED_IN]->()<-[:ACTED_IN]-()
所以我想知道的是:我们能以某种方式限制多路径查询中关系发生的顺序吗?就像是:
MATCH (tom {name: "Tom Hanks"}){-[:ACTED_IN]->()<-[:DIRECTED]-()-[:DIRECTED]->()<-[:ACTED_IN]-}*(a:Person) return DISTINCT a
*适用于花括号中的所有内容。
来自path expander procs的APOC Procedures应该在这里提供帮助,因为我们添加了表达标签,关系或两者的重复序列的能力。
在这种情况下,由于你想匹配模式的actor而不是导演(或路径中的任何电影),我们需要指定你想要返回的路径中的哪些节点,这需要使用labelFilter
除了relationshipFilter
之外,或者只是使用组合的sequence
配置属性来指定预期的交替标签/关系,并确保我们在模式中您所需的点上的Person节点上使用端节点过滤器。
以下是安装APOC后如何执行此操作:
MATCH (tom:Person {name: "Tom Hanks"})
CALL apoc.path.expandConfig(tom, {sequence:'>Person, ACTED_IN>, *, <DIRECTED, *, DIRECTED>, *, <ACTED_IN', maxLevel:12}) YIELD path
WITH last(nodes(path)) as person, min(length(path)) as distance
RETURN person.name
我们通常会将subgraphNodes()
用于这些,因为它可以有效地扩展和修剪我们已经看到的节点的路径,但在这种情况下,我们希望能够重新访问已访问过的节点,因为它们可能会在进一步的迭代中出现。顺序,所以为了得到正确答案,我们不能使用这个或任何使用NODE_GLOBAL唯一性的过程。
因此,我们需要防止探索太多路径,因为即使在我们已经发现所有不同节点成为可能之后,探索适合路径的关系的排列也会飙升。为了避免这种情况,我们必须添加一个maxLevel,所以我在这种情况下使用12。
此过程还将生成到同一节点的多个路径,因此我们将获得到每个节点的所有路径的最小长度。
sequence配置属性允许我们为序列中的每个步骤指定交替的标签和关系类型过滤,从起始节点开始。我们在第一个Person标签(>
)之前使用了一个结束节点过滤器符号>Person
,表示我们只需要序列中此点的Person节点的路径(作为序列中的第一个元素,它也将是最后一个元素)重复的序列)。我们使用通配符*
作为所有其他节点的标签过滤器,这意味着节点被列入白名单并且无论其标签是什么都将被遍历,但我们不希望返回到这些节点的任何路径。
如果你想看到所有演员都是由导演汤姆汉克斯导演的电影演员,但他们从未采访过汤姆,这是一种方式:
MATCH (tom {name: "Tom Hanks"})-[:ACTED_IN]->(m)
MATCH (m)<-[:ACTED_IN]-(ignoredActor)
WITH COLLECT(DISTINCT m) AS ignoredMovies, COLLECT(DISTINCT ignoredActor) AS ignoredActors
UNWIND ignoredMovies AS movie
MATCH (movie)<-[:DIRECTED]-()-[:DIRECTED]->(m2)
WHERE NOT m2 IN ignoredMovies
MATCH (m2)<-[:ACTED_IN]-(a:Person)
WHERE NOT a IN ignoredActors
RETURN DISTINCT a
前2个MATCH
条款故意不合并为一个条款,因此汤姆汉克斯节点将被捕获为ignoredActor
。 (MATCH
子句过滤掉两次使用相同关系的任何结果。)