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[>]|…
| 输入 | 类型 | 方向 |
|---|---|---|
|
|
出站 |
|
|
入站 |
|
|
双向 |
|
|
出站 |
|
|
入站 |
标签过滤器
标签过滤器的语法描述如下
语法:[+-/>]LABEL1|LABEL2|*|…
| 符号 | 过滤器类型 | 输入示例 | 描述 |
|---|---|---|---|
|
拒绝列表 |
|
路径中的任何节点都不会具有拒绝列表中存在的标签。 |
|
允许列表 |
|
路径中的所有节点都必须具有允许列表中的标签(终止节点和结束节点除外,如果使用这些过滤器)。如果没有允许列表操作符,则允许所有标签。 |
|
终止 |
|
只返回到具有给定标签的节点为止的路径,并停止超出该节点的进一步扩展。终止节点不必遵守允许列表。终止过滤优先于结束节点过滤。 |
|
结束节点 |
|
只返回到具有给定标签的节点为止的路径,但继续扩展以匹配超出该节点的结束节点。结束节点不必遵守允许列表即可返回,但只有当节点具有允许列表中的标签时,才允许超出它们的扩展。 |
|
复合标签 |
|
这返回标签的合取,例如 /Foo:Bar 意味着终止节点必须同时匹配 |
标签过滤器操作符优先级和行为
允许多个标签过滤器操作符同时存在。以下面的示例为例
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的人的路径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。
KNOWS的仅包含Engineering人员的路径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的人员路径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的人员的路径,在遇到带有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的人员的路径,其中路径以带有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的人员的路径,在达到 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的人员的路径,其中路径在到达 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 到 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;
| 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的人员的路径,排除包含 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的人员的 10 条路径,使用 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的人员的 10 条路径,使用 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 |