apoc.path.expandConfig过程
|
此过程不建议在多线程环境中运行。因此,并行运行时不支持此过程。有关详细信息,请参阅 Cypher 手册 → 并行运行时。 |
语法 |
|
||
描述 |
返回从起始 |
||
输入参数 |
名称 |
类型 |
描述 |
|
|
算法开始的起始节点。 |
|
|
|
|
|
返回参数 |
名称 |
类型 |
描述 |
|
|
扩展后的路径。 |
|
配置参数
此过程支持以下配置参数
| 名称 | 类型 | 默认 | 描述 |
|---|---|---|---|
|
|
-1 |
遍历中的最小跳数 |
|
|
-1 |
遍历中的最大跳数 |
|
|
null |
要遍历的关系类型和方向。 请参阅 关系过滤器。 |
|
|
null |
要遍历的节点标签。 请参阅 标签过滤器。 |
|
|
null |
逗号分隔的标签和关系过滤器,用于重复序列中的每个步骤。如果存在, 请参阅 指定节点标签和关系类型的序列。 |
|
|
true |
从距离起始节点一步远的位置开始匹配节点标签和/或关系类型序列(在 |
|
|
RELATIONSHIP_PATH |
在遍历中扩展关系时使用的策略。 请参阅 唯一性。 |
|
|
true |
遍历时使用广度优先搜索。如果设置为 |
|
|
false |
|
|
|
-1 |
限制返回的路径数量。当使用 |
|
|
false |
路径扩展是否可选?如果设置为 |
|
|
null |
只有这些节点可以作为返回路径的终点,如果可能,扩展将继续经过这些节点。 |
|
|
null |
只有这些节点可以作为返回路径的终点,且扩展不会继续超过这些节点。 |
|
|
null |
扩展中只允许这些节点存在(如果存在 endNodes 和 terminatorNodes,它们也将被允许)。 |
|
|
null |
返回的任何路径都不会包含这些节点。 |
|
|
null |
请参阅 allowlistNodes。 |
|
|
null |
请参阅 denylistNodes。 |
关系过滤器
关系过滤器的语法描述如下
语法: [<]RELATIONSHIP_TYPE1[>]|[<]RELATIONSHIP_TYPE2[>]|…
| input | type | 方向 |
|---|---|---|
|
|
传出 |
|
|
传入 |
|
|
两者 |
|
|
传出 |
|
|
传入 |
标签过滤器
标签过滤器的语法描述如下
语法: [+-/>]LABEL1|LABEL2|*|…
| 符号 | 过滤器类型 | 输入示例 | 描述 |
|---|---|---|---|
|
黑名单 |
|
路径中没有任何节点的标签存在于黑名单中。 |
|
白名单 |
|
路径中的所有节点必须具有白名单中的标签(如果使用这些过滤器,则豁免终止和结束节点)。如果不存在白名单运算符,则允许所有标签。 |
|
终止 |
|
仅返回到具有给定标签的节点的路径,并在其后停止进一步扩展。终止节点不必遵守白名单。终止过滤优先于结束节点过滤。 |
|
结束节点 |
|
仅返回到具有给定标签的节点的路径,但继续扩展以匹配其后的结束节点。结束节点无需遵守白名单即可返回,但只有在节点在白名单中有标签时才允许在其后进行扩展。 |
|
复合标签 |
|
这返回标签的合取,例如 /Foo:Bar 意味着终止节点必须同时匹配 |
标签过滤器运算符的优先级和行为
同时允许使用多个标签过滤器运算符。以以下示例为例
labelFilter:'+Person|Movie|-SciFi|>Western|/Romance'
如果我们处理此标签过滤器,我们可以看到
-
:Person和:Movie标签被列入白名单 -
:SciFi被列入黑名单 -
:Western是结束节点标签 -
:Romance是终止标签。
运算符评估的优先级不依赖于它们在 labelFilter 中的位置,而是固定的
黑名单过滤器 -,终止过滤器 /,结束节点过滤器 >,白名单过滤器 +。
这意味着
-
没有黑名单标签
-会出现在返回路径的节点中,即使相同的标签(或带有黑名单标签的节点的另一个标签)包含在另一个过滤器列表中。 -
如果使用终止过滤器
/或结束节点过滤器>,则只会返回到具有这些标签的节点的路径。这些结束节点不受白名单过滤器的约束。 -
如果一个节点是终止节点
/,则不会在该节点之后进行进一步扩展。 -
白名单仅适用于除终止或结束节点过滤器指定的结束节点之外的节点。如果没有出现结束节点或终止节点运算符,则白名单适用于路径的所有节点。
-
如果 labelFilter 中没有出现白名单运算符,则视为所有标签都在白名单中。
唯一性
节点和关系的唯一性指导扩展和返回的结果。下表描述了可用值
| 值 | description(描述) |
|---|---|
|
对于每个返回的节点,存在一条从起始 |
|
一个节点不能被遍历超过一次。这与传统遍历框架的行为一致。 |
|
同一层级上的实体保证是唯一的。 |
|
对于每个返回的节点,存在一条从起始 |
|
这类似于 NODE_GLOBAL,但仅保证最近访问的节点之间的唯一性,且计数可配置。遍历巨大的图是非常占用内存的,因为它需要跟踪所有已访问的节点。对于巨大的图,遍历器可能会占用 JVM 中的所有内存,从而导致 OutOfMemoryError。结合此唯一性,您可以提供一个计数,即最近访问的节点数量。这可能会导致节点被访问多次,但可以实现无限扩展。 |
|
一个关系不能被遍历超过一次,而节点可以。 |
|
同一层级上的实体保证是唯一的。 |
|
与 NODE_RECENT 相同,但针对关系。 |
|
无限制(用户需要自行管理) |
指定节点标签和关系类型的序列
路径扩展器过程可以对标签、关系类型或两者的重复序列进行扩展。序列可以按如下方式定义
-
如果仅使用标签序列,请使用
labelFilter,但使用逗号分隔重复序列中每一步的过滤条件。 -
如果仅使用关系序列,请使用
relationshipFilter,但使用逗号分隔重复序列中每一次关系遍历的过滤条件。 -
如果使用关系和标签序列,请使用
sequence参数。
| 用法 | 配置参数 | description(描述) | 语法 | 说明 |
|---|---|---|---|---|
仅标签序列 |
|
语法和过滤器相同,但使用逗号 ( |
|
起始节点必须是 :Post 节点且不能是 :Blocked,下一个节点必须是 :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;
| path | 跳数 |
|---|---|
(: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。
Engineering 人员的路径,这些人是 Praveena KNOWS 的,跳数为 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;
| path | 跳数 |
|---|---|
(: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;
| path | 跳数 |
|---|---|
(: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;
| path | 跳数 |
|---|---|
(: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;
| path | 跳数 |
|---|---|
(: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;
| path | 跳数 |
|---|---|
(: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;
| path | 跳数 |
|---|---|
(: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 再到 Joe。但我们也得到了一条额外的路径,从 Alicia 到 Joe 再到 Praveena 再回到 Joe。
白名单节点和黑名单节点
也可以指定白名单和黑名单节点。
让我们基于之前找到 Alicia KNOWS 或 FOLLOWS 的人的查询进行构建。我们希望任何返回的路径仅包含 Mark、Joe、Zhen 和 Praveena 节点,我们可以通过将这些节点传递给 allowlistNodes 参数来实现这一点。
FOLLOWS 或 KNOWS 关系类型且跳数为 1 到 3 跳的 Alicia 路径,仅包含包含 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;
| path | 跳数 |
|---|---|
(: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;
| path | 跳数 |
|---|---|
(: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 跳,使用 BFSMATCH (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;
| path | 跳数 |
|---|---|
(: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 跳,使用 DFSMATCH (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;
| path | 跳数 |
|---|---|
(: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 之间交替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;
| path | 跳数 |
|---|---|
(: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 关系类型的路径。如果我们想确保此 relationshipFilter 中定义的关系类型序列至少匹配一次,我们需要使用 minLevel 为 2,因为过滤器中有两种关系类型。
FOLLOWS 和 KNOWS 之间交替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;
| path | 跳数 |
|---|---|
(: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 中定义的第一个关系类型仅适用于起始节点。
FOLLOWS 和 KNOWS 之间交替,在首先从 Jake 跟随 KNOWS 关系之后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;
| path | 跳数 |
|---|---|
(: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 标签之间交替。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;
| path | 跳数 |
|---|---|
(: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 标签之间交替。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;
| path | 跳数 |
|---|---|
(: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 标签之间交替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;
| path | 跳数 |
|---|---|
(: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 |
(: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:Engineering {name: "Praveena"}) |
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: "Zhen"})-[:KNOWS]→(: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 |
(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(: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 |
(:Person:Engineering {name: "Praveena"})←[:FOLLOWS]-(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Field {name: "Stefan"})←[:FOLLOWS]-(:Person:DevRel {name: "Mark"}) |
4 |
(:Person:Engineering {name: "Praveena"})←[:FOLLOWS]-(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"}) |
4 |
(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Field {name: "Stefan"})←[:FOLLOWS]-(:Person:DevRel {name: "Mark"}) |
4 |
(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"})←[:KNOWS]-(:Person:Engineering {name: "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;
| 节点 | 跳数 |
|---|---|
[(:Person:Field {name: "Joe"}), (:Person:Sales {name: "Anthony"})] |
2 |
[(:Person:Field {name: "Joe"}), (:Person:Engineering {name: "Zhen"})] |
2 |
[(:Person:Field {name: "Joe"}), (:Person:Product {name: "Alicia"})] |
2 |
[(:Person:Field {name: "Joe"}), (:Person:DevRel {name: "Mark"})] |
2 |
[(:Person:Field {name: "Joe"}), (:Person:Engineering {name: "Praveena"})] |
2 |
[(:Person:Field {name: "Joe"}), (:Person:Sales {name: "Anthony"})] |
2 |
[(:Person:Field {name: "Joe"}), (:Person:Engineering {name: "Zhen"})] |
2 |
[(:Person:Field {name: "Joe"}), (:Person:Product {name: "Alicia"})] |
2 |
[(:Person:Field {name: "Joe"}), (:Person:Engineering {name: "Praveena"})] |
2 |
[(:Person:Field {name: "Joe"}), (:Person:DevRel {name: "Mark"})] |
2 |
[(:Person:Field {name: "Joe"}), (:Person:Engineering {name: "Zhen"}), (:Person:Field {name: "Stefan"}), (:Person:DevRel {name: "Mark"})] |
4 |
[(:Person:Field {name: "Joe"}), (:Person:DevRel {name: "Mark"}), (:Person:Field {name: "Stefan"}), (:Person:Engineering {name: "Zhen"})] |
4 |
[(:Person:Field {name: "Joe"}), (:Person:Engineering {name: "Zhen"}), (:Person:Field {name: "Stefan"}), (:Person:DevRel {name: "Mark"})] |
4 |
[(:Person:Field {name: "Joe"}), (:Person:DevRel {name: "Mark"}), (:Person:Field {name: "Stefan"}), (:Person:Engineering {name: "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;
| 节点 | 跳数 |
|---|---|
[(:Person:Field {name: "Joe"}), (:Person:DevRel {name: "Mark"})] |
2 |
[(:Person:Field {name: "Joe"}), (:Person:DevRel {name: "Mark"})] |
2 |
[(:Person:Engineering {name: "Zhen"}), (:Person:DevRel {name: "Lju"})] |
2 |
[(:Person:Field {name: "Joe"}), (:Person:DevRel {name: "Mark"}), (:Person:Product {name: "Jake"}), (:Person:DevRel {name: "Lju"})] |
4 |
[(:Person:Field {name: "Joe"}), (:Person:DevRel {name: "Mark"}), (:Person:Product {name: "Jake"}), (:Person:DevRel {name: "Lju"})] |
4 |
[(:Person:Engineering {name: "Zhen"}), (:Person:DevRel {name: "Lju"}), (:Person:Product {name: "Jake"}), (:Person:DevRel {name: "Mark"})] |
4 |