Neo4j 图数据库 CRUD 完全指南
Neo4j 是目前最主流的图数据库,用 Cypher 查询语言操作节点和关系。这篇文章覆盖 CRUD 全部操作 - 创建、查询、更新、删除,外加索引约束、实用函数,以及 Rust neo4rs 驱动的接入方式。适用 Neo4j 5.x 版本。
01 核心概念:节点、关系与属性Neo4j 的数据模型只有三样东西:节点(Node)、关系(Relationship)和属性(Property)。节点用圆括号表示,关系用方括号加箭头表示,属性是键值对。整个数据结构可以一行 Cypher 写清楚:(nodeA:Label)-[rel:TYPE {key: "value"}]->(nodeB:Label)Cypher 的语法符号不多,记住几个就够了:符号含义示例()节点(p:Person)[]关系[r:KNOWS]-->有方向的关系(a)-->(b):Label节点标签(p:Person){k: v}属性{name: "Neo"}$param参数化变量$name02 CREATE -- 创建节点与关系2.1 创建节点创建节点的语法很直接:CREATE后面跟一个圆括号包裹的节点定义。变量名是可选的,只有后续需要引用这个节点时才写。// cypher
-- 创建一个 Person 节点
CREATE (p:Person {name: "张三", age: 28, city: "北京"})
-- 多个标签
CREATE (p:Person:Employee {name: "李四", department: "技术部"})
-- 不绑定变量(不需要后续引用时)
CREATE (:Person {name: "王五"})一个节点可以挂多个标签,用:Label1:Label2连写。属性值支持字符串、整数、浮点数、布尔值和列表。2.2 创建关系关系有两个硬性要求:必须有方向,必须有类型。命名惯例是大写字母加下划线,比如BELONGS_TO、WORKS_AT。-- 在已有节点之间创建关系
MATCH (a:Person {name: "张三"}), (b:Person {name: "李四"})
CREATE (a)-[:KNOWS {since: 2020}]->(b)
-- 同时创建节点和关系
CREATE (a:Person {name: "赵六"})-[:WORKS_AT {role: "工程师"}]->(c:Company {name: "科大讯飞"})2.3 MERGE -- 创建时去重MERGE是CREATE的安全版本:先查有没有,没有才建。配合ON CREATE SET和ON MATCH SET可以分别处理"新建"和"已存在"两种情况。MERGE (p:Person {name: "张三"})
ON CREATE SET p.age = 28, p.created_at = datetime()
ON MATCH SET p.last_seen = datetime()重要提醒:MERGE 中的属性是匹配条件,不是全量属性。放太多属性进去,只要有一个值不同就会创建新节点,容易造成重复数据。03 READ -- 查询的各种姿势3.1 MATCH 基本查询所有查询都从MATCH开始,配合WHERE做过滤,RETURN指定返回内容。-- 查询所有 Person
MATCH (p:Person)
RETURN p
-- 带条件查询
MATCH (p:Person)
WHERE p.age > 25 AND p.city = "北京"
RETURN p.name, p.age3.2 WHERE 条件大全WHERE 子句支持的操作比较丰富,比较、逻辑、字符串匹配、正则、列表查询都有:-- 比较与逻辑
WHERE p.age > 25
WHERE p.age <> 30 -- 不等于
WHERE p.age < 20 OR p.age > 60
WHERE NOT p.name = "张三"
-- 字符串匹配
WHERE p.name STARTS WITH "张"
WHERE p.name CONTAINS "小"
WHERE p.name =~ '张.*' -- 正则
-- 列表与空值
WHERE p.age IN [25, 28, 30]
WHERE p.age IS NOT NULL
WHERE p.email IS NULL3.3 查询关系与多跳路径图数据库最强的地方就是关系遍历。Cypher 用*N表示精确 N 跳,*1..3表示 1 到 3 跳的可变长度路径。-- 直接朋友
MATCH (a:Person)-[:KNOWS]->(b:Person)
RETURN a.name, b.name
-- 朋友的朋友(精确 2 跳)
MATCH (a:Person {name: "张三"})-[:KNOWS*2]->(c:Person)
RETURN c.name
-- 1 到 3 跳范围
MATCH (a:Person {name: "张三"})-[:KNOWS*1..3]->(c:Person)
RETURN c.name注意:裸写*表示任意跳数,在大图上会跑死查询。生产环境一定要限定上界。3.4 排序、分页与聚合-- 排序 + 分页
MATCH (p:Person)
RETURN p.name, p.age
ORDER BY p.age DESC
SKIP 10 LIMIT 5
-- 聚合函数
MATCH (p:Person)
RETURN
count(p) AS total,
avg(p.age) AS avg_age,
collect(p.name) AS names
-- 分组聚合
MATCH (p:Person)
RETURN p.city, count(p) AS cnt
ORDER BY cnt DESC3.5 OPTIONAL MATCH 与 WITHOPTIONAL MATCH相当于 SQL 的 LEFT JOIN -- 即使没匹配到也返回 null。WITH则是管道操作符,把前一段的结果传给下一段。-- OPTIONAL MATCH:没朋友的人也会返回
MATCH (p:Person)
OPTIONAL MATCH (p)-[:KNOWS]->(friend:Person)
RETURN p.name, friend.name
-- WITH:筛选朋友数大于 3 的人
MATCH (p:Person)-[:KNOWS]->(friend)
WITH p, count(friend) AS fc
WHERE fc > 3
RETURN p.name, fc04 UPDATE -- 更新属性与标签4.1 SET 更新属性先 MATCH 找到节点,再用 SET 修改属性。这里有个关键区别:+=是合并(保留原有属性),=是替换(清除原有属性再写入)。生产环境推荐始终用+=。推荐:+= 合并属性MATCH (p:Person {name: "张三"})
SET p += {age: 30, city: "深圳", email: "zs@example.com"}不推荐:= 替换全部属性MATCH (p:Person {name: "张三"})
SET p = {name: "张三", age: 30} -- 原有的 city, email 全没了4.2 标签与属性的增删-- 添加标签
MATCH (p:Person {name: "张三"})
SET p:Employee:Senior
-- 移除标签
MATCH (p:Person {name: "张三"})
REMOVE p:Senior
-- 删除属性(两种写法等效)
MATCH (p:Person {name: "张三"})
REMOVE p.email
MATCH (p:Person {name: "张三"})
SET p.email = null05 DELETE -- 删除操作删除节点时有个坑:如果节点还有关系,直接DELETE会报错。用DETACH DELETE可以自动先删关系再删节点,生产环境建议始终用它。-- 删除节点(必须无关系)
MATCH (p:Person {name: "张三"})
DELETE p
-- 删除节点及其所有关系(推荐)
MATCH (p:Person {name: "张三"})
DETACH DELETE p
-- 只删关系,保留节点
MATCH (a:Person {name: "张三"})-[r:KNOWS]->(b:Person {name: "李四"})
DELETE r
-- 清空数据库(小库适用)
MATCH (n) DETACH DELETE n数据量大的话,一次性删几百万节点会撑爆内存。分批删:CALL {
MATCH (n)
WITH n LIMIT 10000
DETACH DELETE n
} IN TRANSACTIONS OF 10000 ROWS06 索引与约束索引决定查询速度,约束保证数据完整性。MATCH 里 WHERE 常命中的属性,一定要建索引。-- 单属性索引
CREATE INDEX person_name_idx FOR (p:Person) ON (p.name)
-- 复合索引
CREATE INDEX person_name_age_idx FOR (p:Person) ON (p.name, p.age)
-- 全文索引
CREATE FULLTEXT INDEX person_ft FOR (p:Person) ON EACH [p.name, p.bio]
-- 唯一约束(自动创建索引)
CREATE CONSTRAINT person_name_unique
FOR (p:Person) REQUIRE p.name IS UNIQUE
-- 查看 / 删除
SHOW INDEXES
SHOW CONSTRAINTS
DROP INDEX person_name_idx07 实用函数与表达式7.1 字符串、数学与列表函数-- 字符串
RETURN toUpper("hello"),
trim(" hello "),
substring("hello", 0, 3),
replace("hello", "l", "L")
-- 数学
RETURN abs(-5), ceil(4.2), round(4.567, 2), rand()
-- 列表推导
RETURN [x IN range(1,5) WHERE x > 3] -- [4, 5]
-- 类型转换
RETURN toInteger("42"), toFloat("3.14"), toString(42)7.2 日期时间与 CASE 表达式-- 时间戳记录
MATCH (p:Person {name: "张三"})
SET p.updated_at = datetime()
-- 日期过滤
MATCH (p:Person)
WHERE p.created_at > datetime("2024-01-01T00:00:00Z")
RETURN p.name
-- CASE 表达式
MATCH (p:Person)
RETURN p.name,
CASE
WHEN p.age < 18 THEN "minor"
WHEN p.age < 60 THEN "adult"
ELSE "senior"
END AS age_group08 Rust neo4rs 驱动接入Rust 生态里连 Neo4j 用neo4rs这个 crate,走 Bolt 协议。写操作用graph.run(),读操作用graph.execute(),参数绑定用.param()链式调用。8.1 依赖与连接// Cargo.toml
[dependencies]
neo4rs = "0.7"
tokio = { version = "1", features = ["full"] }8.2 读写与事务// rust
use neo4rs::*;
use std::sync::Arc;
// 建立连接
let graph = Arc::new(
Graph::new("bolt://localhost:7687", "neo4j", "password").await?
);
// 写操作:graph.run()
graph.run(
query("CREATE (p:Person {name: $name, age: $age})")
.param("name", "张三")
.param("age", 28)
).await?;
// 读操作:graph.execute()
let mut result = graph.execute(
query("MATCH (p:Person) WHERE p.age > $age RETURN p.name AS name")
.param("age", 20)
).await?;
while let Ok(Some(row)) = result.next().await {
let name: String = row.get("name").unwrap();
println!("{}", name);
}// rust - 事务
let txn = graph.start_txn().await?;
txn.run(
query("CREATE (p:Person {name: $name})").param("name", "UserA")
).await?;
txn.run(
query("CREATE (p:Person {name: $name})").param("name", "UserB")
).await?;
txn.commit().await?; -- 或 txn.rollback().await?API 速查表:场景方法说明写操作graph.run(query)CREATE / SET / DELETE读操作graph.execute(query)MATCH ... RETURN参数绑定.param("k", v)防注入,链式调用事务graph.start_txn()commit() / rollback()提取 Stringrow.get::<String>("col")类型需匹配提取 i64row.get::<i64>("col")Neo4j Integer09 最佳实践与 Docker 部署9.1 八条实战建议参数化查询:用$param代替字符串拼接,防 Cypher 注入高频字段建索引:WHERE 里频繁出现的属性必须有索引用 MERGE 替代 CREATE:有可能重复的场景下避免数据冗余删除用 DETACH DELETE:避免残留关系导致删除失败大批量操作分批处理:别一次性操作百万节点,内存会炸标签按业务实体划分:标签是最重要的索引入口关系类型命名规范:大写加下划线,如 BELONGS_TO属性更新用 +=:避免 = 意外清空全部属性9.2 Docker 一键部署// bash
docker run -d
--name neo4j
-p 7474:7474
-p 7687:7687
-e NEO4J_AUTH=neo4j/your_password
-v neo4j_data:/data
neo4j:latest跑起来之后,浏览器访问http://localhost:7474就是 Neo4j Browser,可以直接在里面写 Cypher。Bolt 连接地址是bolt://localhost:7687。
上一条:gurobi技巧
下一条:neo4j构建知识图谱
品质保证
多年的生产力软件专家
专业实力
资深技术支持项目实施团队
安全无忧
多位认证安全工程师
多元服务
软件提供方案整合,项目咨询实施
购软平台-找企业级软件,上购软平台。平台提供更齐全的软件产品、更专业的技术服务,同时提供行业资讯、软件使用教程和技巧。购软平台打造企业级数字产品综合应用服务平台。用户体验和数字类产品的专业化服务是我们不断追求的目标。购软平台您身边的企业级数字产品优秀服务商。
沪公网安备31011302006932号