使用配置扩展路径
使用配置扩展路径过程支持强大的变长路径遍历,并提供对遍历的精细控制。对于不需要精细控制遍历的更基本算法版本,请参阅扩展路径。
过程概述
该过程描述如下
限定名称 | 类型 |
---|---|
apoc.path.expandConfig |
|
配置参数
这些过程支持以下配置参数
名称 | 类型 | 默认 | 描述 |
---|---|---|---|
minLevel |
INTEGER |
-1 |
遍历中的最小跳数 |
maxLevel |
INTEGER |
-1 |
遍历中的最大跳数 |
relationshipFilter |
STRING |
null |
要遍历的关系类型和方向。 参见关系过滤器。 |
labelFilter |
STRING |
null |
要遍历的节点标签。 参见标签过滤器。 |
sequence |
STRING |
null |
逗号分隔的交替标签和关系过滤器,用于重复序列中的每个步骤。如果存在,将忽略 |
beginSequenceAtStart |
BOOLEAN |
true |
从起始节点开始,匹配距离起始节点一个节点的节点标签和/或关系类型序列(在 |
uniqueness |
STRING |
RELATIONSHIP_PATH |
在遍历中扩展关系时使用的策略。 参见唯一性。 |
bfs |
BOOLEAN |
true |
遍历时使用广度优先搜索。如果设置为 |
filterStartNode |
BOOLEAN |
false |
|
limit |
INTEGER |
-1 |
限制返回的路径数量。当使用 |
optional |
BOOLEAN |
false |
路径扩展是否可选?如果设置为 |
endNodes |
LIST<NODE> |
null |
只有这些节点可以结束返回的路径,并且如果可能,扩展将继续通过这些节点。 |
terminatorNodes |
LIST<NODE> |
null |
只有这些节点可以结束返回的路径,并且扩展不会继续通过这些节点。 |
allowlistNodes |
LIST<NODE> |
null |
只有这些节点被允许在扩展中(如果存在,endNodes 和 terminatorNodes 也将被允许)。 |
denylistNodes |
LIST<NODE> |
null |
返回的路径都不会包含这些节点。 |
whitelistNodes (已弃用) |
LIST<NODE> |
null |
参见 allowlistNodes。 |
blacklistNodes (已弃用) |
LIST<NODE> |
null |
参见 denylistNodes。 |
关系过滤器
关系过滤器的语法描述如下
语法:[<]RELATIONSHIP_TYPE1[>]|[<]RELATIONSHIP_TYPE2[>]|…
输入 | 类型 | 方向 |
---|---|---|
|
|
出站 |
|
|
入站 |
|
|
双向 |
|
|
出站 |
|
|
入站 |
标签过滤器
标签过滤器的语法描述如下
语法:[+-/>]LABEL1|LABEL2|*|…
符号 | 过滤类型 | 输入示例 | 描述 |
---|---|---|---|
|
拒绝列表 |
|
路径中的任何节点都不会具有拒绝列表中存在的标签。 |
|
允许列表 |
|
路径中的所有节点都必须具有允许列表中的标签(如果使用终止节点和结束节点过滤器,则这些节点除外)。如果不存在允许列表操作符,则允许所有标签。 |
|
终止 |
|
只返回到达具有给定标签的节点的路径,并停止超出该节点的进一步扩展。终止节点无需遵守允许列表。终止过滤优先于结束节点过滤。 |
|
结束节点 |
|
只返回到达具有给定标签的节点的路径,但继续扩展以匹配超出其范围的结束节点。结束节点无需遵守允许列表即可返回,但只有当节点具有允许列表中的标签时,才允许超出其范围的扩展。 |
|
复合标签 |
|
这返回标签的合取,例如 |
标签过滤器操作符优先级和行为
允许多个标签过滤器操作符同时使用。以下面的示例为例
labelFilter:'+Person|Movie|-SciFi|>Western|/Romance'
如果我们分析这个标签过滤器,我们可以看到
-
:Person
和:Movie
标签被允许 -
:SciFi
被拒绝 -
:Western
是一个结束节点标签 -
:Romance
是一个终止标签。
操作符评估的优先级不取决于它们在labelFilter中的位置,而是固定的
拒绝列表过滤器-
,终止过滤器/
,结束节点过滤器>
,允许列表过滤器+
。
这意味着
-
返回路径的节点中永远不会出现被拒绝的标签
-
,即使相同的标签(或具有被拒绝标签的另一个节点的标签)包含在另一个过滤器列表中。 -
如果使用终止过滤器
/
或结束节点过滤器>
,则只返回到达具有这些标签的节点的路径作为结果。这些结束节点不受允许列表过滤器的限制。 -
如果一个节点是终止节点
/
,则不会发生超出该节点的进一步扩展。 -
允许列表仅适用于终止或结束节点过滤器中的节点(但不包括结束节点)。如果不存在结束节点或终止节点操作符,则允许列表适用于路径中的所有节点。
-
如果labelFilter中不存在允许列表操作符,则视为所有标签都已允许。
唯一性
节点和关系的唯一性指导扩展和返回结果。下表描述了可用值
值 | 描述 |
---|---|
|
对于每个返回的节点,都有一条从起始 |
|
一个节点不能被遍历多次。这是旧版遍历框架的行为。 |
|
同一级别的实体保证是唯一的。 |
|
对于每个返回的节点,都有一条从起始 |
|
这类似于 NODE_GLOBAL,但只保证最近访问节点的唯一性,并可配置计数。遍历大型图非常占用内存,因为它会跟踪所有已访问的节点。对于大型图,遍历器可能会占用 JVM 中的所有内存,导致 OutOfMemoryError。与此唯一性一起,您可以提供一个计数,即最近访问节点的数量。这可能导致一个节点被多次访问,但可以无限扩展。 |
|
一个关系不能被遍历多次,而节点可以。 |
|
同一级别的实体保证是唯一的。 |
|
与 NODE_RECENT 相同,但适用于关系。 |
|
无限制(用户必须自行管理) |
指定节点标签和关系类型的序列
路径扩展器过程可以基于标签、关系类型或两者的重复序列进行扩展。序列可以定义如下
-
如果只使用标签序列,请使用
labelFilter
,但用逗号分隔重复序列中每个步骤的过滤条件。 -
如果只使用关系序列,请使用
relationshipFilter
,但用逗号分隔重复序列中每个步骤的过滤条件。 -
如果同时使用关系和标签序列,请使用
sequence
参数。
用法 | 配置参数 | 描述 | 语法 | 解释 |
---|---|---|---|---|
仅标签序列 |
|
相同的语法和过滤器,但使用逗号 ( |
|
起始节点必须是未被阻塞的:Post节点,下一个节点必须是:Reply,再下一个必须是:Admin,然后如果可能则重复。只返回在序列该位置以 |
仅关系序列 |
|
相同的语法,但使用逗号 ( |
|
扩展将首先从起始节点扩展 |
关系和标签的序列 |
|
一个逗号分隔的交替标签和关系过滤器字符串,用于重复序列中的每个步骤。序列应以标签过滤器开头,并以关系过滤器结尾。如果存在,将忽略 |
|
结合了上述行为。 |
在某些用例中,序列不从起始节点开始,而是从距离起始节点一个节点的位置开始。
配置参数beginSequenceAtStart
切换此行为。其默认值为true
。如果设置为false
,则会更改labelFilter
、relationshipFilter
和sequence
的预期值,如下所述
sequence | 更改后的行为 | 示例 | 解释 |
---|---|---|---|
|
起始节点不被视为序列的一部分。序列从距离起始节点一个节点的位置开始。 |
|
从起始 |
|
序列字符串中的第一个关系过滤器将不被视为重复序列的一部分,而只用于从起始 |
|
|
|
结合了上述两种行为。 |
|
结合了上述行为。 |
序列提示
序列中的标签过滤与 如果需要限制序列重复的次数,可以使用 |
示例
本节中的示例基于以下示例图
MERGE (mark:Person:DevRel {name: "Mark"})
MERGE (lju:Person:DevRel {name: "Lju"})
MERGE (praveena:Person:Engineering {name: "Praveena"})
MERGE (zhen:Person:Engineering {name: "Zhen"})
MERGE (martin:Person:Engineering {name: "Martin"})
MERGE (joe:Person:Field {name: "Joe"})
MERGE (stefan:Person:Field {name: "Stefan"})
MERGE (alicia:Person:Product {name: "Alicia"})
MERGE (jake:Person:Product {name: "Jake"})
MERGE (john:Person:Product {name: "John"})
MERGE (jonny:Person:Sales {name: "Jonny"})
MERGE (anthony:Person:Sales {name: "Anthony"})
MERGE (rik:Person:Sales {name: "Rik"})
MERGE (zhen)-[:KNOWS]-(stefan)
MERGE (zhen)-[:KNOWS]-(lju)
MERGE (zhen)-[:KNOWS]-(praveena)
MERGE (zhen)-[:KNOWS]-(martin)
MERGE (mark)-[:KNOWS]-(jake)
MERGE (alicia)-[:KNOWS]-(jake)
MERGE (jonny)-[:KNOWS]-(anthony)
MERGE (john)-[:KNOWS]-(rik)
MERGE (alicia)-[:FOLLOWS]->(joe)
MERGE (joe)-[:FOLLOWS]->(mark)
MERGE (joe)-[:FOLLOWS]->(praveena)
MERGE (joe)-[:FOLLOWS]->(zhen)
MERGE (mark)-[:FOLLOWS]->(stefan)
MERGE (stefan)-[:FOLLOWS]->(joe)
MERGE (praveena)-[:FOLLOWS]->(joe)
MERGE (lju)-[:FOLLOWS]->(jake)
MERGE (alicia)-[:FOLLOWS]->(jonny)
MERGE (zhen)-[:FOLLOWS]->(john)
MERGE (anthony)-[:FOLLOWS]->(joe)
下面的 Neo4j Browser 可视化显示了示例图
KNOWS
关系类型被认为是双向的,如果 Zhen 认识 Stefan,我们可以推断 Stefan 认识 Zhen。当使用KNOWS
关系时,我们将忽略方向。
FOLLOWS
关系具有方向,因此在使用时我们将指定方向。
关系类型和节点标签过滤器
让我们从 Praveena 节点开始扩展路径。我们只想考虑KNOWS
关系类型,因此我们将其指定为relationshipFilter
参数。
KNOWS
的人,从 1 到 2 跳的路径MATCH (p:Person {name: "Praveena"})
CALL apoc.path.expandConfig(p, {
relationshipFilter: "KNOWS",
minLevel: 1,
maxLevel: 2
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"}) |
1 |
(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Martin"}) |
2 |
(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:DevRel {name: "Lju"}) |
2 |
(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Field {name: "Stefan"}) |
2 |
Praveena 只与 Zhen 有直接的KNOWS
关系,但 Zhen 与另外 3 个人有KNOWS
关系,这意味着他们距离 Praveena 有 2 跳。
我们还可以提供节点标签过滤器来限制返回的节点。如果只想返回路径中每个节点都带有Engineering
标签的路径,我们将向labelFilter
参数提供值+Engineering
。
KNOWS
的Engineering
人员,从 1 到 2 跳的路径MATCH (p:Person {name: "Praveena"})
CALL apoc.path.expandConfig(p, {
relationshipFilter: "KNOWS",
labelFilter: "+Engineering",
minLevel: 1,
maxLevel: 2
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"}) |
1 |
(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Martin"}) |
2 |
我们失去了以 Lju 和 Stefan 结尾的路径,因为这两个节点都没有Engineering
标签。
我们可以指定多种关系类型。以下查询从 Alicia 节点开始,然后扩展FOLLOWS
和KNOWS
关系
FOLLOWS
或 KNOWS
的人员,从 1 到 3 跳的路径MATCH (p:Person {name: "Alicia"})
CALL apoc.path.expandConfig(p, {
relationshipFilter: "FOLLOWS>|KNOWS",
minLevel: 1,
maxLevel: 3
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
1 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Sales {name: "Jonny"}) |
1 |
(:Person:Product {name: "Alicia"})-[:KNOWS]→(:Person:Product {name: "Jake"}) |
1 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Sales {name: "Jonny"})-[:KNOWS]→(:Person:Sales {name: "Anthony"}) |
2 |
(:Person:Product {name: "Alicia"})-[:KNOWS]→(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:DevRel {name: "Mark"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:FOLLOWS]→(:Person:Product {name: "John"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Martin"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Praveena"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:DevRel {name: "Lju"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Field {name: "Stefan"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"})-[:KNOWS]→(:Person:Product {name: "Jake"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Sales {name: "Jonny"})-[:KNOWS]→(:Person:Sales {name: "Anthony"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
3 |
(:Person:Product {name: "Alicia"})-[:KNOWS]→(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"}) |
3 |
此查询返回 19 条路径,Alicia 的连接非常广!
我们可以在从 Alicia 出发的路径中看到返回路径的 Neo4j Browser 可视化。
我们还可以使用标签过滤器指定遍历终止条件。如果希望在遍历遇到包含Engineering
标签的节点时立即终止遍历,我们可以使用/Engineering
节点过滤器。
FOLLOWS
或 KNOWS
的人员,从 1 到 3 跳的路径,一旦到达带有Engineering
标签的节点即终止MATCH (p:Person {name: "Alicia"})
CALL apoc.path.expandConfig(p, {
relationshipFilter: "FOLLOWS>|KNOWS",
labelFilter: "/Engineering",
minLevel: 1,
maxLevel: 3
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"}) |
2 |
现在我们只剩下两条路径。但此查询并未捕获所有从 Alicia 出发并以带有Engineering
标签的节点结尾的路径。我们可以使用>Engineering
节点过滤器来定义一个遍历,该遍历
-
只返回在带有
Engineering
标签的节点处终止的路径 -
之后继续扩展到结束节点,寻找更多以
Engineering
标签结尾的路径
FOLLOWS
或 KNOWS
的人员,从 1 到 3 跳的路径,其中路径以带有Engineering
标签的节点结尾MATCH (p:Person {name: "Alicia"})
CALL apoc.path.expandConfig(p, {
relationshipFilter: "FOLLOWS>|KNOWS",
labelFilter: ">Engineering",
minLevel: 1,
maxLevel: 3
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Martin"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Praveena"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"}) |
3 |
我们的查询现在还返回经过 Praveena 和 Zhen 的路径,一条通往 Martin,还有其他路径返回 Zhen 和 Praveena!
终止节点和结束节点
除了为遍历指定终止标签和结束标签外,我们还可以指定终止节点和结束节点。
让我们基于之前找到 Alicia KNOWS
或 FOLLOWS
的人的查询。我们希望任何返回的路径在遇到 Joe 节点时立即停止,这可以通过将 Joe 节点传递给terminatorNodes
参数来实现。
FOLLOWS
或 KNOWS
的人员,从 1 到 3 跳的路径,一旦到达 Joe 即终止MATCH (p:Person {name: "Alicia"})
MATCH (joe:Person {name: "Joe"})
CALL apoc.path.expandConfig(p, {
relationshipFilter: "FOLLOWS>|KNOWS",
minLevel: 1,
maxLevel: 3,
terminatorNodes: [joe]
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
1 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Sales {name: "Jonny"})-[:KNOWS]→(:Person:Sales {name: "Anthony"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
3 |
Alicia FOLLOWS
Joe,但还有一条路径通过 Jonny 和 Anthony。
终止节点方法不一定能找到 Alicia 和 Joe 之间存在的所有路径。可能存在其他路径两次经过 Joe 节点。我们可以通过将 Joe 节点传递给endNodes
参数来找到这些路径。如果使用此参数,所有返回的路径都将以 Joe 节点结束,但扩展将继续通过此节点,尝试找到其他以 Joe 结束的路径。
FOLLOWS
或 KNOWS
的人员,从 1 到 3 跳的路径,其中路径在到达 Joe 时结束MATCH (p:Person {name: "Alicia"})
MATCH (joe:Person {name: "Joe"})
CALL apoc.path.expandConfig(p, {
relationshipFilter: "FOLLOWS>|KNOWS",
minLevel: 1,
maxLevel: 3,
endNodes: [joe]
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
1 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Sales {name: "Jonny"})-[:KNOWS]→(:Person:Sales {name: "Anthony"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
3 |
我们得到了使用终止节点方法得到的两条路径,从 Alicia 到 Joe,以及从 Alicia 到 Jonny 再到 Jonny 再到 Joe。但我们还有一条额外路径,从 Alicia 到 Joe 再到 Praveena 再到 Joe。
允许列表节点和拒绝列表节点
也可以指定允许列表和拒绝列表节点。
让我们基于之前找到 Alicia KNOWS
或 FOLLOWS
的人的查询。我们希望任何返回的路径只包含节点 Mark、Joe、Zhen 和 Praveena,这可以通过将这些节点传递给allowlistNodes
参数来实现。
FOLLOWS
或KNOWS
关系类型,从 1 到 3 跳的路径,只包含 Mark、Joe、Zhen 和 Praveena 的路径MATCH (p:Person {name: "Alicia"})
MATCH (allowlist:Person)
WHERE allowlist.name IN ["Mark", "Joe", "Zhen", "Praveena"]
WITH p, collect(allowlist) AS allowlistNodes
CALL apoc.path.expandConfig(p, {
relationshipFilter: "FOLLOWS>|KNOWS",
minLevel: 1,
maxLevel: 3,
allowlistNodes: allowlistNodes
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
1 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Praveena"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"}) |
3 |
在允许列表中,唯一与 Alicia 有直接连接的人是 Joe,所以所有路径都经过他。然后我们从 Joe 到其他人,再在他们之间进行 3 跳的路径。
我们可以在从 Alicia 到 Mark、Joe、Zhen 和 Praveena 的路径中看到返回路径的 Neo4j Browser 可视化。
拒绝列表用于从返回的路径中排除节点。如果想排除包含 Joe 的路径,可以通过将 Joe 节点传递给denylistNodes
参数来实现。
FOLLOWS
或 KNOWS
的人员,从 1 到 3 跳的路径,排除包含 Joe 的路径MATCH (p:Person {name: "Alicia"})
MATCH (joe:Person {name: "Joe"})
CALL apoc.path.expandConfig(p, {
relationshipFilter: "FOLLOWS>|KNOWS",
minLevel: 1,
maxLevel: 3,
denylistNodes: [joe]
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Sales {name: "Jonny"}) |
1 |
(:Person:Product {name: "Alicia"})-[:KNOWS]→(:Person:Product {name: "Jake"}) |
1 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Sales {name: "Jonny"})-[:KNOWS]→(:Person:Sales {name: "Anthony"}) |
2 |
(:Person:Product {name: "Alicia"})-[:KNOWS]→(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:DevRel {name: "Mark"}) |
2 |
(:Person:Product {name: "Alicia"})-[:KNOWS]→(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"}) |
3 |
这返回的路径集非常小,因为 Joe 是连接 Alicia 到图其余部分的一个非常关键的节点。
我们可以在从 Alicia 出发但不包含 Joe 的路径中看到返回路径的 Neo4j Browser 可视化。
广度优先搜索和深度优先搜索
我们可以通过指定bfs: true
来控制遍历是否使用广度优先搜索 (BFS),或者通过指定bfs: false
来控制是否使用深度优先搜索 (DFS) 算法。这通常与limit
参数结合使用,以根据所选算法查找最近的节点。
FOLLOWS
或 KNOWS
的人员,从 1 到 3 跳MATCH (p:Person {name: "Alicia"})
MATCH (joe:Person {name: "Joe"})
CALL apoc.path.expandConfig(p, {
relationshipFilter: "FOLLOWS>|KNOWS",
minLevel: 1,
maxLevel: 5,
bfs: true,
limit: 10
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
1 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Sales {name: "Jonny"}) |
1 |
(:Person:Product {name: "Alicia"})-[:KNOWS]→(:Person:Product {name: "Jake"}) |
1 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Sales {name: "Jonny"})-[:KNOWS]→(:Person:Sales {name: "Anthony"}) |
2 |
(:Person:Product {name: "Alicia"})-[:KNOWS]→(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:DevRel {name: "Mark"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:FOLLOWS]→(:Person:Product {name: "John"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Martin"}) |
3 |
从这些结果中我们可以看到,路径在进入下一个级别之前,在每个级别上都完全扩展。例如,我们首先从
-
Alicia
→
Joe -
Alicia
→
Jonny -
Alicia
→
Jake
然后从这些节点开始跟随关系。一旦它扩展了级别 2 的所有内容,它将接着探索级别 3。
如果我们使用深度优先搜索算法,遍历将尽可能远地(直到maxLevel
跳数)沿着特定路径向下,然后返回并探索其他路径。
FOLLOWS
或 KNOWS
的人员,从 1 到 3 跳MATCH (p:Person {name: "Alicia"})
MATCH (joe:Person {name: "Joe"})
CALL apoc.path.expandConfig(p, {
relationshipFilter: "FOLLOWS>|KNOWS",
minLevel: 1,
maxLevel: 3,
bfs: false,
limit: 10
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
1 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"}) |
2 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:FOLLOWS]→(:Person:Product {name: "John"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Martin"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Praveena"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:DevRel {name: "Lju"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Field {name: "Stefan"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
3 |
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"}) |
3 |
现在我们得到了不同的返回路径集。我们甚至看不到从 Alicia 到 Jonny 或从 Alicia 到 Jake 的路径,因为我们 10 条路径的限制完全被通过 Joe 的路径占据了。
我们可以在使用深度优先搜索从 Alicia 出发的路径中看到返回路径的 Neo4j Browser 可视化。
唯一性
我们可以通过uniqueness
参数指定遍历使用的唯一性策略。有关有效策略列表,请参阅唯一性。默认值为RELATIONSHIP_PATH
。
在本节中,我们将编写从 Joe 开始并遍历FOLLOWS
关系的查询。
FOLLOWS
关系类型,从 1 到 3 跳的路径中的节点MATCH (p:Person {name: "Joe"})
CALL apoc.path.expandConfig(p, {
relationshipFilter: "FOLLOWS>",
minLevel: 1,
maxLevel: 3,
uniqueness: "RELATIONSHIP_PATH" // default
})
YIELD path
RETURN [node in nodes(path) | node.name] AS nodes, length(path) AS hops
ORDER BY hops;
节点 | 跳数 |
---|---|
["Joe", "Zhen"] |
1 |
["Joe", "Praveena"] |
1 |
["Joe", "Mark"] |
1 |
["Joe", "Zhen", "John"] |
2 |
["Joe", "Praveena", "Joe"] |
2 |
["Joe", "Mark", "Stefan"] |
2 |
["Joe", "Praveena", "Joe", "Zhen"] |
3 |
["Joe", "Praveena", "Joe", "Mark"] |
3 |
["Joe", "Mark", "Stefan", "Joe"] |
3 |
返回的几条路径包含 Joe 节点两次。如果我们要确保路径中的节点是唯一的,可以使用NODE_PATH
策略。
FOLLOWS
关系类型,从 1 到 3 跳的路径中的节点,使用NODE_PATH
策略MATCH (p:Person {name: "Joe"})
CALL apoc.path.expandConfig(p, {
relationshipFilter: "FOLLOWS>",
minLevel: 1,
maxLevel: 3,
uniqueness: "NODE_PATH"
})
YIELD path
RETURN [node in nodes(path) | node.name] AS nodes, length(path) AS hops
ORDER BY hops;
节点 | 跳数 |
---|---|
["Joe", "Zhen"] |
1 |
["Joe", "Praveena"] |
1 |
["Joe", "Mark"] |
1 |
["Joe", "Zhen", "John"] |
2 |
["Joe", "Mark", "Stefan"] |
2 |
现在返回的路径具有唯一的节点列表。
关系类型序列
关系类型序列可以通过逗号分隔传递给relationshipFilter
的值来指定。
例如,如果我们要从 Joe 节点开始,并遍历出站方向的FOLLOWS
关系和任一方向的KNOWS
关系的序列,我们可以指定关系过滤器FOLLOWS>,KNOWS
。
FOLLOWS
和KNOWS
之间交替,从 1 到 4 跳的路径MATCH (p:Person {name: "Joe"})
CALL apoc.path.expandConfig(p, {
relationshipFilter: "FOLLOWS>,KNOWS",
beginSequenceAtStart: true,
minLevel: 1,
maxLevel: 4
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"}) |
1 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"}) |
1 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"}) |
1 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Martin"}) |
2 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Praveena"}) |
2 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:DevRel {name: "Lju"}) |
2 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Field {name: "Stefan"}) |
2 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"}) |
2 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"})-[:KNOWS]→(:Person:Product {name: "Jake"}) |
2 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
3 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:DevRel {name: "Lju"})-[:FOLLOWS]→(:Person:Product {name: "Jake"}) |
3 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Field {name: "Stefan"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
3 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"})-[:FOLLOWS]→(:Person:Product {name: "John"}) |
3 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:DevRel {name: "Lju"})-[:FOLLOWS]→(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:DevRel {name: "Mark"}) |
4 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:DevRel {name: "Lju"})-[:FOLLOWS]→(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:Product {name: "Alicia"}) |
4 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"})-[:FOLLOWS]→(:Person:Product {name: "John"})-[:KNOWS]→(:Person:Sales {name: "Rik"}) |
4 |
minLevel
和maxLevel
值指的是路径中关系的跳数。使用minLevel
为 1 意味着将返回从 Joe 出发,带有FOLLOWS
关系类型,且跳数为 1 的路径。如果我们要确保此relationshipFilter
中定义的关系类型序列至少匹配一次,我们需要使用minLevel
为2
,因为过滤器中有两种关系类型。
FOLLOWS
和KNOWS
之间交替,从 2 到 4 跳的路径MATCH (p:Person {name: "Joe"})
CALL apoc.path.expandConfig(p, {
relationshipFilter: "FOLLOWS>,KNOWS",
beginSequenceAtStart: true,
minLevel: 2,
maxLevel: 4
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Martin"}) |
2 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Praveena"}) |
2 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:DevRel {name: "Lju"}) |
2 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Field {name: "Stefan"}) |
2 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"}) |
2 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"})-[:KNOWS]→(:Person:Product {name: "Jake"}) |
2 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
3 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:DevRel {name: "Lju"})-[:FOLLOWS]→(:Person:Product {name: "Jake"}) |
3 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Field {name: "Stefan"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
3 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"})-[:FOLLOWS]→(:Person:Product {name: "John"}) |
3 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:DevRel {name: "Lju"})-[:FOLLOWS]→(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:DevRel {name: "Mark"}) |
4 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:DevRel {name: "Lju"})-[:FOLLOWS]→(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:Product {name: "Alicia"}) |
4 |
(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"})-[:FOLLOWS]→(:Person:Product {name: "John"})-[:KNOWS]→(:Person:Sales {name: "Rik"}) |
4 |
此配置还可以与beginSequenceAtStart: false
结合使用,这意味着序列将从距离起始节点一跳的位置开始。如果使用此配置,则relationshipFilter
中定义的第一个关系类型将仅适用于起始节点。
KNOWS
关系后,关系类型在FOLLOWS
和KNOWS
之间交替,从 3 到 5 跳的路径MATCH (p:Person {name: "Jake"})
CALL apoc.path.expandConfig(p, {
relationshipFilter: "KNOWS,FOLLOWS>,KNOWS",
beginSequenceAtStart: false,
minLevel: 3,
maxLevel: 7
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"}) |
3 |
(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Sales {name: "Jonny"})-[:KNOWS]→(:Person:Sales {name: "Anthony"}) |
3 |
(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"})-[:FOLLOWS]→(:Person:Product {name: "John"}) |
4 |
(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Sales {name: "Jonny"})-[:KNOWS]→(:Person:Sales {name: "Anthony"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
4 |
(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"})-[:FOLLOWS]→(:Person:Product {name: "John"})-[:KNOWS]→(:Person:Sales {name: "Rik"}) |
5 |
节点标签序列
节点标签序列可以通过逗号分隔传递给labelFilter
的值来指定。这通常与beginSequenceAtStart: false
结合使用,这意味着序列将从距离起始节点一跳的位置开始。
例如,如果从 Praveena 节点开始,并希望返回包含交替Field
和DevRel
节点的路径,我们可以指定标签过滤器为"+Field,+DevRel"
。
Field
和DevRel
标签之间交替,从 1 到 4 跳的路径。MATCH (p:Person {name: "Praveena"})
CALL apoc.path.expandConfig(p, {
labelFilter: "+Field,+DevRel",
beginSequenceAtStart: false,
minLevel: 1,
maxLevel: 4
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Engineering {name: "Praveena"})←[:FOLLOWS]-(:Person:Field {name: "Joe"}) |
1 |
(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) |
1 |
(:Person:Engineering {name: "Praveena"})←[:FOLLOWS]-(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"}) |
2 |
(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"}) |
2 |
(:Person:Engineering {name: "Praveena"})←[:FOLLOWS]-(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"}) |
3 |
(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"}) |
3 |
minLevel
和maxLevel
值指的是路径中关系的跳数。使用minLevel
为 1 意味着将返回距离 Praveena 一跳的节点具有Field
标签的路径。如果我们要确保此labelFilter
中定义的标签序列至少匹配一次,我们需要使用minLevel
为2
。
Field
和DevRel
标签之间交替,从 2 到 4 跳的路径。MATCH (p:Person {name: "Praveena"})
CALL apoc.path.expandConfig(p, {
labelFilter: "+Field,+DevRel",
beginSequenceAtStart: false,
minLevel: 2,
maxLevel: 4
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Engineering {name: "Praveena"})←[:FOLLOWS]-(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"}) |
2 |
(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"}) |
2 |
(:Person:Engineering {name: "Praveena"})←[:FOLLOWS]-(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"}) |
3 |
(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"}) |
3 |
现在,仅包含从 Praveena 到 Joe 的关系的路径已被过滤掉。
但是,如果我们不想指定多个标签的存在,而是想找到节点不包含某个标签的路径呢?要查找包含交替的Field
节点和非Field
节点的路径,我们可以指定标签过滤器为"+Field,-Field"
。
Field
标签和不具有Field
标签之间交替,从 1 到 4 跳的路径中的节点MATCH (p:Person {name: "Praveena"})
CALL apoc.path.expandConfig(p, {
labelFilter: "+Field,-Field",
beginSequenceAtStart: false,
minLevel: 2,
maxLevel: 4
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;
路径 | 跳数 |
---|---|
(:Person:Engineering {name: "Praveena"})←[:FOLLOWS]-(:Person:Field {name: "Joe"})←[:FOLLOWS]-(:Person:Sales {name: "Anthony"}) |
2 |
(:Person:Engineering {name: "Praveena"})←[:FOLLOWS]-(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"}) |
2 |
(:Person:Engineering {name: "Praveena"})←[:FOLLOWS]-(:Person:Field {name: "Joe"})←[:FOLLOWS]-(:Person:Product {name: "Alicia"}) |
2 |
(:Person:Engineering {name: "Praveena"})←[:FOLLOWS]-(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"}) |
2 |
(:Person:Engineering {name: "Praveena"})←[:FOLLOWS]-(:Person:Field {name: "Joe"})←[:FOLLOWS]-(:Person:Engineering {name: "Praveena"}) |
2 |
(:人物:工程部 {姓名: "Praveena"})-[:关注]→(:人物:现场部 {姓名: "Joe"})←[:关注]-(:人物:销售部 {姓名: "Anthony"}) |
2 |
(:人物:工程部 {姓名: "Praveena"})-[:关注]→(:人物:现场部 {姓名: "Joe"})-[:关注]→(:人物:工程部 {姓名: "Zhen"}) |
2 |
(:人物:工程部 {姓名: "Praveena"})-[:关注]→(:人物:现场部 {姓名: "Joe"})←[:关注]-(:人物:产品部 {姓名: "Alicia"}) |
2 |
(:人物:工程部 {姓名: "Praveena"})-[:关注]→(:人物:现场部 {姓名: "Joe"})-[:关注]→(:人物:工程部 {姓名: "Praveena"}) |
2 |
(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"}) |
2 |
(:人物:工程部 {姓名: "Praveena"})←[:关注]-(:人物:现场部 {姓名: "Joe"})-[:关注]→(:人物:工程部 {姓名: "Zhen"})-[:认识]→(:人物:现场部 {姓名: "Stefan"}) |
3 |
(:Person:Engineering {name: "Praveena"})←[:FOLLOWS]-(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"}) |
3 |
(:人物:工程部 {姓名: "Praveena"})-[:关注]→(:人物:现场部 {姓名: "Joe"})-[:关注]→(:人物:工程部 {姓名: "Zhen"})-[:认识]→(:人物:现场部 {姓名: "Stefan"}) |
3 |
(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"}) |
3 |
(:人物:工程部 {姓名: "Praveena"})←[:关注]-(:人物:现场部 {姓名: "Joe"})-[:关注]→(:人物:工程部 {姓名: "Zhen"})-[:认识]→(:人物:现场部 {姓名: "Stefan"})←[:关注]-(:人物:开发者关系部 {姓名: "Mark"}) |
4 |
(:人物:工程部 {姓名: "Praveena"})←[:关注]-(:人物:现场部 {姓名: "Joe"})-[:关注]→(:人物:开发者关系部 {姓名: "Mark"})-[:关注]→(:人物:现场部 {姓名: "Stefan"})←[:认识]-(:人物:工程部 {姓名: "Zhen"}) |
4 |
(:人物:工程部 {姓名: "Praveena"})-[:关注]→(:人物:现场部 {姓名: "Joe"})-[:关注]→(:人物:工程部 {姓名: "Zhen"})-[:认识]→(:人物:现场部 {姓名: "Stefan"})←[:关注]-(:人物:开发者关系部 {姓名: "Mark"}) |
4 |
(:人物:工程部 {姓名: "Praveena"})-[:关注]→(:人物:现场部 {姓名: "Joe"})-[:关注]→(:人物:开发者关系部 {姓名: "Mark"})-[:关注]→(:人物:现场部 {姓名: "Stefan"})←[:认识]-(:人物:工程部 {姓名: "Zhen"}) |
4 |
我们还有更多路径,路径长度在2到4跳之间。这些路径具有以下标签:
-
2跳 -
Field
→ 非Field
-
3跳 -
Field
→ 非Field
→Field
-
4跳 -
Field
→ 非Field
→Field
→ 非Field
这些路径有点难以阅读,因此我们可以使用 nodes
函数只返回节点来简化输出。我们还会过滤结果,以便只返回与完整 +Field,-Field
标签过滤器匹配的路径。这可以通过只返回偶数长度的路径来实现。
Field
标签和不具有 Field
标签之间交替:MATCH (p:Person {name: "Praveena"})
CALL apoc.path.expandConfig(p, {
labelFilter: "+Field,-Field",
beginSequenceAtStart: false,
minLevel: 2,
maxLevel: 4
})
YIELD path
WHERE length(path) % 2 = 0
// Remove the Praveena node from the returned path
RETURN nodes(path)[1..] AS nodes, length(path) AS hops
ORDER BY hops;
节点 | 跳数 |
---|---|
[(:人物:现场部 {姓名: "Joe"}), (:人物:销售部 {姓名: "Anthony"})] |
2 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:工程部 {姓名: "Zhen"})] |
2 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:产品部 {姓名: "Alicia"})] |
2 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:开发者关系部 {姓名: "Mark"})] |
2 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:工程部 {姓名: "Praveena"})] |
2 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:销售部 {姓名: "Anthony"})] |
2 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:工程部 {姓名: "Zhen"})] |
2 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:产品部 {姓名: "Alicia"})] |
2 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:工程部 {姓名: "Praveena"})] |
2 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:开发者关系部 {姓名: "Mark"})] |
2 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:工程部 {姓名: "Zhen"}), (:人物:现场部 {姓名: "Stefan"}), (:人物:开发者关系部 {姓名: "Mark"})] |
4 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:开发者关系部 {姓名: "Mark"}), (:人物:现场部 {姓名: "Stefan"}), (:人物:工程部 {姓名: "Zhen"})] |
4 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:工程部 {姓名: "Zhen"}), (:人物:现场部 {姓名: "Stefan"}), (:人物:开发者关系部 {姓名: "Mark"})] |
4 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:开发者关系部 {姓名: "Mark"}), (:人物:现场部 {姓名: "Stefan"}), (:人物:工程部 {姓名: "Zhen"})] |
4 |
在节点序列中,*
字符可用作通配符,表示该位置可以出现任何标签。如果我们要匹配任意标签后跟 DevRel
标签的节点序列,可以指定标签过滤器 *,+DevRel
。
DevRel
标签之间交替:MATCH (p:Person {name: "Praveena"})
CALL apoc.path.expandConfig(p, {
labelFilter: "*,+DevRel",
beginSequenceAtStart: false,
minLevel: 2,
maxLevel: 4
})
YIELD path
WHERE length(path) % 2 = 0
// Remove the Praveena node from the returned path
RETURN nodes(path)[1..] AS nodes, length(path) AS hops
ORDER BY hops;
节点 | 跳数 |
---|---|
[(:人物:现场部 {姓名: "Joe"}), (:人物:开发者关系部 {姓名: "Mark"})] |
2 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:开发者关系部 {姓名: "Mark"})] |
2 |
[(:人物:工程部 {姓名: "Zhen"}), (:人物:开发者关系部 {姓名: "Lju"})] |
2 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:开发者关系部 {姓名: "Mark"}), (:人物:产品部 {姓名: "Jake"}), (:人物:开发者关系部 {姓名: "Lju"})] |
4 |
[(:人物:现场部 {姓名: "Joe"}), (:人物:开发者关系部 {姓名: "Mark"}), (:人物:产品部 {姓名: "Jake"}), (:人物:开发者关系部 {姓名: "Lju"})] |
4 |
[(:人物:工程部 {姓名: "Zhen"}), (:人物:开发者关系部 {姓名: "Lju"}), (:人物:产品部 {姓名: "Jake"}), (:人物:开发者关系部 {姓名: "Mark"})] |
4 |