扩展路径
过程概览
该过程描述如下
限定名称 | 类型 |
---|---|
apoc.path.expand |
|
参数语法
此过程接受以下参数
-
start
- 节点或节点 ID 列表 -
relationshipFilter
- 要扩展的关系类型 -
labelFilter
- 要扩展的节点标签 -
minLevel
- 遍历中的最小跳数 -
maxLevel
- 遍历中的最大跳数
关系过滤器
关系过滤器的语法描述如下
语法:[<]RELATIONSHIP_TYPE1[>]|[<]RELATIONSHIP_TYPE2[>]|…
输入 | 类型 | 方向 |
---|---|---|
|
|
出站 |
|
|
入站 |
|
|
双向 |
|
|
出站 |
|
|
入站 |
标签过滤器
标签过滤器的语法描述如下
语法:[+-/>]LABEL1|LABEL2|*|…
符号 | 过滤类型 | 输入示例 | 描述 |
---|---|---|---|
|
黑名单 |
|
路径中没有任何节点会带有黑名单中存在的标签。 |
|
白名单 |
|
路径中的所有节点必须具有白名单中的标签(如果使用终止节点和末端节点过滤器,则这些节点除外)。如果没有白名单运算符,则允许所有标签。 |
|
终止 |
|
仅返回到达具有给定标签的节点的路径,并停止在此节点之外的进一步扩展。终止节点不必遵守白名单。终止过滤优先于末端节点过滤。 |
|
末端节点 |
|
仅返回到达具有给定标签的节点的路径,但会继续扩展以匹配其后的末端节点。末端节点不必遵守白名单即可返回,但只有当节点具有白名单中的标签时,才允许在其后进行扩展。 |
|
复合标签 |
|
这会返回标签的合取,例如 /Foo:Bar 表示终止节点必须同时匹配 |
示例
本节中的示例基于以下示例图
MERGE (mark:Person:DevRel {name: "Mark"})
MERGE (praveena:Person:Engineering {name: "Praveena"})
MERGE (joe:Person:Field {name: "Joe"})
MERGE (lju:Person:DevRel {name: "Lju"})
MERGE (zhen:Person:Engineering {name: "Zhen"})
MERGE (stefan:Person:Field {name: "Stefan"})
MERGE (alicia:Person:Product {name: "Alicia"})
MERGE (martin:Person:Engineering {name: "Martin"})
MERGE (jake:Person:Product {name: "Jake"})
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 (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)
下面的 Neo4j 浏览器可视化展示了示例图
KNOWS
关系类型被认为是双向的,即如果 Zhen 认识 Stefan,则可以推断 Stefan 认识 Zhen。使用 KNOWS
关系时,我们将忽略方向。
FOLLOWS
关系有方向性,因此在使用时我们将指定方向。
让我们从 Praveena 节点开始扩展路径。我们只想考虑 KNOWS
关系类型,因此将其指定为关系过滤器。
KNOWS
的人,从 1 跳到 2 跳的路径MATCH (p:Person {name: "Praveena"})
CALL apoc.path.expand(p, "KNOWS", null, 1, 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
标签的路径。
KNOWS
的路径,其中只包含 Engineering
人员,从 1 跳到 2 跳MATCH (p:Person {name: "Praveena"})
CALL apoc.path.expand(p, "KNOWS", "+Engineering", 1, 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.expand(p, "FOLLOWS>|KNOWS", "", 1, 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 的连接非常广!
我们还可以使用标签过滤器指定遍历终止条件。如果希望在遍历遇到包含 Engineering
标签的节点时立即终止遍历,我们可以使用 /Engineering
节点过滤器。
FOLLOWS
或 KNOWS
的人,从 1 跳到 3 跳的路径,一旦到达带有 Engineering
标签的节点即终止MATCH (p:Person {name: "Alicia"})
CALL apoc.path.expand(p, "FOLLOWS>|KNOWS", "/Engineering", 1, 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.expand(p, "FOLLOWS>|KNOWS", ">Engineering", 1, 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 的路径!