Neo4j与Cypher入门:从图思维到多跳查询实战

作者:桃酥不是桃日期:2026/8/22

当写《RAG 没有死》那篇、写到 GraphRAG 那一节的时候,我心里是有点发虚的。多跳推理、知识图谱、社区发现,这些词都是从论文和官方文档里搬来的,我自己并没有真的建过一张图、跑过一条查询。引用式写作的账迟早要还,所以最近两个周末我把 Neo4j 和 Cypher 认真补了一遍,顺手把 Agent 场景下图的选型边界也想清楚了。

这篇是补课笔记的上篇,聊三件事,图数据库到底解决什么问题,Cypher 怎么写,什么时候值得为你的 Agent 引入一张图。先澄清一个容易混的点,之前写《从 Loop 到 Graph》时说过,Agent 编排里的那张「执行图」不是知识图谱,一个组织的是系统怎么运转,一个组织的是系统知道什么,这篇讲的是后者。至于让 LLM 自己写 Cypher 会发生什么,下篇再引爆,友情提示,场面比想象中惨烈。

01 关系是一等公民,不是 JOIN 的产物

从一个具体查询说起。社交数据里要回答「Alice 的朋友的朋友看过哪些电影」,用 MySQL 大概长这样。

1SELECT DISTINCT w.movie_title
2FROM friend f1
3JOIN friend f2 ON f2.user_id = f1.friend_id
4JOIN watch w ON w.user_id = f2.friend_id
5WHERE f1.user_id = 'alice';
6

两层 JOIN 不算痛。真正的痛在需求的变化。产品说要「朋友的朋友的朋友」,你加一层 JOIN。说要排除 Alice 自己看过的,再加一个 NOT IN 子查询。哪天产品经理说了句「咱做个六度人脉功能吧」,你的 SQL 就变成了一张自连接的千层饼,执行计划里全是表扫描。

索引一个都救不了。

图数据库对这个问题的回答很直接。关系不是每次查询时 JOIN 出来的,是写入时就存下来的。 Neo4j 的存储模型有个说法叫 index-free adjacency,每个节点直接持有指向邻居的引用。查 Alice 的朋友,顺着指针跳一步就到了。查朋友的朋友,再跳一步。遍历的代价只和「这一跳有多少个邻居」有关,和整张图里是几百万还是几亿个节点无关。

这句话反过来也成立。如果你的查询模式是全表聚合、跑报表,关系本身不参与过滤,那图的这个优势一文不值,老老实实用 MySQL 或者 ClickHouse,别为了时髦上图。

02 五分钟跑起来一个 Neo4j

工具链从简,一条 Docker 命令的事。

1docker run -d --name neo4j \
2  -p 7474:7474 -p 7687:7687 \
3  -e NEO4J_AUTH=neo4j/neo4j12345 \
4  neo4j:5
5

跑起来之后浏览器打开 http://localhost:7474,就是 Neo4j Browser,一个网页版的查询台。7474 是 HTTP 端口,7687 是 Bolt 协议端口,后面 Python 驱动连的是后者。Python 侧装个官方驱动就能干活。

1pip install neo4j
2
1from neo4j import GraphDatabase
2
3driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "neo4j12345"))
4with driver.session() as session:
5    result = session.run("RETURN 1 AS one")  # 图数据库版的 hello world
6    print(result.single()["one"])
7driver.close()
8

03 建模,把世界拆成节点和关系

属性图模型只有三个角色。节点是实体,一个人、一部电影。关系是实体之间的有向连接,谁看过谁。属性是挂在节点或关系上的键值对,电影的上映年份、看片的评分。

把这句话翻译成建模口诀就是,名词做节点,动词做关系。

1CREATE (alice:Person {name: 'Alice', city: '北京'}),
2       (bob:Person {name: 'Bob', city: '上海'}),
3       (carol:Person {name: 'Carol', city: '北京'}),
4       (dave:Person {name: 'Dave', city: '深圳'}),
5       (inception:Movie {title: '盗梦空间', released: 2010}),
6       (matrix:Movie {title: '黑客帝国', released: 1999}),
7       (truman:Movie {title: '楚门的世界', released: 1998}),
8       (alice)-[:FRIEND_OF {since: 2021}]->(bob),
9       (bob)-[:FRIEND_OF {since: 2020}]->(carol),
10       (carol)-[:FRIEND_OF {since: 2022}]->(dave),
11       (alice)-[:WATCHED {score: 5}]->(inception),
12       (bob)-[:WATCHED {score: 5}]->(inception),
13       (bob)-[:WATCHED {score: 4}]->(matrix),
14       (carol)-[:WATCHED {score: 5}]->(truman),
15       (dave)-[:WATCHED {score: 4}]->(inception)
16

