Neo4j 数据库使用文档

0 阅读15分钟

Neo4j 数据库使用文档

本文档详细介绍 Neo4j 图形数据库的使用,涵盖核心概念、Cypher 查询语言、Docker 安装、Spring Boot 整合、性能优化及图数据科学等核心主题。

📚 相关文档


💡 创作不易,请点个收藏关注!


目录


一、Neo4j 简介

1.1、什么是 Neo4j

Neo4j [ˈniːoʊ dʒɔːrˈdʒiːə] 发音为"霓-欧,卓尔-吉亚",是一款领先的开源图形数据库

1.1.1、基本概念

Neo4j 属于 NoSQL 数据库的一种,但与传统 NoSQL 数据库(如 MongoDB 的文档存储、Redis 的键值存储)不同,Neo4j 专门用于存储和查询图形结构的数据

📌 关键点:Neo4j 并不是用图形来"展示"数据,而是用图形的方式"存储"数据
1.1.2、数据存储方式对比
数据库类型存储方式适用场景
关系型数据库(MySQL)按行存储在表中,通过外键关联结构化数据、事务性数据
文档数据库(MongoDB)存储 JSON 文档半结构化数据
键值数据库(Redis)Key-Value 对缓存、session 存储
图形数据库(Neo4j)节点 + 关系高度关联的数据
1.1.3、图形数据示例
传统关系型数据库表示"用户关注"关系:
┌──────────┐     ┌──────────┐
│ 用户A表  │────>│ 用户B表  │
│ id: 1    │     │ id: 2    │
│ name: 张三│     │ name: 李四│
└──────────┘     └──────────┘
   ↓
   需要一个中间表来存储关系:
   ┌──────────────┐
   │ 关注关系表    │
   │ user_id: 1   │
   │ follow_id: 2 │
   │ create_time  │
   └──────────────┘

Neo4j 表示"用户关注"关系:
┌─────┐        关注        ┌─────┐
│张三 │ ───────────────> │李四 │
└─────┘                  └─────┘
   直接用关系连接,无需中间表!

1.2、为什么选择图形数据库

1.2.1、关系型数据库的痛点

当查询涉及多层关联时,关系型数据库会变得非常复杂:

-- 查询"张三关注的人关注了哪些商品"
SELECT p.* FROM products p
JOIN user_follows uf1 ON uf1.follow_id = u2.id
JOIN users u1 ON uf1.user_id = u1.id
JOIN user_follows uf2 ON uf2.user_id = u2.id
JOIN users u2 ON uf2.follow_id = u2.id
WHERE u1.name = '张三'
-- 如果是 5 层、10 层关联呢?
1.2.2、Neo4j 的优势
-- 同样的查询在 Neo4j 中
MATCH (zhangSan:Person {name: '张三'})-[:关注*1..3]->(p:Product)
RETURN p
1.2.3、什么时候该用 Neo4j
✅ 推荐使用 Neo4j 的场景:
   - 数据之间的关系非常重要且复杂
   - 需要查询多层关系(如朋友的朋友)
   - 数据结构经常变化
   - 需要实时分析关系路径

❌ 不推荐使用 Neo4j 的场景:
   - 主要做简单的增删改查
   - 数据之间几乎没有关联
   - 需要强事务保证(ACID)
   - 数据量巨大但关系简单

1.3、核心概念详解

1.3.1、节点(Node)

什么是节点? 节点代表现实世界中的实体对象,类似于面向对象编程中的"对象"。

节点的组成部分:

┌─────────────────────────┐
│       Person 标签        │  ← 标签(Label):表示节点类型
├─────────────────────────┤
│  name: "张三"            │  ← 属性(Property):键值对
│  age: 25                │
│  gender: "男"           │
│  city: "北京"           │
└─────────────────────────┘

类比理解:

  • 如果把数据库比作 Excel,节点就像是一张表中的一行数据
  • 但节点比表中的行更强大,因为它可以同时拥有多个标签任意数量的属性

示例代码:

-- 创建一个简单的 Person 节点
CREATE (p:Person {name: "张三", age: 25})

-- 创建一个有多个标签的节点
CREATE (p:Person:VIP {name: "李四", level: 3})

-- 创建一个没有标签的节点(不推荐,但语法上允许)
CREATE (p {name: "王五"})
1.3.2、关系(Relationship)

什么是关系? 关系连接两个节点,表示它们之间的联系。关系是 Neo4j 的核心概念,也是它区别于其他数据库的关键。

关系的特点:

  1. 有方向性:关系有起点和终点,不能反向访问
  2. 有类型:每种关系都有一个类型,如 KNOWSWORKS_FOR
  3. 可以有属性:关系也可以存储数据

关系图示:

┌─────┐                    ┌─────┐
 张三  ──[关系:KNOWS]──>  李四 
└─────┘                    └─────┘
                             
      KNOWS 关系的属性:      
      since: 2020            
      via: "工作"            

类比理解:

  • 如果节点是 Excel 中的行,关系就像是行与行之间的引用链接
  • 但 Neo4j 的关系比链接更强大,它可以存储属性

示例代码:

-- 创建两个节点并建立关系
CREATE (zhangSan:Person {name: "张三"})
CREATE (liSi:Person {name: "李四"})
CREATE (zhangSan)-[:KNOWS {since: 2020, via: "大学同学"}]->(liSi)
1.3.3、属性(Property)

什么是属性? 属性是存储在节点和关系上的键值对,用于描述实体或关系的特征。

属性规则:

  1. 属性是可选的,节点和关系可以没有属性
  2. 属性值有多种类型:字符串、数字、布尔、数组等
  3. 同一个节点的不同属性之间没有顺序

支持的属性类型:

-- 字符串
{name: "张三"}

-- 数字
{age: 25, height: 175.5}

-- 布尔
{isVip: true, isActive: false}

-- 数组
{tags: ["程序员", "音乐", "旅游"]}

-- 嵌套对象(Neo4j 5.x 支持)
{address: {city: "北京", street: "中关村"}}

-- 空值(注意:Neo4j 中用 null 表示空值)
{title: null}
1.3.4、标签(Label)

什么是标签? 标签用于对节点进行分类和标记,类似于关系型数据库中的"表名"。

标签的特点:

  1. 一个节点可以有多个标签
  2. 标签用于索引和查询优化
  3. 标签是可选的,但建议总是添加

示例:

-- 一个人同时是用户和VIP客户
CREATE (p:Person:VIP:Customer {
    name: "张三",
    level: 3,
    points: 10000
})
1.3.5、路径(Path)

什么是路径? 路径是节点和关系的有序序列,表示从起点到终点的路线。

路径示例:

