数据准备

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_alarmrca_vm 通过 alarm_to_vm 关系关联。
  • rca_apprca_vm 通过 app_run_in_vm 关系关联。
  • rca_vmrca_phy 通过 vm_run_in_phy 关系关联。

每组数据中的节点都能按照约定正确地关联在一起。

整体的效果:

Neo4j Graph VisualizationCreated using Neo4j (http://www.neo4j.com/)alarm_to_vmapp_run_in_vmvm_run_in_phyalarm_to_vmapp_run_in_vmvm_run_in_phy 报警1 应用A 192.168.… 192.168.… 报警2 应用B 192.168.… 192.168.…

基础查询

基本查询

如何通过 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 期望提供的参数是:

  1. startNode:起始节点。
  2. relationshipFilter:用于过滤关系类型的字符串。
  3. labelFilter:用于过滤节点标签的字符串(如果不需要,可以传空字符串 "")。
  4. minDepth:最小深度(从 startNode 出发的关系链的最小级别)。
  5. 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

解释:

  1. 起始节点 alarm:查询从 rca_alarmname: '报警1')节点开始。
  2. 关系类型过滤器 "alarm_to_vm|app_run_in_vm|vm_run_in_phy":指定我们希望通过这些关系类型进行路径扩展。
  3. 节点标签过滤器 "":空字符串表示我们不对节点的标签进行任何过滤,这样所有类型的节点都会被匹配。
  4. 最小深度 1:表示最小路径深度从 rca_alarmrca_vm(1级)。
  5. 最大深度 3:表示最大路径深度为3,即扩展到 rca_apprca_phy

结果:

该查询将从 rca_alarm 节点开始,沿着指定的关系类型路径扩展,直到到达 rca_apprca_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"})                                                        │
└──────────────────────────────────────────────────────────────────────┘
Neo4j Graph VisualizationCreated using Neo4j (http://www.neo4j.com/)alarm_to_vmapp_run_in_vmvm_run_in_phy 报警1 192.168.… 应用A 192.168.…

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_vmrca_phyrca_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"})]                                                         │                                                    │
└──────────────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────┘

参考资料

Neo4j安装插件

neo4j手动安装插件