首页>软件资讯>常见问题

常见问题

Neo4j 图数据库 CRUD 完全指南

发布时间:2026-03-26 08:23:33人气:49

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构建知识图谱