(张三)-[:KNOWS]->(李四)-[:KNOWS]->(王五)-[:KNOWS]->(赵六]
 ↑                                                    ↑
 起点                                                终点
-- 路径长度:3 跳(经过 3 条关系)
MATCH path = (zhangSan)-[:KNOWS*3]->(zhaoLiu)
RETURN path

1.4、Neo4j 的优势

优势说明举例
高性能原生图形存储,关系查询无需 JOIN查询"朋友的朋友"比 SQL 快 100 倍
直观易用数据模型符合人类思维习惯用画图的方式理解数据
灵活性高模式可选,随时可以修改结构添加新属性无需 ALTER TABLE
** Cypher 强大**声明式查询语言,学习成本低用类似英语的语法查询图形
生态完善多种语言驱动,可视化工具丰富Java、Python、JavaScript 都能用

1.5、Neo4j 的应用领域

🎯 典型应用场景:

社交网络 ──────────> 用户关系、好友推荐、朋友圈分析
                    (微信、微博、LinkedIn)

推荐系统 ──────────> 商品推荐、内容推荐、好友推荐
                    (京东、抖音、Netflix)

知识图谱 ──────────> 语义搜索、智能问答、实体关系
                    (百度百科、医疗知识库)

欺诈检测 ──────────> 关联分析、异常检测、风险评估
                    (金融风控、反洗钱)

基础设施管理 ──────> 依赖关系、故障定位、容量规划
                    (IT 运维、网络拓扑)

二、Cypher 查询语言

2.1、Cypher 入门基础

2.1.1、什么是 Cypher

Cypher 是 Neo4j 独创的图形查询语言,它的设计目标是让查询图形数据变得像画画一样简单

2.1.2、Cypher 符号约定
()   -- 圆括号表示节点
[]   -- 方括号表示关系
{}   -- 大括号表示属性
-->  -- 箭头表示关系方向
:    -- 冒号用于标签和关系类型
$    -- 美元符号用于参数
2.1.3、基本语法对应关系
SQL 语法                Cypher 语法
─────────────────────────────────────────
SELECT * FROM users     MATCH (u:User) RETURN u
WHERE id = 1            WHERE u.id = 1
JOIN ... ON ...         (a)-->(b) 或 (a)-->(b)
INSERT INTO ...          CREATE ...
UPDATE ... SET ...      SET ...
DELETE ...              DELETE ...

2.2、基本语法详解

2.2.1、创建节点
-- 语法:CREATE (变量名:标签 {属性})
-- 创建一个简单节点
CREATE (p:Person {name: "张三", age: 25})

-- 创建并返回(推荐做法,便于确认创建结果)
CREATE (p:Person {name: "李四", age: 30})
RETURN p

-- 创建多个节点
CREATE (p1:Person {name: "王五"}),
       (p2:Person {name: "赵六"}),
       (p3:Company {name: "阿里巴巴"})
RETURN p1, p2, p3
2.2.2、创建关系
-- 基础语法
-- MATCH 匹配节点,CREATE 创建关系
MATCH (a:Person {name: "张三"}), (b:Person {name: "李四"})
CREATE (a)-[:KNOWS]->(b)
RETURN a, b

-- 创建带属性的关系
MATCH (a:Person {name: "张三"}), (b:Person {name: "李四"})
CREATE (a)-[r:FOLLOWS {since: 2020, level: "亲密"}]->(b)
RETURN a, r, b

-- 关系方向的理解
-- 张三 --[KNOWS]--> 李四:张三 认识 李四
-- 张三 <--[KNOWS]-- 李四:李四 认识 张三
-- 注意:关系是单向的,但查询时可以忽略方向
2.2.3、匹配查询(MATCH)
-- MATCH:查找符合条件的节点
-- RETURN:返回结果

-- 查找所有 Person 节点
MATCH (p:Person)
RETURN p

-- 查找特定属性的节点
MATCH (p:Person {name: "张三", age: 25})
RETURN p

-- 使用 WHERE 子句(更灵活)
MATCH (p:Person)
WHERE p.name = "张三" AND p.age > 20
RETURN p

-- 查找有关系连接的节点
MATCH (p:Person)-[:KNOWS]->(other:Person)
WHERE p.name = "张三"
RETURN other
2.2.4、更新操作(SET)
-- 更新单个属性
MATCH (p:Person {name: "张三"})
SET p.age = 26
RETURN p

-- 更新多个属性
MATCH (p:Person {name: "张三"})
SET p.age = 26, p.city = "上海"
RETURN p

-- 添加新属性
MATCH (p:Person {name: "张三"})
SET p.email = "zhangsan@example.com"
RETURN p

-- 删除属性(设置为 null)
MATCH (p:Person {name: "张三"})
SET p.email = null
RETURN p
2.2.5、删除操作(DELETE)
-- 删除关系(先删除关系,再删除节点)
MATCH (a:Person)-[r:KNOWS]->(b:Person)
DELETE r

-- 删除节点(必须先删除所有关系)
MATCH (p:Person {name: "张三"})
OPTIONAL MATCH (p)-[r]-()  -- 匹配所有关联关系
DELETE p, r

-- 删除节点和关系(简写)
MATCH (p:Person {name: "张三"})
DETACH DELETE p

-- 删除所有内容(清空数据库)
MATCH (n) DETACH DELETE n

2.3、常用语句

2.3.1、创建示例数据

为了更好地学习,我们先创建一套示例数据:

-- 创建人物节点
CREATE (zhangsan:Person {name: "张三", age: 28, city: "北京"})
CREATE (lisi:Person {name: "李四", age: 25, city: "上海"})
CREATE (wangwu:Person {name: "王五", age: 30, city: "北京"})
CREATE (zhaoliu:Person {name: "赵六", age: 27, city: "深圳"})

-- 创建公司节点
CREATE (alibaba:Company {name: "阿里巴巴", industry: "互联网"})
CREATE (tencent:Company {name: "腾讯", industry: "互联网"})
CREATE (bytedance:Company {name: "字节跳动", industry: "互联网"})

-- 创建人物关系
MATCH (zhangsan:Person {name: "张三"}), (lisi:Person {name: "李四"})
CREATE (zhangsan)-[:KNOWS {since: 2020}]->(lisi)

MATCH (lisi:Person {name: "李四"}), (wangwu:Person {name: "王五"})
CREATE (lisi)-[:KNOWS {since: 2019}]->(wangwu)

MATCH (zhangsan:Person {name: "张三"}), (zhaoliu:Person {name: "赵六"})
CREATE (zhangsan)-[:KNOWS {since: 2021}]->(zhaoliu)

