图数据库 Neo4j 的基本概念和查询
认识图数据库 Neo4j
当你处理的数据之间充满复杂的关联——比如社交网络中的好友关系、电商中的商品推荐路径、金融领域的欺诈交易链条——传统的关系型数据库在处理多对多、多跳查询时会暴露出性能低下、建模僵化的问题。图数据库正是为这种高度连接的数据而生。Neo4j 是目前最流行的原生图数据库,它将数据存储为节点和关系,并提供强大的查询语言 Cypher,让你能直观地表达图遍历和模式匹配。
为什么选择图数据库?
- 直观建模:数据模型与现实世界的实体和关系一一对应,无需复杂的 Join 表。
- 高性能关联查询:在深度超过 2 跳的关系查询中,图数据库通过无索引邻接技术保持毫秒级响应,不会像关系型数据库那样随着数据量和跳数增长而指数级下降。
- 灵活扩展:节点和关系可以随时增加属性,不需要预先定义所有字段。
- 内置图算法:Neo4j 提供路径查找、中心性分析、社区发现等算法,助力高级分析。
Neo4j 的核心概念
掌握四个基本要素,你就理解了 Neo4j 的顶层设计。
节点(Node)
节点代表一个实体,例如“用户”、“电影”或“城市”。在图中通常用圆圈表示。
- 每个节点可以有一个或多个标签(Label),用来分组。例如
:Person、:Movie。 - 节点可以携带属性(Property),以键值对的形式存储数据,如
{name: "Alice", age: 30}。
关系(Relationship)
关系连接两个节点,表示它们之间的行为或联系。关系是有方向性的,但也可以在查询时忽略方向。
- 关系必须有一个类型(Type),如
:ACTED_IN、:FOLLOWS。 - 和节点一样,关系也可以包含属性,例如关系上的
{since: 2020}。 - 关系始终有一个起始节点和一个结束节点。
属性(Property)
属性是键值对,节点和关系都可以拥有。值类型支持数字、字符串、布尔、列表等。属性是 Neo4j 中实际存储的数据单元。
标签(Label)
标签用于对节点进行分类、建立索引和定义约束。一个节点可以有多个标签,例如 :Person:Employee,这使得节点可以参与多种角色。
路径(Path)
路径由一系列用关系连接起来的节点组成,可以是一跳(直接相连),也可以是多跳。Cypher 查询的本质就是寻找匹配特定模式的路径。
安装与启动 Neo4j
推荐使用 Neo4j Desktop 或 AuraDB(免费云实例)快速上手。
- 下载 Neo4j Desktop 并安装。
- 创建本地数据库,设置密码,然后点击“Start”启动。
- 打开 Neo4j Browser,这是一个内置的 Web 查询界面,输入
:play start可获取指引。
你也可以通过 Docker 启动:
docker run -d --name neo4j \
-p 7474:7474 -p 7687:7687 \
-e NEO4J_AUTH=neo4j/test1234 \
neo4j:latest
浏览器访问 http://localhost:7474 登录即可。
Cypher 查询语言入门
Cypher 是一种声明式、模式匹配型的图查询语言,其设计灵感来源于 ASCII 艺术。你可以在画布视觉化地表达你想寻找的图模式。
基本语法元素
| 元素 | 写法示例 | 含义 |
|---|---|---|
| 节点 | (n) |
无标签匿名节点 |
| 带标签的节点 | (p:Person) |
标签为 Person 的节点,赋给变量 p |
| 带属性的节点 | (p:Person {name: "Tom"}) |
属性 name 为 "Tom" 的 Person 节点 |
| 有向关系 | (p)-[:KNOWS]->(f) |
从 p 到 f 的 KNOWS 关系 |
| 无向关系 | (p)-[:KNOWS]-(f) |
任意方向的 KNOWS 关系 |
| 可变跳数关系 | (p)-[:KNOWS*1..3]-(f) |
1 到 3 跳的 KNOWS 路径 |
创建数据
我们先创建一个简单的社交网络:两个人互相关注。
// 创建节点
CREATE (alice:Person {name: "Alice", age: 30})
CREATE (bob:Person {name: "Bob", age: 25})
// 创建关系
MATCH (a:Person {name: "Alice"}), (b:Person {name: "Bob"})
CREATE (a)-[:FOLLOWS {since: 2021}]->(b)
CREATE (b)-[:FOLLOWS {since: 2022}]->(a)
也可以合并创建:
CREATE (alice:Person {name: "Alice"})-[:FOLLOWS {since: 2021}]->(bob:Person {name: "Bob"})
使用 MERGE 避免重复
MERGE 相当于“匹配或创建”,它检查模式是否存在,不存在则创建。适合确保实体唯一。
MERGE (city:City {name: "London"})
ON CREATE SET city.country = "UK"
基础查询
匹配节点并返回
// 查找所有 Person 节点
MATCH (p:Person)
RETURN p.name, p.age
带关系的查询
// 谁关注了 Alice?
MATCH (follower:Person)-[:FOLLOWS]->(alice:Person {name: "Alice"})
RETURN follower.name
使用 WHERE 过滤
// 查找年龄大于 28 的人
MATCH (p:Person)
WHERE p.age > 28
RETURN p.name
多跳查询(朋友的朋友)
// 找到 Alice 关注的人所关注的人,但不包含 Alice 自己以及她已直接关注的人
MATCH (alice:Person {name: "Alice"})-[:FOLLOWS]->(fof)-[:FOLLOWS]->(fof_follow)
WHERE NOT (alice)-[:FOLLOWS]->(fof_follow) AND fof_follow <> alice
RETURN DISTINCT fof_follow.name
聚合与排序
支持常用的聚合函数 COUNT, SUM, AVG, MIN, MAX。
// 每人被多少人关注
MATCH (p:Person)<-[:FOLLOWS]-(f)
RETURN p.name, COUNT(f) AS followerCount
ORDER BY followerCount DESC
最短路径查询
Neo4j 内置了最短路径算法函数,用于快速找到两个节点之间的最短路径。
// 找到 Alice 与某个用户之间的最短关系路径
MATCH (a:Person {name: "Alice"}), (b:Person {name: "某人名字"}),
path = shortestPath((a)-[:FOLLOWS*]-(b))
RETURN path
删除数据
// 删除关系
MATCH (a:Person {name: "Alice"})-[r:FOLLOWS]->(b:Person {name: "Bob"})
DELETE r
// 删除节点(需先删除所有关联关系,或用 DETACH DELETE 级联删除关系)
MATCH (obsolete:Person)
WHERE obsolete.name = "Bob"
DETACH DELETE obsolete
索引和约束
为了提高查询性能并确保数据完整性,可以为标签的属性建立索引和唯一性约束。
// 创建唯一性约束(自动建立索引)
CREATE CONSTRAINT FOR (p:Person) REQUIRE p.name IS UNIQUE
// 创建索引(非唯一)
CREATE INDEX FOR (p:Person) ON (p.age)
约束会阻止插入重复的 name 值,索引会加速基于 age 的范围查询。
实战案例:电影推荐图
使用 Neo4j 内置的示例数据集 :play movie-graph 可以快速体验。其模型包含 :Person, :Movie 节点和 :ACTED_IN, :DIRECTED, :REVIEWED 等关系。
- 查找 Tom Hanks 出演的电影:
MATCH (tom:Person {name: "Tom Hanks"})-[:ACTED_IN]->(movie) RETURN movie.title - 推荐“共演关系”——与 Tom Hanks 合作过的演员还演过哪些电影:
MATCH (tom:Person {name: "Tom Hanks"})-[:ACTED_IN]->(:Movie)<-[:ACTED_IN]-(coActor), (coActor)-[:ACTED_IN]->(otherMovie) RETURN DISTINCT otherMovie.title LIMIT 10
常见问题与最佳实践
- 关系方向:创建时方向很重要,但查询时可以通过
()--()忽略方向,()-[]->()指定方向。 - 属性类型:选择合适的类型,不要将需要范围查询的数值存为字符串。
- 大事务批处理:大量数据导入时使用
USING PERIODIC COMMIT或neo4j-admin import工具。 - 避免笛卡尔积:在
MATCH左侧开始用具有较小基数的节点,并提供标签和索引字段。 - 使用参数化查询:在应用程序中不要拼接字符串,使用
$param传递变量,既安全又可缓存执行计划。
总结
Neo4j 将高度关联的数据转化为直观的图模型,配合 Cypher 语言使复杂的关系查询变得简洁而高效。掌握了节点、关系、属性、标签这四个基石,以及 CREATE, MATCH, RETURN, WHERE 等核心查询语句,你就已经能解决大部分图分析场景。接下来可以深入探索图数据建模、性能调优和内置图算法库 GDS,让图数据库的价值最大化。