数据准备
neo4j 中,假设我有实体:
rca_alarm{name:'报警名称', appName:'对应应用名称', ip: '对应的虚拟机IP'}
rca_app{appName:'应用名称'}
rca_vm{ip:'xx.xx.xx.xx 虚拟机IP地址'}
rca_phy{ip:'xx.xx.xx.xx 物理机IP地址'}
rca_alarm 通过 ip 与 rca_vm 关联,关系名:alarm_to_vm rca_app 运行在固定的虚拟机上, 关系名:app_run_in_vm 虚拟机运行在在物理机上, 关系名:vm_run_in_phy
节点的唯一性:
rca_alarm name 唯一 rca_app appName 唯一 rca_vm ip 唯一 rca_phy ip 唯一
帮我构 2 组数据,给出对应的 neo4j 初始化语句。
rca_alarm->rca_vm
rca_app->rca_vm->rca_phy
保证一组数据中,4个节点之间是可以按照约定互相关联的。
初始化脚本
构建的两组数据和对应的 Neo4j 初始化语句。
第一组数据
- rca_alarm: 报警1,关联到
vm1
(IP: 192.168.1.1) - rca_app: 应用 A,运行在
vm1
上 - rca_vm: 虚拟机 1(IP: 192.168.1.1),运行在
phy1
(IP: 192.168.0.1)上 - rca_phy: 物理机 1(IP: 192.168.0.1)
第二组数据
- rca_alarm: 报警2,关联到
vm2
(IP: 192.168.2.2) - rca_app: 应用 B,运行在
vm2
上 - rca_vm: 虚拟机 2(IP: 192.168.2.2),运行在
phy2
(IP: 192.168.0.2)上 - rca_phy: 物理机 2(IP: 192.168.0.2)
Neo4j 初始化语句
// 第一组数据
CREATE (alarm1:rca_alarm {name: '报警1', appName: '应用A', ip: '192.168.1.1'})
CREATE (appA:rca_app {appName: '应用A'})
CREATE (vm1:rca_vm {ip: '192.168.1.1'})
CREATE (phy1:rca_phy {ip: '192.168.0.1'})
// 第一组数据的关系
CREATE (alarm1)-[:alarm_to_vm]->(vm1)
CREATE (appA)-[:app_run_in_vm]->(vm1)
CREATE (vm1)-[:vm_run_in_phy]->(phy1)
// 第二组数据
CREATE (alarm2:rca_alarm {name: '报警2', appName: '应用B', ip: '192.168.2.2'})
CREATE (appB:rca_app {appName: '应用B'})
CREATE (vm2:rca_vm {ip: '192.168.2.2'})
CREATE (phy2:rca_phy {ip: '192.168.0.2'})
// 第二组数据的关系
CREATE (alarm2)-[:alarm_to_vm]->(vm2)
CREATE (appB)-[:app_run_in_vm]->(vm2)
CREATE (vm2)-[:vm_run_in_phy]->(phy2)
这些语句将创建两组数据,并且节点之间按照约定的关系进行连接:
rca_alarm
与rca_vm
通过alarm_to_vm
关系关联。rca_app
与rca_vm
通过app_run_in_vm
关系关联。rca_vm
与rca_phy
通过vm_run_in_phy
关系关联。
每组数据中的节点都能按照约定正确地关联在一起。
整体的效果:
基础查询
基本查询
如何通过 rca_alarm 开始,查询 name: ‘报警1’ 的报警信息,将关联的 rca_alarm, rca_vm, rca_phy 都查询出来。
MATCH (alarm:rca_alarm {name: '报警1'})-[:alarm_to_vm]->(vm:rca_vm)-[:vm_run_in_phy]->(phy:rca_phy)
RETURN alarm, vm, phy
效果:
╒═══════════════════════════════════════════════════════════╤═════════════════════════════╤══════════════════════════════╕
│alarm │vm │phy │
╞═══════════════════════════════════════════════════════════╪═════════════════════════════╪══════════════════════════════╡
│(:rca_alarm {appName: "应用A",ip: "192.168.1.1",name: "报警1"})│(:rca_vm {ip: "192.168.1.1"})│(:rca_phy {ip: "192.168.0.1"})│
└───────────────────────────────────────────────────────────┴─────────────────────────────┴──────────────────────────────┘
APOC 的的路径拓展
抱歉给您带来了困扰!报错的原因是因为 apoc.path.expand
需要更多的参数,而我们缺少了一些必要的参数。
具体来说,apoc.path.expand
期望提供的参数是:
- startNode:起始节点。
- relationshipFilter:用于过滤关系类型的字符串。
- labelFilter:用于过滤节点标签的字符串(如果不需要,可以传空字符串
""
)。 - minDepth:最小深度(从
startNode
出发的关系链的最小级别)。 - maxDepth:最大深度(从
startNode
出发的关系链的最大级别)。
我们需要根据这些要求来调整查询。
APOC 查询
MATCH (alarm:rca_alarm {name: '报警1'})
CALL apoc.path.expand(
alarm, // 起始节点
"alarm_to_vm|app_run_in_vm|vm_run_in_phy", // 关系类型过滤器
"", // 节点标签过滤器,空字符串表示不进行标签过滤
1, // 最小深度:从 alarm 到 vm 至少1级
3 // 最大深度:到 app 和 phy 最多3级
) YIELD path
RETURN path
解释:
- 起始节点
alarm
:查询从rca_alarm
(name: '报警1'
)节点开始。 - 关系类型过滤器
"alarm_to_vm|app_run_in_vm|vm_run_in_phy"
:指定我们希望通过这些关系类型进行路径扩展。 - 节点标签过滤器
""
:空字符串表示我们不对节点的标签进行任何过滤,这样所有类型的节点都会被匹配。 - 最小深度
1
:表示最小路径深度从rca_alarm
到rca_vm
(1级)。 - 最大深度
3
:表示最大路径深度为3,即扩展到rca_app
和rca_phy
。
结果:
该查询将从 rca_alarm
节点开始,沿着指定的关系类型路径扩展,直到到达 rca_app
和 rca_phy
节点,返回路径。
路径中将包含每一个经过的节点和关系。
这个实际上关注的是路径:
╒══════════════════════════════════════════════════════════════════════╕
│path │
╞══════════════════════════════════════════════════════════════════════╡
│(:rca_alarm {name: "报警1",appName: "应用A",ip: "192.168.1.1"})-[:alarm_to│
│_vm]->(:rca_vm {ip: "192.168.1.1"}) │
├──────────────────────────────────────────────────────────────────────┤
│(:rca_alarm {name: "报警1",appName: "应用A",ip: "192.168.1.1"})-[:alarm_to│
│_vm]->(:rca_vm {ip: "192.168.1.1"})<-[:app_run_in_vm]-(:rca_app {appNa│
│me: "应用A"}) │
├──────────────────────────────────────────────────────────────────────┤
│(:rca_alarm {name: "报警1",appName: "应用A",ip: "192.168.1.1"})-[:alarm_to│
│_vm]->(:rca_vm {ip: "192.168.1.1"})-[:vm_run_in_phy]->(:rca_phy {ip: "│
│192.168.0.1"}) │
└──────────────────────────────────────────────────────────────────────┘
APOC 路径扩展 vs 子图搜索
在 APOC 中,路径扩展 (apoc.path.expand
) 和子图搜索 (apoc.path.subgraphNodes
) 是两种不同的图遍历方式,它们的主要区别在于 查询的结果 和 应用场景。
1. apoc.path.expand
— 路径扩展
apoc.path.expand
主要用于从一个起始节点出发,沿着指定的关系类型扩展路径。
它是一个 路径查询,结果是一个或多个路径,可以包含多个节点和关系,路径会根据给定的关系类型和深度限制逐层扩展。
特点:
- 起始节点:你从一个特定的节点开始扩展。
- 关系类型过滤:你可以指定要遵循的关系类型,过滤不需要的关系。
- 最大/最小深度:你可以限制路径扩展的最大和最小深度。
- 路径结果:返回的是一条或多条完整的路径,路径中包括经过的节点和关系。
使用场景:
- 查找从一个节点出发的特定路径。
- 当你需要返回路径的完整信息(节点和关系)时。
2. apoc.path.subgraphNodes
— 子图搜索
apoc.path.subgraphNodes
则是用来查找一个子图(subgraph)的,它返回的是 一个子图中的所有节点,并且可以过滤节点和关系的类型。
不同于 expand
查询路径,subgraphNodes
更侧重于查找与某个节点相关的完整子图,而不是查找路径。
特点:
-
子图查询:返回的是一个子图,包含从起始节点出发的所有相关节点和关系。
-
节点/关系过滤:你可以指定需要包含的节点和关系类型,还可以限制子图中包含的最大深度。
-
没有返回路径:它返回的是子图中的节点和关系,不是路径,因此不关心路径的具体顺序。
使用场景:
-
当你希望查询与某个节点相关的 所有节点和关系,并且不关心路径的顺序或具体细节时。
-
适用于寻找与某个节点直接或间接相关的所有节点,并可以做过滤。
示例:
从 rca_alarm
开始,查找与其相关的所有节点(rca_vm
、rca_phy
、rca_app
):
MATCH (alarm:rca_alarm {name: '报警1'})
CALL apoc.path.subgraphNodes(
alarm, // 起始节点
{relationshipFilter: "alarm_to_vm|app_run_in_vm|vm_run_in_phy", // 关系类型过滤器
labelFilter: "rca_vm|rca_phy|rca_app", // 节点标签过滤器
maxDepth: 3} // 最大深度
) YIELD node
RETURN node
返回的是每一个节点信息:
╒═══════════════════════════════════════════════════════════╕
│node │
╞═══════════════════════════════════════════════════════════╡
│(:rca_alarm {name: "报警1",appName: "应用A",ip: "192.168.1.1"})│
├───────────────────────────────────────────────────────────┤
│(:rca_vm {ip: "192.168.1.1"}) │
├───────────────────────────────────────────────────────────┤
│(:rca_app {appName: "应用A"}) │
├───────────────────────────────────────────────────────────┤
│(:rca_phy {ip: "192.168.0.1"}) │
└───────────────────────────────────────────────────────────┘
主要区别总结:
特性 | apoc.path.expand |
apoc.path.subgraphNodes |
---|---|---|
查询目的 | 查找路径,返回的是路径信息(节点+关系) | 查找子图,返回的是所有相关节点和关系 |
结果 | 返回一条或多条完整路径 | 返回符合条件的所有节点和关系 |
路径方向 | 关系方向有明确的规定,可以控制正向或逆向 | 不关注路径方向,更多关注节点间的连接关系 |
适用场景 | 查找从某节点出发的特定路径 | 查找一个节点的相关子图,特别适用于图谱探索 |
返回内容 | 返回路径,包含节点和关系 | 返回节点和关系,但不包含路径结构 |
路径深度 | 可以设置最小和最大深度 | 设置最大深度,并能返回所有相关节点 |
总结
- 路径扩展 (
apoc.path.expand
):适用于查找路径和逐步扩展路径。它能帮助你清晰地了解从一个节点出发的路径及其经过的关系。 - 子图搜索 (
apoc.path.subgraphNodes
):适用于查找某个节点的相关子图,返回的是节点和关系的集合,而不关心路径的顺序。
选择哪种方法取决于您的查询需求。如果您需要获得路径信息,使用 apoc.path.expand
,如果只关心与某个节点相关的所有节点和关系,使用 apoc.path.subgraphNodes
。
返回节点+边
apoc.path.subgraphAll
是 APOC 库中的一个强大工具,用于从指定的起始节点出发,查找与其相关的所有节点和关系,返回的是一个完整的子图。
与 apoc.path.subgraphNodes
类似,subgraphAll
也是用于查找与某个节点相关的子图,但它的不同之处在于,subgraphAll
会返回整个子图,包括所有的 节点 和 关系,而 subgraphNodes
只返回节点。
apoc.path.subgraphAll
的特点:
-
返回完整的子图:不仅返回节点,还返回所有的关系。你可以得到一个完整的图形结构,而不仅仅是节点。
-
路径搜索:与
apoc.path.expand
不同,apoc.path.subgraphAll
不是逐个扩展路径,而是直接搜索整个子图中的所有节点和关系。 -
节点和关系过滤:可以选择性地过滤某些类型的节点和关系。
-
最大深度限制:你可以设置子图搜索的最大深度。
语法
apoc.path.subgraphAll(startNode, {relationshipFilter: STRING, labelFilter: STRING, maxDepth: INTEGER})
-
startNode: 起始节点。
-
relationshipFilter: 用于过滤关系的字符串,可以使用
|
连接多个关系类型。 -
labelFilter: 用于过滤节点标签的字符串,多个标签之间用
|
连接。 -
maxDepth: 限制最大深度,默认为
3
。
例子 1:查找与 rca_alarm
节点相关的所有节点和关系
查询从 rca_alarm
节点出发,获取所有相关节点和关系,可以使用 apoc.path.subgraphAll
:
结果:
╒══════════════════════════════════════════════════════════════════════╤════════════════════════════════════════════════════╕
│nodes │relationships │
╞══════════════════════════════════════════════════════════════════════╪════════════════════════════════════════════════════╡
│[(:rca_alarm {name: "报警1",appName: "应用A",ip: "192.168.1.1"}), (:rca_vm│[[:alarm_to_vm], [:vm_run_in_phy], [:app_run_in_vm]]│
│ {ip: "192.168.1.1"}), (:rca_phy {ip: "192.168.0.1"}), (:rca_app {appN│ │
│ame: "应用A"})] │ │
└──────────────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────┘
例子 2:查找整个子图,包括所有相关节点和关系(没有过滤)
如果您不想限制节点和关系的类型,您可以去掉过滤器,查询整个子图:
MATCH (alarm:rca_alarm {name: '报警1'})
CALL apoc.path.subgraphAll(
alarm,
{} // 没有任何过滤器
) YIELD nodes, relationships
RETURN nodes, relationships
在这个查询中,apoc.path.subgraphAll
会返回与 rca_alarm
相关的所有节点和关系,直到达到默认的最大深度(3)。
结果:
╒══════════════════════════════════════════════════════════════════════╤════════════════════════════════════════════════════╕
│nodes │relationships │
╞══════════════════════════════════════════════════════════════════════╪════════════════════════════════════════════════════╡
│[(:rca_alarm {name: "报警1",appName: "应用A",ip: "192.168.1.1"}), (:rca_vm│[[:alarm_to_vm], [:vm_run_in_phy], [:app_run_in_vm]]│
│ {ip: "192.168.1.1"}), (:rca_phy {ip: "192.168.0.1"}), (:rca_app {appN│ │
│ame: "应用A"})] │ │
└──────────────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────┘