-- 创建工作关系
MATCH (zhangsan:Person {name: "张三"}), (alibaba:Company {name: "阿里巴巴"})
CREATE (zhangsan)-[:WORKS_FOR {role: "高级工程师", salary: 50000}]->(alibaba)

MATCH (lisi:Person {name: "李四"}), (tencent:Company {name: "腾讯"})
CREATE (lisi)-[:WORKS_FOR {role: "产品经理", salary: 40000}]->(tencent)

MATCH (wangwu:Person {name: "王五"}), (bytedance:Company {name: "字节跳动"})
CREATE (wangwu)-[:WORKS_FOR {role: "前端工程师", salary: 45000}]->(bytedance)
2.3.2、基本查询
-- 查询所有人物
MATCH (p:Person) RETURN p

-- 查询所有公司
MATCH (c:Company) RETURN c

-- 查询所有数据
MATCH (n) RETURN n
2.3.3、条件查询
-- 单一条件
MATCH (p:Person)
WHERE p.city = "北京"
RETURN p

-- 多条件(AND)
MATCH (p:Person)
WHERE p.city = "北京" AND p.age > 25
RETURN p

-- 多条件(OR)
MATCH (p:Person)
WHERE p.city = "北京" OR p.city = "上海"
RETURN p

-- 范围查询
MATCH (p:Person)
WHERE 25 <= p.age <= 30
RETURN p

-- IN 查询
MATCH (p:Person)
WHERE p.city IN ["北京", "上海", "广州"]
RETURN p
2.3.4、关系查询
-- 查询张三认识的人
MATCH (zhangsan:Person {name: "张三"})-[r:KNOWS]->(friend:Person)
RETURN friend

-- 查询认识张三的人(反向)
MATCH (friend:Person)-[r:KNOWS]->(zhangsan:Person {name: "张三"})
RETURN friend

-- 查询双向关系(不管方向)
MATCH (zhangsan:Person {name: "张三"})-[r:KNOWS]-(friend:Person)
RETURN friend

-- 查询所有关系类型
MATCH (n)-[r]->(m)
RETURN type(r), count(*) AS count
ORDER BY count DESC
2.3.5、路径查询
-- 查询直接关系(1跳)
MATCH (zhangsan:Person {name: "张三"})-[r:KNOWS]->(friend:Person)
RETURN friend

-- 查询2跳关系(朋友的朋友)
MATCH (zhangsan:Person {name: "张三"})-[:KNOWS*2]->(friend:Person)
RETURN friend

-- 查询1到3跳关系
MATCH (zhangsan:Person {name: "张三"})-[:KNOWS*1..3]->(friend:Person)
RETURN friend

-- 查找最短路径
MATCH (zhangsan:Person {name: "张三"}), (wangwu:Person {name: "王五"})
MATCH path = shortestPath((zhangsan)-[:KNOWS*]-(wangwu))
RETURN path

-- 查找所有路径
MATCH path = (zhangsan:Person {name: "张三"})-[:KNOWS*]-(wangwu:Person {name: "王五"})
RETURN path

2.4、Cypher 进阶

2.4.1、聚合函数
-- COUNT:计数
MATCH (p:Person)
RETURN count(p) AS totalPerson

-- COUNT DISTINCT:去重计数
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)
RETURN count(DISTINCT c) AS totalCompany

-- SUM:求和
MATCH (p:Person)-[r:WORKS_FOR]->(c:Company)
RETURN sum(r.salary) AS totalSalary

-- AVG:平均值
MATCH (p:Person)-[r:WORKS_FOR]->(c:Company)
RETURN avg(r.salary) AS avgSalary

-- MAX / MIN:最大/最小值
MATCH (p:Person)-[r:WORKS_FOR]->(c:Company)
RETURN max(r.salary) AS maxSalary, min(r.salary) AS minSalary

-- COLLECT:收集为列表
MATCH (p:Person)
RETURN p.city, collect(p.name) AS persons
2.4.2、分组查询
-- 按城市分组统计人数
MATCH (p:Person)
RETURN p.city AS city, count(p) AS count
ORDER BY count DESC

-- 按公司分组统计平均薪资
MATCH (p:Person)-[r:WORKS_FOR]->(c:Company)
RETURN c.name AS company, avg(r.salary) AS avgSalary
ORDER BY avgSalary DESC

-- 多维度分组
MATCH (p:Person)-[r:WORKS_FOR]->(c:Company)
RETURN p.city AS city, c.name AS company, count(p) AS count
2.4.3、WITH 子句

WITH 子句用于连接查询的各个部分,非常重要!

-- 场景:找出薪资高于平均值的员工
MATCH (p:Person)-[r:WORKS_FOR]->(c:Company)
WITH avg(r.salary) AS avgSalary
MATCH (p:Person)-[r:WORKS_FOR]->(c:Company)
WHERE r.salary > avgSalary
RETURN p.name, r.salary, c.name

-- 场景:先过滤再排序
MATCH (p:Person)
WHERE p.age > 25
WITH p ORDER BY p.age DESC
RETURN p LIMIT 3

-- 场景:收集结果继续处理
MATCH (p:Person)-[:KNOWS]->(friend:Person)
WITH p, collect(friend) AS friends
WHERE size(friends) > 1
RETURN p.name, size(friends) AS friendCount
2.4.4、UNWIND 子句

UNWIND 将列表展开为单独的行:

-- 场景:批量创建节点
UNWIND [{name: "甲", age: 20}, {name: "乙", age: 21}, {name: "丙", age: 22}] AS person
CREATE (p:Person {name: person.name, age: person.age})

-- 场景:批量创建关系
MATCH (p1:Person), (p2:Person)
WHERE p1.name = "张三" AND p2.name IN ["李四", "王五", "赵六"]
WITH p1, p2
CREATE (p1)-[:KNOWS]->(p2)

-- 场景:收集后展开去重
MATCH (p:Person)-[:KNOWS]->(f:Person)
WITH collect(DISTINCT f) AS friends
UNWIND friends AS friend
RETURN friend
2.4.5、UNION 和 UNION ALL
-- UNION:合并结果,自动去重
MATCH (p:Person)
WHERE p.city = "北京"
RETURN p.name AS name
UNION
MATCH (p:Person)
WHERE p.age > 28
RETURN p.name AS name

-- UNION ALL:合并结果,保留重复
MATCH (p:Person)
WHERE p.city = "北京"
RETURN p.name AS name
UNION ALL
MATCH (p:Person)
WHERE p.age > 28
RETURN p.name AS name
2.4.6、MERGE 语句

MERGE 是 Cypher 最强大的语句之一:如果存在就匹配,不存在则创建

