快速开始:建立图工程心智模型
本章目标
读完并动手完成后,你应能:
- 用 NetworkX 或 Neo4j 创建带标签与属性的小图。
- 写出两跳模式匹配查询,并解释与 SQL JOIN 的差异。
- 运行 BFS/最短路径与简单中心性,读懂结果含义。
- 判断一个业务问题是否「值得上图」。
本篇聚焦知识与动手直觉,不涉及把 graph-engineering.chenxiaoshivivid.top 部署到生产环境。
0. 你需要什么环境
推荐两条并行路径(可任选其一先走通):
| 路径 | 适合 | 安装要点 |
|---|---|---|
| Python + NetworkX | 算法直觉、教学原型 | pip install networkx matplotlib |
| Neo4j + Cypher | 持久化图、工业查询语法 | Neo4j Desktop 或 Docker 社区版 |
可选增强:py2neo / 官方 Python Driver、rdflib(RDF 入门)、torch-geometric(稍后再装)。
# 最小 Python 环境示例(使用虚拟环境)
python3 -m venv .venv
source .venv/bin/activate
pip install networkx matplotlib pandas
1. 十分钟:用 NetworkX 看见第一张图
import networkx as nx
import matplotlib.pyplot as plt
G = nx.DiGraph()
G.add_node("Alice", label="Person", city="Shanghai")
G.add_node("Bob", label="Person", city="Beijing")
G.add_node("Acme", label="Company", industry="AI")
G.add_edge("Alice", "Acme", type="WORKS_AT", since=2022)
G.add_edge("Bob", "Acme", type="WORKS_AT", since=2021)
G.add_edge("Alice", "Bob", type="KNOWS", weight=0.8)
print("节点数", G.number_of_nodes(), "边数", G.number_of_edges())
print("Alice 出边", list(G.out_edges("Alice", data=True)))
print("到 Acme 的最短路径", nx.shortest_path(G, "Alice", "Acme"))
观察三点:
- 节点携带业务类型(Person/Company)与属性。
- 边有类型与属性(since、weight),关系是一等对象。
- 有向图决定「Alice→Bob」与「Bob→Alice」是否同一关系。
练习:把「关注」建成双向两条边,再建成一条无向边,分别打印邻居集合,体会建模差异。
2. 十分钟:用 Cypher 表达同一模型
在 Neo4j Browser 中:
CREATE (a:Person {name:'Alice', city:'Shanghai'})
CREATE (b:Person {name:'Bob', city:'Beijing'})
CREATE (c:Company {name:'Acme', industry:'AI'})
CREATE (a)-[:WORKS_AT {since:2022}]->(c)
CREATE (b)-[:WORKS_AT {since:2021}]->(c)
CREATE (a)-[:KNOWS {weight:0.8}]->(b)
RETURN a, b, c;
查询「和 Alice 在同一公司的人」:
MATCH (a:Person {name:'Alice'})-[:WORKS_AT]->(c:Company)<-[:WORKS_AT]-(colleague:Person)
WHERE colleague <> a
RETURN colleague.name, c.name;
这是典型的模式匹配:你画出路径形状,引擎负责遍历。对比 SQL:你需要 employees、companies 两表 JOIN,并自己保证「同一公司」条件不写错。
3. SQL vs Cypher:同一业务问题
电商「买了 A 的用户还买了什么」:
-- SQL:多层 JOIN,深度增加时复杂度陡增
SELECT DISTINCT p2.name
FROM orders o1
JOIN order_items oi1 ON oi1.order_id = o1.id
JOIN order_items oi2 ON oi2.order_id IN (
SELECT o2.id FROM orders o2 WHERE o2.user_id = o1.user_id
)
JOIN products p2 ON p2.id = oi2.product_id
WHERE o1.user_id = 42 AND oi2.product_id <> oi1.product_id;
// Cypher:路径即模式
MATCH (u:User {id: 42})-[:PURCHASED]->(p1:Product)
MATCH (u)-[:PURCHASED]->(p2:Product)
WHERE p2 <> p1
RETURN DISTINCT p2.name;
心智转换清单:
- 先画 3–7 个节点的业务草图,再写查询。
- 边类型名用动词或动宾(
PURCHASED、BELONGS_TO),避免含糊的RELATED。 - 深度可变时用
*1..3,并设上限,防止意外全图扫描。
4. 图论最小武器库(动手版)
4.1 遍历:BFS 与 DFS
from collections import deque
def bfs(graph, start):
seen, q = {start}, deque([start])
order = []
while q:
u = q.popleft()
order.append(u)
for v in graph.successors(u):
if v not in seen:
seen.add(v)
q.append(v)
return order
BFS 给最短无权路径;DFS 适合拓扑排序与连通分量探索。图数据库内部的路径扩展,本质上是受索引与方向约束的受限 BFS/DFS。
4.2 最短路径
path = nx.shortest_path(G, "Alice", "Bob") # 无权
# 有权:nx.dijkstra_path(G, s, t, weight="weight")
社交「六度」查询在 Cypher 中:
MATCH (a:Person {name:'Alice'}), (b:Person {name:'Bob'})
MATCH p = shortestPath((a)-[:KNOWS*..6]-(b))
RETURN [n IN nodes(p) | n.name] AS route, length(p) AS hops;
务必限制 *..6:无上限的变长路径是生产事故高发区。
4.3 中心性直觉
| 指标 | 直观含义 | 何时用 |
|---|---|---|
| 度中心性 | 直接邻居多少 | 快速找枢纽 |
| PageRank | 被重要节点指向 | 权威排序、推荐种子 |
| Betweenness | 有多少最短路径经过我 | 桥接节点、风控切入点 |
| Closeness | 到他人平均距离 | 信息传播效率 |
print(nx.pagerank(G))
print(nx.degree_centrality(G))
先在 20 节点小图上肉眼验证「算法说的枢纽」是否符合业务直觉,再上万级图。
5. 第一个迷你知识图谱
场景:人物—任职—公司—位于—城市。
MERGE (alice:Person {name:'Alice'})
MERGE (acme:Company {name:'Acme'})
MERGE (sh:City {name:'Shanghai'})
MERGE (alice)-[:WORKS_AT {title:'Engineer'}]->(acme)
MERGE (acme)-[:LOCATED_IN]->(sh);
扩展问题:
- 「在上海工作的工程师有哪些?」——路径
Person-WORKS_AT->Company-LOCATED_IN->City。 - 若同一公司有多个办公城市,
LOCATED_IN应挂在公司还是「办公地」事件节点?这是实体中心 vs 事件中心建模分歧。
建议:关系简单且稳定 → 实体中心;关系带时间、地点、角色且频繁变化 → 引入事件/事实节点。
6. 导入第一批 CSV(概念步骤)
- 准备
nodes_person.csv、nodes_company.csv、edges_works_at.csv。 - 小数据用
LOAD CSV;百万级以上用批量导入工具(绕过逐条事务)。 - 导入后立刻跑:节点计数、边计数、度分布抽样、一条业务查询。
// 度分布抽样:找出超级节点苗头
MATCH (n)
RETURN labels(n)[0] AS label, n.name AS name, COUNT { (n)--() } AS degree
ORDER BY degree DESC LIMIT 20;
若 Top1 度远高于中位数,立刻记录,后续要做限流或边分片。
7. 查询优化入门:PROFILE
PROFILE
MATCH (a:Person {name:'Alice'})-[:KNOWS*1..3]-(b:Person)
RETURN DISTINCT b.name;
看执行计划是否:属性查找走索引、扩展方向合理、是否出现巨大中间结果。常见改进:
- 为高频查找属性建索引:
CREATE INDEX FOR (p:Person) ON (p.name); - 尽量带标签与边类型,避免全图
(n)--(m)。 - 参数化查询,禁止字符串拼接用户输入。
8. GraphRAG 最小闭环(本地思维实验)
不必先上完整平台,先用纸笔走通:
- 问题:「Alice 所在公司在哪座城市?」
- 实体链接:Alice →
Person节点。 - 子图扩展:
WORKS_AT、LOCATED_IN两跳。 - 证据:「Alice-WORKS_AT->Acme-LOCATED_IN->Shanghai」。
- 生成:回答并引用路径。
对比纯向量:若语料里只有「Acme 很强」而没有城市事实,向量可能胡答;图路径则要么找到,要么明确「图中无此边」。
9. 七天课表如何对接本章
| 天数 | 主题 | 本章可预热的动作 |
|---|---|---|
| Day1 | 建模范式 | 完成人物—公司图 |
| Day2 | 查询语言 | Cypher 两跳 + PROFILE |
| Day3 | 图算法 | PageRank / 最短路径 |
| Day4 | 图库架构 | 度分布与超级节点观察 |
| Day5 | 知识图谱 | MERGE + 属性约束 |
| Day6 | GNN/GraphRAG | 证据路径打包思维实验 |
| Day7 | 生产与前沿 | 导入校验与观测指标 |
子站 7 天学会 Graph Engineering 提供课表、算法沙盒与 SQL/Cypher 对比,建议与文档交叉阅读。
10. 验收清单(请自测)
- 能口述节点、边、标签、属性、方向的区别。
- 能写出至少一条 2 跳 Cypher,并解释结果。
- 能对小图运行 BFS 与 PageRank,并解释 Top 节点。
- 能指出一个「不该上图」的场景(例如纯报表聚合、无关系遍历)。
- 能列出导入后必查的 4 个健康指标:点数、边数、度 Top、一条黄金查询。
11. 常见新手坑
- 把所有字段都做成节点:颜色、状态枚举优先做属性,除非要围绕它们做关系分析。
- 无类型的 RELATED 边:后期无法过滤与索引,查询会变成全图泥潭。
- 忽略方向:
FOLLOWS画反会导致推荐与风控逻辑全部反转。 - 变长路径无上限:开发环境「碰巧很快」,生产直接超时。
- 只用可视化大屏验收:大屏好看不等于查询正确、证据可追溯。
12. 下一步
用一周时间每天做一个「垂直切片」:选一个真实小问题(通讯录、阅读书目、课程依赖),建图、查询、画路径、写三句结论。图工程的肌肉记忆,来自反复的「问题→模式→验证」,而不是只看术语表。
13. 小练习:社交媒体图(60 分钟)
设计并实现:
- 节点:
User、Post、Tag - 边:
FOLLOWS、POSTED、LIKED、TAGGED_WITH
要求:
- 至少 10 用户、30 帖子、15 标签。
- 查询「我关注的人点赞过的帖子」三跳路径。
- 用度中心性找出「被关注最多」与「发帖最多」是否同一批人。
- 写 200 字笔记:哪些边该有时间戳属性?哪些查询会撞上超级网红节点?
MATCH (me:User {name:$me})-[:FOLLOWS]->(f)-[:LIKED]->(p:Post)
RETURN p.id, p.text, count(*) AS likesFromFollowing
ORDER BY likesFromFollowing DESC LIMIT 10;
若 FOLLOWS 出边巨大,先限制 f 数量或按最近活跃过滤——这就是生产级「超级节点治理」的雏形。
14. 从快速开始到工程判断
快速开始的终点不是「会跑通示例」,而是建立判断力:何时用属性、何时用节点;何时用图算法、何时用简单计数;何时上 GraphRAG、何时纯 Cypher 已够用。带着这些问题进入开发指南,你会读得更快、踩坑更少。