把这段贴进 Browser,回车,demo 图就建好了。PersonMovie 是标签(label),用来圈定一类节点,扮演类似「表」的角色。FRIEND_OF 这种全大写的是关系类型。注意关系是有方向的,(alice)-[:WATCHED]->(inception) 是 Alice 看了盗梦空间,反过来就不成立。方向这个细节先记一下,下篇讲 LLM 生成 Cypher 时它是重灾区。

FRIEND_OF

FRIEND_OF

FRIEND_OF

WATCHED 5

WATCHED 5

WATCHED 4

WATCHED 5

WATCHED 4

Alice

Bob

Carol

Dave

盗梦空间

黑客帝国

楚门的世界

口诀是入口不是终点。评分现在只是 WATCHED 关系上的一个属性,哪天产品要给评分挂评论、挂时间线,这个属性就该升级成一个独立的 Rating 节点。属性图建模里没有唯一正确答案,只有当前需求下的合理答案,这是它和关系型第三范式最大的思维差异,范式怕冗余,图怕的是把该成为实体的东西埋进属性里。

04 Cypher,用 ASCII 画出来的查询语言

Cypher 的设计哲学是「你画的图就是你写的查询」。模式用一对圆括号表示节点,方括号加箭头表示关系,(a)-[:KNOWS]->(b) 本身就是一张示意图。上手比 SQL 容易,坑也真不少,这里只讲后面会用到的核心子集。

最基础的模式匹配。

1-- 找出所有在北京的人
2MATCH (p:Person {city: '北京'})
3RETURN p.name
4

加条件用 WHERE,语义和 SQL 基本一致。真正体现图特色的是变长路径,方括号里的 *1..2 表示关系深度区间。

1-- Alice 的朋友,和朋友的朋友
2MATCH (a:Person {name: 'Alice'})-[:FRIEND_OF*1..2]->(f:Person)
3RETURN DISTINCT f.name
4

回头看第 01 节那个两层 JOIN,这里一行模式写完。扩展性更是两个物种,六度人脉无非是把 *1..2 改成 *1..6,SQL 那边要再叠四层自连接。图的护城河就在这,遍历深度的边际成本约等于零。

顺着 demo 图再走一步,把「Alice 的朋友看过哪些电影」查出来,顺带感受一下结果长什么样。

1MATCH (a:Person {name: 'Alice'})-[:FRIEND_OF*1..2]->(f:Person)-[:WATCHED]->(m:Movie)
2RETURN DISTINCT m.title
3
1"盗梦空间"
2"黑客帝国"
3"楚门的世界"
4

聚合也顺手写一个,给每部电影的评分求平均。

1MATCH (p:Person)-[r:WATCHED]->(m:Movie)
2RETURN m.title, avg(r.score) AS avg_score, count(p) AS voters
3ORDER BY avg_score DESC
4

接着是 MERGE,可以理解成「存在就复用,不存在就创建」。

1MERGE (p:Person {name: 'Alice'})
2ON CREATE SET p.created_at = timestamp()  -- 第一次创建时执行
3ON MATCH SET p.visit_count = coalesce(p.visit_count, 0) + 1  -- 已存在时执行
4RETURN p
5

这个命令在 Agent 场景里出场率极高,写记忆、做实体消歧都靠它。它也有一些微妙的坑,什么时候坑、怎么坑,下篇专门讲。

性能方面记住三件事就够了。查询永远带 LIMIT。变长路径永远给上界,写 *1..3 而不是裸的 *,环状数据会把你拖进无限遍历。高频过滤的属性建索引,CREATE INDEX person_name FOR (p:Person) ON (p.name)。至于看不懂一条查询为什么慢,前面加个 PROFILE 看执行计划,哪些操作在扫全库一目了然。EXPLAIN 和 PROFILE 这对兄弟下篇还有大用,先按下不表。

05 Agent 场景,什么时候值得引入一张图

工具都齐了,回到选型。我在 RAG 系列里说过一句话,GraphRAG 是对症药,不是通用升级。这一节把「对症」具体化成一张决策表。

你的需求长这样更合适的选择原因
语义相似、模糊召回(找相关文档)向量数据库embedding 的主场,图帮不上忙
精确点查、事务、报表关系数据库SQL 生态成熟,别为了时髦上图
多跳关系、路径、环检测(风控团伙、供应链穿透)图数据库遍历是原生操作,深度增加成本几乎不变
跨文档的全局模式归纳GraphRAG图加社区摘要,补向量检索的短板
Agent 的长期记忆图加向量的混合实体和关系要长期演化,还要能模糊召回