-- 如果"张三"节点存在则匹配,不存在则创建
MERGE (p:Person {name: "张三", age: 28})
RETURN p

-- MERGE + ON CREATE:创建时设置属性
MERGE (p:Person {name: "孙七"})
ON CREATE SET p.created = timestamp(), p.city = "未知"
RETURN p

-- MERGE + ON MATCH:匹配时更新属性
MERGE (p:Person {name: "张三"})
ON MATCH SET p.lastSeen = timestamp(), p.loginCount = coalesce(p.loginCount, 0) + 1
RETURN p

-- 完整示例:创建或更新关系
MATCH (zhangsan:Person {name: "张三"})
MATCH (lisi:Person {name: "李四"})
MERGE (zhangsan)-[r:FOLLOWS]->(lisi)
ON CREATE SET r.since = date()
ON MATCH SET r.lastUpdated = timestamp()
RETURN r

2.5、常见错误与解决方案

2.5.1、忘记 MATCH 直接 CREATE
-- ❌ 错误:直接 CREATE 不存在的节点
CREATE (p:Person {name: "张三"})
-- 每次执行都会创建新节点,导致重复数据!

-- ✅ 正确:先匹配或检查
MERGE (p:Person {name: "张三"})
-- 或者
MATCH (p:Person {name: "张三"})
CREATE (p)-[:KNOWS]->(other)
2.5.2、关系必须连接两个节点
-- ❌ 错误:关系只连接一个节点
CREATE (p:Person {name: "张三"})-[r:KNOWS]->()

-- ✅ 正确:必须有两个节点
CREATE (p:Person {name: "张三"})-[r:KNOWS]->(q:Person {name: "李四"})
2.5.3、删除有关系的节点
-- ❌ 错误:直接删除有关系的节点
MATCH (p:Person {name: "张三"})
DELETE p
-- 报错:节点有关系,不能直接删除

-- ✅ 正确:先删除关系
MATCH (p:Person {name: "张三"})
DETACH DELETE p
-- DETACH DELETE 会先删除所有关系再删除节点
2.5.4、大小写敏感性
-- ❌ 错误:属性名大小写不一致
CREATE (p:Person {Name: "张三"})  -- 注意 N 是大写
MATCH (p:Person) WHERE p.name = "张三"  -- 注意 n 是小写
-- 不会匹配!因为 Name ≠ name

-- ✅ 正确:保持大小写一致
CREATE (p:Person {name: "张三"})
MATCH (p:Person) WHERE p.name = "张三"

三、Docker 安装与运行

3.1、环境准备

3.1.1、什么是 Docker

Docker 是一个容器化平台,可以简单理解为"轻量级虚拟机"。使用 Docker 安装 Neo4j 的好处:

✅ 优点:
   - 一键安装,无需手动配置环境
   - 环境隔离,不影响本机其他应用
   - 易于迁移,配置可以导出复用
   - 版本切换方便,想用什么版本就用什么版本

⚠️ 前提:
   - 需要先安装 Docker(docker --version 查看)
   - Docker 需要在后台运行
3.1.2、Docker 版本选择
版本说明推荐场景
community社区版,免费学习、开发、测试
enterprise企业版,付费生产环境
5.x最新稳定版新项目
4.x经典版本旧项目兼容

3.2、拉取镜像

# 拉取 Neo4j 社区版镜像(推荐)
docker pull neo4j:5.18.0-community

# 拉取最新版本
docker pull neo4j:latest

# 拉取特定版本
docker pull neo4j:5.16.0-community

3.3、运行容器

# 基础运行命令
docker run \
  --name neo4j \                          # 容器名称
  -p 7474:7474 \                          # Web UI 端口
  -p 7687:7687 \                          # Bolt 协议端口
  -e NEO4J_AUTH=neo4j/jayczx120 \         # 初始用户名/密码
  -v /Volumes/czx/dev_tool/docker/neo4j/data:/data \  # 数据持久化
  -v /Volumes/czx/dev_tool/docker/neo4j/logs:/logs \  # 日志持久化
  -d \                                    # 后台运行
  neo4j:5.18.0-community
参数详解
参数说明必填
--name neo4j容器名称,用于后续管理
-p 7474:7474Web UI 端口映射,访问 http://localhost:7474
-p 7687:7687数据库连接端口,Java/Python 等客户端连接用
-e NEO4J_AUTH初始用户名/密码,格式:用户名/密码
-v 数据目录将容器内数据映射到宿主机,重启不丢失建议
-d后台运行,不加则前台运行建议
--env NEO4J_PLUGINS安装插件,如 APOC、GDS可选
--memory限制内存使用,如 --memory=4g可选
常用运行示例
# 1. 最小配置运行
docker run --name neo4j -p 7474:7474 -p 7687:7687 -e NEO4J_AUTH=neo4j/password -d neo4j:latest

# 2. 带数据持久化
docker run --name neo4j \
  -p 7474:7474 -p 7687:7687 \
  -e NEO4J_AUTH=neo4j/password \
  -v ~/neo4j/data:/data \
  -v ~/neo4j/logs:/logs \
  -d neo4j:latest

# 3. 限制内存,安装常用插件
docker run --name neo4j \
  -p 7474:7474 -p 7687:7687 \
  -e NEO4J_AUTH=neo4j/password \
  -e NEO4J_PLUGINS='["apoc", "graph-data-science"]' \
  --memory=4g \
  -v ~/neo4j/data:/data \
  -d neo4j:latest

3.4、访问 Neo4j 浏览器

3.4.1、打开浏览器

打开浏览器,访问:http://localhost:7474

image.png

3.4.2、首次登录
  1. 浏览器显示登录界面
  2. 连接 URL:bolt://localhost:7687(已自动填充)
  3. 用户名:neo4j
  4. 密码:你在 NEO4J_AUTH 中设置的密码
  5. 点击"Connect"
3.4.3、Neo4j 浏览器界面介绍
┌─────────────────────────────────────────────────────────────┐
│  Neo4j Browser                                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  $ :play intro                                              │
│                                                             │
│  ┌───────────────────────────────────────────────────────┐  │
│  │                                                       │  │
│  │              Cypher 编辑器                             │  │
│  │              (输入 Cypher 查询语句)                    │  │
│  │                                                       │  │
│  │  MATCH (n) RETURN n  LIMIT 25                         │  │
│  │                                                       │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                             │
│  [▶ Run]  [⏹ Stop]  [⇥ History]  [⭐ Favorite]              │
│                                                             │
│  ┌───────────────────────────────────────────────────────┐  │
│  │                                                       │  │
│  │              结果展示区域                              │  │
│  │              (表格或图形化展示)                        │  │
│  │                                                       │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3.5、常用 Docker 命令

# 查看运行中的容器
docker ps

# 查看所有容器(包括已停止的)
docker ps -a

# 启动已停止的容器
docker start neo4j

# 停止容器(数据不会丢失)
docker stop neo4j

# 重启容器
docker restart neo4j

# 删除容器(数据目录映射则数据不丢失)
docker rm neo4j

# 查看容器日志
docker logs -f neo4j  # -f 实时跟踪

# 进入容器内部(调试用)
docker exec -it neo4j bash

# 复制文件到容器
docker cp myfile.txt neo4j:/data/

# 从容器复制文件出来
docker cp neo4j:/logs/neo4j.log .

3.6、数据持久化详解

3.6.1、为什么要持久化
┌─────────────────────────────────────────────────────────┐
│  Docker 容器特性                                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  情况1:没有挂载数据目录                                  │
│  ┌─────────┐                                            │
│  │ 容器    │  ← docker rm neo4j 后数据全部丢失!         │
│  │ 数据    │                                            │
│  └─────────┘                                            │
│                                                         │
│  情况2:挂载了数据目录                                    │
│  ┌─────────┐      ┌─────────────────┐                   │
│  │ 容器    │ ←──→ │ 宿主机目录       │                  │
│  │ 数据    │      │ /data           │  ← 删除容器后数据  │
│  └─────────┘      └─────────────────┘    仍然保留        │
│                                                         │
└─────────────────────────────────────────────────────────┘
3.6.2、数据目录说明
目录说明重要程度
/data数据库数据,包含节点、关系、属性⭐⭐⭐ 核心
/logs日志文件⭐⭐ 重要
/conf配置文件⭐ 一般
/plugins插件目录⭐ 可选

3.7、清空数据 / 重置数据库

3.7.1、Cypher 清空(推荐)
-- 删除所有节点和关系,保留数据库配置
MATCH (n)
DETACH DELETE n

优点: 操作简单,不影响数据库配置

缺点: 节点 ID 可能不连续

3.7.2、清空数据目录
# 1. 停止容器
docker stop neo4j

# 2. 删除数据目录内容
rm -rf /Volumes/czx/dev_tool/docker/neo4j/data/*

# 3. 重新启动容器
docker start neo4j

注意: 这会完全重置数据库,相当于新安装


四、Spring Boot 整合

4.1、概念解释:什么是 Spring Data Neo4j

4.1.1、Spring Data 简介

Spring Data 是 Spring 生态的数据访问层框架,提供了统一的数据访问方式。

Spring Data 家族
├── Spring Data JPA      → 操作关系型数据库
├── Spring Data MongoDB  → 操作文档数据库
├── Spring Data Neo4j    → 操作图形数据库  ← 我们要学的
├── Spring Data Redis    → 操作键值数据库
└── ...
4.1.2、Spring Data Neo4j 能做什么
✅ Neo4j 能做的事,Spring Data Neo4j 都能做,而且更简单!

Spring Data Neo4j 提供:
├── 注解方式定义节点和关系
├── 自动生成 Cypher 查询
├── Repository 接口(类似 JPA)
├── 事务管理
└── 模板操作(Neo4jTemplate)
4.1.3、版本兼容性
Spring BootSpring Data Neo4jNeo4j
3.2.x7.x5.x
3.1.x7.x5.x
2.7.x6.x4.x/5.x

4.2、添加依赖

4.2.1、Maven 方式
<dependencies>
    <!-- Spring Boot 3.x 使用 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-neo4j</artifactId>
    </dependency>

    <!-- 如果需要使用 Cypher 模板 -->
    <dependency>
        <groupId>org.neo4j.driver</groupId>
        <artifactId>neo4j-java-driver</artifactId>
    </dependency>
</dependencies>
4.2.2、Gradle 方式
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-neo4j'
}

4.3、配置连接信息

4.3.1、application.yml 配置
spring:
  application:
    name: my-neo4j-app
  
  data:
    neo4j:
      # 数据库连接地址(bolt 是 Neo4j 的私有协议)
      uri: bolt://localhost:7687
      
      # 认证信息
      authentication:
        username: neo4j
        password: your_password_here
      
      # 数据库名称(可选,默认为 neo4j)
      # database: neo4j
      
      # 连接池配置(可选)
      # pool:
      #   max-connection-pool-size: 50
      #   connection-acquisition-timeout: 60s
4.3.2、application.properties 配置
# Neo4j 连接配置
spring.data.neo4j.uri=bolt://localhost:7687
spring.data.neo4j.authentication.username=neo4j
spring.data.neo4j.authentication.password=your_password_here
# spring.data.neo4j.database=neo4j

4.4、定义实体(重点)

4.4.1、基本节点实体
package com.example.neo4j.entity;

import org.springframework.data.neo4j.core.schema.GeneratedValue;
import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.data.neo4j.core.schema.Property;

/**
 * 人物节点
 */
@Node  // 标记为节点
public class Person {

    @Id  // 主键
    @GeneratedValue  // 自动生成
    private Long id;
    
    @Property  // 普通属性
    private String name;
    
    @Property
    private Integer age;
    
    @Property
    private String city;

    // 无参构造函数(必须)
    public Person() {}

    // 全参构造函数
    public Person(String name, Integer age, String city) {
        this.name = name;
        this.age = age;
        this.city = city;
    }

    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
    public String getCity() { return city; }
    public void setCity(String city) { this.city = city; }
}
4.4.2、公司节点实体
package com.example.neo4j.entity;

import org.springframework.data.neo4j.core.schema.GeneratedValue;
import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.data.neo4j.core.schema.Property;

@Node
public class Company {

    @Id
    @GeneratedValue
    private Long id;
    
    @Property
    private String name;
    
    @Property
    private String industry;

    public Company() {}

    public Company(String name, String industry) {
        this.name = name;
        this.industry = industry;
    }

    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getIndustry() { return industry; }
    public void setIndustry(String industry) { this.industry = industry; }
}
4.4.3、关系实体(有属性时使用)

当关系需要存储属性(如入职时间、职位)时,需要创建关系类:

package com.example.neo4j.entity;

import org.springframework.data.neo4j.core.schema.Property;
import org.springframework.data.neo4j.core.schema.RelationshipProperties;
import org.springframework.data.neo4j.core.schema.GeneratedValue;
import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.TargetNode;

@RelationshipProperties  // 标记为关系属性类
public class WorksFor {

    @Id
    @GeneratedValue
    private Long id;
    
    @Property
    private String role;  // 职位
    
    @Property
    private Double salary;  // 薪资
    
    @Property
    private String startDate;  // 入职日期
    
    @TargetNode  // 指向关系另一端的节点
    private Company company;