重点展开最后一行,它是 2025 年之后图数据库在 Agent 领域最热的落地点。我之前写《长会话状态治理》时用 Redis 存会话状态,那解决的是「短期工作记忆」。长期记忆是另一回事,用户上周说自己对花生过敏,今天问「帮我订个花生酱」,Agent 得把隔着七天的两个信息点连起来。你想想看,这类实体加关系的记忆天然就是图结构,Zep 的开源框架 Graphiti 和 Mem0 的图记忆模式走的都是这个路线,底层都对接 Neo4j。

图记忆也不是免费午餐。实体抽取要跑 LLM,关系要维护时间有效性,比如用户去年过敏今年好了没,图会越长越大。坦率的讲,如果你的 Agent 会话生命周期只有一轮,那就别碰图,纯浪费。

06 图从哪来

查询写得很爽,但这一切的前提是图已经存在。建图是另一半工程,而且往往是更贵的那一半。

主流路线三条。一是手写 ETL,从关系库抽数据拼节点拼关系,质量最可控,量大了人肉扛不住。二是规则加词典的抽取,适合结构规整的日志类数据。三是让 LLM 从非结构化文本里抽实体和关系,Neo4j 官方的 LLM Knowledge Graph Builder 就是这条路线的参考实现,接上 PDF、网页、YouTube 链接就能自动建图。我在 RAG 系列里提过,LLM 抽取建图的成本可以是向量索引的十到五十倍,这个数字直接决定你该不该走第三条路线。

建图的具体工程细节够单独写一篇,这里先挖个坑。这篇的任务是把「图侧的查询能力」讲清楚,建图路线的取舍留到以后展开。

07 写在最后

回顾一下这篇的要点。

  • 图数据库的护城河是关系即存储,遍历深度的边际成本约等于零,多跳和路径类查询是它的主场
  • 建模口诀是名词做节点、动词做关系,但属性和节点之间没有永恒边界,跟着需求演化
  • Cypher 的核心是模式匹配,变长路径 *1..2 是对 SQL 自连接的降维打击
  • Agent 场景的选型看需求形态,长期记忆和多跳推理值得上图,模糊召回和报表不要凑热闹
  • 建图是另一半工程,LLM 自动建图省人力但真贵,成本要提前算账

说真的,这篇通篇都是人肉写 Cypher,是我自己现学现卖的过程记录,demo 那张小图我反复删了重建了好几轮。但你可能已经注意到了,03 节那句「方向这个细节先记一下」,04 节那句「MERGE 有微妙的坑」,还有被按下不表的 EXPLAIN 兄弟。下篇就让 LLM 亲自下场写 Cypher,把这些埋好的线一根根引爆。


Neo4j与Cypher入门:从图思维到多跳查询实战》 是转载文章,点击查看原文


相关推荐


Ubuntu 还是 CentOS?云服务器部署全流程对比与实操指南
木易 士心2026/8/9

文章目录 一、 概述二、 核心差异对比表三、 云服务部署架构图四、 详细部署步骤对比1. 远程连接2. 系统更新与软件安装3. 防火墙与端口配置4. 服务管理5. 文件路径差异 五、 避坑指南与选型建议1. 避坑指南2. 选型建议 一、 概述 在云服务部署中,选择操作系统是第一步也是至关重要的一步。Ubuntu 和 CentOS 是目前市场份额最高的两大 Linux 发行版,分别代表了 Debian 系和 RedHat 系的典型特征。 Ubuntu: 以“开


Kimi 3 全方位评测:从“长文本”到“强逻辑”的跨越
木易 士心2026/7/31

文章目录 1. 前言:告别“记忆长”,走向“理解深”2. 核心代际对比:不只是参数提升,更是范式变革3. 深度功能测评:场景化实战拆解3.1 长文本“无损记忆”——从“大海捞针”到“精准穿刺”3.2 复杂推理——像侦探一样拆解迷题3.3 代码工程能力——从“代码补全”到“架构师视角”3.4 多模态联合分析——图表+文本的“通感”理解3.5 拓展案例分析:垂直领域的“专家级”表现 4. 综合能力雷达图:五维全面扩展5. 典型应用场景与选型建议6. 未来展望:从“阅读者”进化为“智能体”6.


如何使用 OpenTelemetry 对你的搜索 API 进行埋点,并使用 ES|QL 对其进行查询
Elasticsearch2026/7/23

作者:来自 Elastic Matthew Adams 向你的 OpenTelemetry span 添加自定义属性,并运行六个 ES|QL 查询,以揭示最热门的搜索、零结果率以及最慢的查询。 Elasticsearch 提供了大量新功能,帮助你针对自己的使用场景构建最佳搜索解决方案。在我们的实践网络研讨会《构建现代 Search AI 体验》中,了解如何将这些功能付诸实践。你还可以立即开始 免费 Cloud 试用,或在你的 本地机器 上试用 Elastic。 使用大约 20 行的 OpenT