    public WorksFor() {}

    public WorksFor(String role, Double salary, String startDate) {
        this.role = role;
        this.salary = salary;
        this.startDate = startDate;
    }

    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getRole() { return role; }
    public void setRole(String role) { this.role = role; }
    public Double getSalary() { return salary; }
    public void setSalary(Double salary) { this.salary = salary; }
    public String getStartDate() { return startDate; }
    public void setStartDate(String startDate) { this.startDate = startDate; }
    public Company getCompany() { return company; }
    public void setCompany(Company company) { this.company = company; }
}
4.4.4、带关系的实体
package com.example.neo4j.entity;

import org.springframework.data.neo4j.core.schema.*;
import java.util.ArrayList;
import java.util.List;

@Node
public class PersonWithRelations {

    @Id
    @GeneratedValue
    private Long id;
    
    @Property
    private String name;
    
    @Property
    private Integer age;
    
    @Property
    private String city;

    // 认识的人(外向关系)
    @Relationship(type = "KNOWS", direction = Relationship.Direction.OUTGOING)
    private List<Person> knows = new ArrayList<>();

    // 在哪家公司工作(使用自定义关系类)
    @Relationship(type = "WORKS_FOR", direction = Relationship.Direction.OUTGOING)
    private List<WorksFor> worksFor = new ArrayList<>();

    public PersonWithRelations() {}

    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
    public String getCity() { return city; }
    public void setCity(String city) { this.city = city; }
    public List<Person> getKnows() { return knows; }
    public void setKnows(List<Person> knows) { this.knows = knows; }
    public List<WorksFor> getWorksFor() { return worksFor; }
    public void setWorksFor(List<WorksFor> worksFor) { this.worksFor = worksFor; }
}
4.4.5、注解说明
注解位置说明
@Node类上标记该类为 Neo4j 节点
@RelationshipProperties类上标记该类为关系属性类
@Id属性上标记主键
@GeneratedValue属性上主键自动生成
@Property属性上标记为属性
@Relationship属性上标记关系
@TargetNode属性上关系指向的目标节点
4.4.6、关系方向详解
// OUTGOING:从当前节点出发指向其他节点
// 例如:张三 --[KNOWS]--> 李四
@Relationship(type = "KNOWS", direction = Relationship.Direction.OUTGOING)
private List<Person> knows;

// INCOMING:从其他节点指向当前节点
// 例如:李四 --[KNOWS]--> 张三(对李四来说,这是 INCOMING)
@Relationship(type = "KNOWS", direction = Relationship.Direction.INCOMING)
private List<Person> knownBy;

// 默认是 OUTGOING,可以不写
@Relationship(type = "KNOWS")
private List<Person> friends;

4.5、创建 Repository

4.5.1、基本 Repository
package com.example.neo4j.repository;

import com.example.neo4j.entity.Person;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.neo4j.repository.query.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;

/**
 * Person 节点的数据访问层
 * 继承 Neo4jRepository,自动获得基本的 CRUD 方法
 */
public interface PersonRepository extends Neo4jRepository<Person, Long> {

    // ========== 方法名自动解析查询 ==========
    
    // 根据名字查找(自动生成:MATCH (p:Person {name: $name}) RETURN p)
    Optional<Person> findByName(String name);
    
    // 根据城市查找
    List<Person> findByCity(String city);
    
    // 多条件查询
    List<Person> findByCityAndAge(String city, Integer age);
    
    // 模糊查询(名字包含)
    List<Person> findByNameContaining(String keyword);
    
    // 年龄大于某值
    List<Person> findByAgeGreaterThan(Integer age);
    
    // ========== 自定义 Cypher 查询 ==========
    
    // 查询所有
    @Query("MATCH (p:Person) RETURN p")
    List<Person> findAllPersons();
    
    // 自定义查询带参数
    @Query("MATCH (p:Person {city: $city}) RETURN p")
    List<Person> findByCityCustom(@Param("city") String city);
    
    // 查询关系
    @Query("MATCH (p:Person {name: $name})-[r:KNOWS]->(friend:Person) RETURN friend")
    List<Person> findFriends(@Param("name") String name);
    
    // 查询朋友的朋友
    @Query("MATCH (p:Person {name: $name})-[:KNOWS*2]->(fof:Person) RETURN fof")
    List<Person> findFriendsOfFriends(@Param("name") String name);
}
4.5.2、支持的方法名查询关键字
关键字示例生成的 Cypher
findByfindByNameMATCH (n {name: $name})
findAllByfindAllByCityMATCH (n {city: $city}) RETURN n
AndfindByNameAndAgeWHERE name = $name AND age = $age
OrfindByNameOrAgeWHERE name = $name OR age = $age
GreaterThanfindByAgeGreaterThanWHERE age > $age
LessThanfindByAgeLessThanWHERE age < $age
BetweenfindByAgeBetweenWHERE age >= $min AND age <= $max
ContainingfindByNameContainingWHERE name CONTAINS $keyword
StartingWithfindByNameStartingWithWHERE name STARTS WITH $prefix
EndingWithfindByNameEndingWithWHERE name ENDS WITH $suffix
InfindByCityInWHERE city IN $cities
ExistsexistsByNameMATCH (n {name: $name}) RETURN count(n) > 0

4.6、CRUD 操作

4.6.1、Service 层
package com.example.neo4j.service;

import com.example.neo4j.entity.Person;
import com.example.neo4j.repository.PersonRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

@Service
public class PersonService {

    @Autowired
    private PersonRepository personRepository;

    /**
     * 创建节点
     */
    @Transactional
    public Person createPerson(String name, Integer age, String city) {
        Person person = new Person(name, age, city);
        return personRepository.save(person);
    }

    /**
     * 根据 ID 查询
     */
    public Optional<Person> findById(Long id) {
        return personRepository.findById(id);
    }

    /**
     * 根据名字查询
     */
    public Optional<Person> findByName(String name) {
        return personRepository.findByName(name);
    }

    /**
     * 查询所有
     */
    public List<Person> findAll() {
        return personRepository.findAll();
    }

    /**
     * 根据城市查询
     */
    public List<Person> findByCity(String city) {
        return personRepository.findByCity(city);
    }

    /**
     * 更新节点
     */
    @Transactional
    public Person updatePerson(Long id, String name, Integer age) {
        Optional<Person> optional = personRepository.findById(id);
        if (optional.isPresent()) {
            Person person = optional.get();
            if (name != null) person.setName(name);
            if (age != null) person.setAge(age);
            return personRepository.save(person);
        }
        throw new RuntimeException("Person not found with id: " + id);
    }

    /**
     * 删除节点
     */
    @Transactional
    public void deletePerson(Long id) {
        personRepository.deleteById(id);
    }

    /**
     * 删除所有
     */
    @Transactional
    public void deleteAll() {
        personRepository.deleteAll();
    }

    /**
     * 批量创建
     */
    @Transactional
    public List<Person> batchCreate(List<Person> persons) {
        return personRepository.saveAll(persons);
    }
}
4.6.2、Controller 层
package com.example.neo4j.controller;

import com.example.neo4j.entity.Person;
import com.example.neo4j.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/api/persons")
public class PersonController {

    @Autowired
    private PersonService personService;

    @PostMapping
    public Person create(@RequestBody Person person) {
        return personService.createPerson(
            person.getName(),
            person.getAge(),
            person.getCity()
        );
    }

    @GetMapping("/{id}")
    public Optional<Person> getById(@PathVariable Long id) {
        return personService.findById(id);
    }

    @GetMapping
    public List<Person> getAll() {
        return personService.findAll();
    }

    @GetMapping("/city/{city}")
    public List<Person> getByCity(@PathVariable String city) {
        return personService.findByCity(city);
    }

    @PutMapping("/{id}")
    public Person update(@PathVariable Long id, @RequestBody Person person) {
        return personService.updatePerson(id, person.getName(), person.getAge());
    }

    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id) {
        personService.deletePerson(id);
    }
}

4.7、自定义 Cypher 查询

当自动生成的方法不满足需求时,可以使用 @Query 注解编写自定义 Cypher:

4.7.1、基本用法
public interface PersonRepository extends Neo4jRepository<Person, Long> {

    // 查询某个人的所有朋友
    @Query("MATCH (p:Person {name: $name})-[r:KNOWS]->(friend:Person) RETURN friend")
    List<Person> findFriendsByName(@Param("name") String name);

    // 查询在某公司工作的所有人
    @Query("MATCH (p:Person)-[:WORKS_FOR]->(c:Company {name: $companyName}) RETURN p")
    List<Person> findByCompany(@Param("companyName") String companyName);

    // 查询朋友数量
    @Query("MATCH (p:Person {name: $name})-[r:KNOWS]->(friend:Person) RETURN count(friend)")
    Long countFriends(@Param("name") String name);

    // 模糊搜索
    @Query("MATCH (p:Person) WHERE p.name CONTAINS $keyword RETURN p")
    List<Person> searchByName(@Param("keyword") String keyword);
}
4.7.2、返回单个值
public interface PersonRepository extends Neo4jRepository<Person, Long> {

    // 返回姓名列表
    @Query("MATCH (p:Person) RETURN p.name")
    List<String> findAllNames();

    // 返回聚合结果
    @Query("MATCH (p:Person) RETURN count(p)")
    Long countAll();
}
4.7.3、复杂查询示例
public interface PersonRepository extends Neo4jRepository<Person, Long> {

    // 查询朋友的朋友(2跳)
    @Query("MATCH (p:Person {name: $name})-[:KNOWS*2]->(fof:Person) RETURN DISTINCT fof")
    List<Person> findFriendsOfFriends(@Param("name") String name);

    // 查询在某城市工作的所有人及其公司
    @Query("""
        MATCH (p:Person)-[r:WORKS_FOR]->(c:Company)
        WHERE p.city = $city
        RETURN p.name AS name, c.name AS company, r.role AS role
        ORDER BY c.name
        """)
    List<Map<String, Object>> findWorkersInCity(@Param("city") String city);

    // 使用 WITH 子句的复杂查询
    @Query("""
        MATCH (p:Person)-[r:WORKS_FOR]->(c:Company)
        WITH avg(r.salary) AS avgSalary, c
        MATCH (p2:Person)-[r2:WORKS_FOR]->(c)
        WHERE r2.salary > avgSalary
        RETURN p2.name AS name, r2.salary AS salary, c.name AS company
        """)
    List<Map<String, Object>> findAboveAverageSalary();
}

4.8、事务管理

Neo4j 操作默认在事务中执行,但显式声明事务可以更好地控制:

@Service
public class PersonService {

    @Autowired
    private PersonRepository personRepository;

    /**
     * 单操作不需要显式事务
     */
    public Person save(Person person) {
        return personRepository.save(person);
    }

    /**
     * 多操作建议添加事务
     */
    @Transactional
    public void createFriendship(String name1, String name2) {
        // 操作1:创建关系
        Optional<Person> p1 = personRepository.findByName(name1);
        Optional<Person> p2 = personRepository.findByName(name2);
        
        if (p1.isPresent() && p2.isPresent()) {
            // 关系创建逻辑
            // 如果失败,整个事务回滚
        }
    }

    /**
     * 异常时自动回滚
     */
    @Transactional
    public void riskyOperation() {
        Person p = new Person("测试", 20, "测试城市");
        personRepository.save(p);
        
        // 抛出异常,p 的创建会被回滚
        throw new RuntimeException("模拟错误");
    }
}

五、性能优化

5.1、索引与约束

5.1.1、创建索引
-- 为经常查询的属性创建索引

-- 单属性索引
CREATE INDEX person_name FOR (p:Person) ON (p.name)

-- 多属性索引
CREATE INDEX person_name_age FOR (p:Person) ON (p.name, p.age)

-- 查看所有索引
SHOW INDEXES

-- 删除索引
DROP INDEX person_name
5.1.2、创建约束
-- 唯一约束(同时创建索引)
CREATE CONSTRAINT person_name_unique FOR (p:Person) REQUIRE p.name IS UNIQUE

-- 存在约束(确保属性存在)
CREATE CONSTRAINT person_age_exists FOR (p:Person) REQUIRE p.age IS NOT NULL

-- 查看所有约束
SHOW CONSTRAINTS

-- 删除约束
DROP CONSTRAINT person_name_unique
5.1.3、什么时候该创建索引
建议创建索引的场景:
✅ WHERE 子句中经常使用的属性
✅ MATCH 子句中经常匹配的标签
✅ ORDER BY 排序的属性
✅ JOIN/关系查询中使用的属性

不建议创建索引的场景:
❌ 唯一性很高的属性(如 UUID)
❌ 查询结果集很小的属性
❌ 很少用于查询的属性

5.2、查询优化技巧

5.2.1、避免全表扫描
-- ❌ 慢:匹配所有节点
MATCH (n) WHERE n.name = '张三' RETURN n

-- ✅ 快:先匹配标签,再过滤
MATCH (p:Person) WHERE p.name = '张三' RETURN p

-- ✅ 更快:使用索引(如果有)
MATCH (p:Person {name: '张三'}) RETURN p
5.2.2、限制返回数量
-- 返回前10条
MATCH (p:Person) RETURN p LIMIT 10