Linux 基础指令从入门到精通:基础指令、重定向、管道与权限完全指南
meilindehuzi_a2026/7/15

Linux 基础指令从入门到精通:基础指令、重定向、管道与权限完全指南 前言1. Linux、操作系统与终端1.1 从 UNIX 到 Linux1.2 操作系统、Shell 与远程登录 2. Linux 文件系统与路径2.1 一切皆文件与目录树2.2 绝对路径、相对路径与定位命令2.3 使用 ls 读懂目录信息 3. 文件与目录的创建、复制、移动和删除3.1 touch、stat、mkdir 与 tree3.2 cp 与通配符3.3 mv、rmdir 与 rm 4. 查看文件、帮助


Rust 开发环境安装(Windows|macOS|Linux)
程序员爱钓鱼2026/7/7

《Rust 编程实战》系列第 2 篇 上一篇我们介绍了为什么要学习 Rust,以及 Rust 的应用场景。 从这一篇开始,我们正式进入 Rust 的学习之旅。 在开始编写 Rust 程序之前,首先需要搭建开发环境。Rust 官方提供了统一的安装工具,无论是 Windows、macOS 还是 Linux,安装方式都非常简单。 本文将带你一步一步完成 Rust 开发环境的安装,并验证是否安装成功。 Rust 官方推荐安装方式 Rust 官方推荐使用 rustup 来安装和管理 Rust。 rus


使用 C# 将 HTML 转 Word 或者 Word 转 HTML
秋天的落叶铺满小路2026/6/29

HTML作为Web内容的标准格式,与Word这种通用的办公文档格式之间的相互转换,是许多企业级应用中的常见需求。无论是将数据库中的动态内容生成为Word报告,还是将现有的Word文档发布为网页,掌握高效的转换方法都能显著提升工作效率。 本文将介绍如何使用 Spire.Doc for .NET 这个专业的文档处理组件,通过 C# 代码轻松实现 HTML 与 Word 之间的相互转换。 为什么选择 Spire.Doc for .NET? .NET 框架本身并不原生支持 Word 或 HTM


Agent 记忆管理
copyer_xyf2026/6/20

模型本身是无状态的。 这一次调用和下一次调用之间,模型不会自动记住你之前说过什么。它看起来“有记忆”,通常是因为 Agent 在每次调用模型前,把历史消息重新组织成上下文发给了模型。 图里的关键点是:Agent 一直在收集用户消息、模型回复、工具结果,然后把这些消息组成队列。模型每次回答时,看到的是这次传进去的完整上下文,而不是它自己真的保存了记忆。 先记住一句话:Agent 的 Memory 本质上是消息管理,不是模型真的拥有记忆。 为什么需要 Memory 如果你连续问两句话: 用户:我叫


Kotlin run 详解:把对象操作收进作用域,再把结果带出来
唐青枫2026/6/12

简介 run 是 Kotlin 标准库里的作用域函数。 作用域函数常见有 5 个: let run with apply also run 的特点比较鲜明: 在对象作用域里执行一段逻辑,然后返回 Lambda 最后一行的结果。 常见写法: val result = user.run { "$name-$age" } 这里的 name、age 来自 user 对象,result 是 Lambda 最后一行的字符串。 run 适合这类场景: 在一个对象里连续读取多个属性 对对象做


为什么 Clean Architecture 能让 ViewModel 保持轻量?
潜龙勿用之化骨龙2026/6/5

在 Android 开发中,MVVM 是非常常见的 UI 架构模式。它通过 ViewModel 承接 UI 状态和用户交互,让界面层变得更加清晰。 但在真实项目里,随着业务不断增加,很多团队都会遇到同一个问题: ViewModel 越写越胖,业务判断、网络请求、数据保存、异常处理全都堆在里面。 一开始这样写可能很方便,但当业务规则越来越复杂时,ViewModel 就会逐渐变成一个“业务大杂烩”:难维护、难复用、难扩展。 Clean Architecture 的价值,正是在于帮助我们把这些职责


Git & Linux 速查表
love8888_cnsd2026/5/29

📋 Git & Linux 速查表 — Java 后端向 一、Git 操作速查 🔥 最高频(日常必用) 命令说明git status查看工作区状态(最常用,肌肉记忆)git add .添加所有改动到暂存区git add <file>添加指定文件到暂存区git commit -m "type: 描述"提交到本地仓库(推荐语义化 message)git push推送到远程仓库git pull拉取并合并远程更新(fetch + merge)git log --oneline -10查看最近 1

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读