-- 按年龄排序后返回前5
MATCH (p:Person)
RETURN p
ORDER BY p.age DESC
LIMIT 5
5.2.3、使用合适的路径长度
-- ❌ 可能很慢:无限跳数
MATCH (a)-[*]->(b) RETURN a, b

-- ✅ 限制跳数
MATCH (a)-[*1..3]->(b) RETURN a, b

-- ✅ 使用最短路径
MATCH path = shortestPath((a)-[*]-(b)) RETURN path
5.2.4、分解复杂查询
-- ❌ 一个大查询
MATCH (a)-[:KNOWS]->(b)-[:KNOWS]->(c)-[:KNOWS]->(d)
WHERE a.city = '北京' AND d.city = '深圳'
RETURN a, b, c, d

-- ✅ 分解为多个简单查询
MATCH (a:Person {city: '北京'})-[:KNOWS*1..3]->(d:Person {city: '深圳'})
RETURN a, d

5.3、批量操作

5.3.1、LOAD CSV 批量导入
-- 假设有一个 CSV 文件:/path/to/persons.csv
-- 内容:name,age,city
--       张三,25,北京
--       李四,30,上海

LOAD CSV FROM 'file:///path/to/persons.csv' AS row
CREATE (p:Person {
    name: row[0],
    age: toInteger(row[1]),
    city: row[2]
})
5.3.2、使用 UNWIND 批量插入
@Service
public class PersonService {

    @Autowired
    private Neo4jTemplate neo4jTemplate;

    @Transactional
    public void batchImport(List<Person> persons) {
        // 分割成小批次,避免内存溢出
        int batchSize = 1000;
        for (int i = 0; i < persons.size(); i += batchSize) {
            List<Person> batch = persons.subList(i, Math.min(i + batchSize, persons.size()));
            neo4jTemplate.save(batch);
        }
    }
}

六、图数据科学(GDS)

6.1、什么是 GDS

GDS(Graph Data Science)是 Neo4j 提供的图算法库,用于在图形数据上执行高级分析。

GDS 能做什么?
├── 找出最重要的节点(PageRank)
├── 发现社群和群组(Community Detection)
├── 找到最短路径(Path Finding)
├── 计算相似度(Similarity)
└── 预测链接(Link Prediction)

6.2、常用算法详解

6.2.1、PageRank - 页面排名算法

用途: 衡量节点的重要性,常用于网页排名、影响力分析。

-- 使用示例:找出最具影响力的用户
CALL gds.pageRank.write('socialGraph', {
    writeProperty: 'pageRankScore'
})
YIELD nodePropertiesWritten
6.2.2、Community Detection - 社群发现

用途: 发现网络中紧密连接的节点群组。

-- Louvain 算法示例
CALL gds.louvain.write('socialGraph', {
    writeProperty: 'communityId'
})
YIELD communityCount, modularity
6.2.3、Shortest Path - 最短路径

用途: 找到两点之间的最短路径。

-- Dijkstra 算法
CALL gds.shortestPath.dijkstra.write('myGraph', {
    sourceNode: $sourceId,
    targetNode: $targetId,
    writeRelationshipType: 'PATH'
})
YIELD distance, nodeIds, path

6.3、使用流程

6.3.1、创建图投影
-- 将数据库中的数据加载到内存中形成图投影
CALL gds.graph.project(
    'myGraph',           -- 图投影名称
    'Person',            -- 节点标签
    {
        KNOWS: {         -- 关系类型
            type: 'KNOWS',
            orientation: 'UNDIRECTED'  -- 无向关系
        }
    }
)
6.3.2、运行算法
-- 运行 PageRank
CALL gds.pageRank.stream('myGraph')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC
LIMIT 10
6.3.3、清理资源
-- 删除不再需要的图投影,释放内存
CALL gds.graph.drop('myGraph')

七、应用场景详解

7.1、社交网络

7.1.1、数据模型
节点:
- Person(用户):name, avatar, bio, createdAt

关系:
- KNOWS:朋友关系
- FOLLOWS:关注关系
- LIKES:点赞关系
7.1.2、常见查询
-- 找出用户的所有好友
MATCH (me:Person {name: "张三"})-[:KNOWS]->(friend:Person)
RETURN friend

-- 找出共同好友
MATCH (me:Person {name: "张三"})-[:KNOWS]->(friend:Person)<-[:KNOWS]-(other:Person {name: "李四"})
RETURN friend

-- 好友推荐(朋友的朋友)
MATCH (me:Person {name: "张三"})-[:KNOWS]->(friend)-[:KNOWS]->(fof:Person)
WHERE NOT (me)-[:KNOWS]->(fof) AND fof <> me
RETURN DISTINCT fof

7.2、知识图谱

7.2.1、数据模型
节点:
- Entity(实体):name, type, description
- Concept(概念):name

关系:
- IS_A:是什么(如"猫 IS_A 动物")
- HAS_A:有什么
- RELATED_TO:关联关系
7.2.2、常见查询
-- 查询实体所有关系
MATCH (e:Entity {name: "北京"})-[r]-(other)
RETURN type(r) AS relation, other.name AS relatedTo

-- 推理查询
MATCH (cat:Entity {name: "猫"})-[:IS_A*]->(animal:Entity)
RETURN animal.name AS category

7.3、推荐系统

-- 基于好友推荐商品
MATCH (user:Person {name: "张三"})-[:KNOWS]->(friend)-[:BOUGHT]->(product:Product)
WHERE NOT (user)-[:BOUGHT]->(product)
RETURN product.name AS recommended, count(*) AS score
ORDER BY score DESC
LIMIT 5

八、总结

核心要点回顾

┌─────────────────────────────────────────────────────────────┐
│                      Neo4j 核心要点                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 核心概念:节点、关系、属性、标签                          │
│                                                             │
│  2. Cypher 基本语句:                                       │
│     - MATCH:查询                                           │
│     - CREATE:创建                                          │
│     - MERGE:创建或更新                                     │
│     - SET:更新属性                                         │
│     - DELETE:删除                                          │
│                                                             │
│  3. Spring Boot 整合:                                      │
│     - @Node 定义节点                                        │
│     - @Relationship 定义关系                                │
│     - Neo4jRepository 数据访问                              │
│                                                             │
│  4. 性能优化:                                              │
│     - 创建索引和约束                                        │
│     - 避免全表扫描                                          │
│     - 限制返回数量                                          │
│                                                             │
│  5. GDS 图算法:                                           │
│     - PageRank:重要性分析                                  │
│     - Community Detection:社群发现                         │
│     - Shortest Path:最短路径                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