alitrack

我在 DuckDB 里塞了一个图算法引擎

我在 DuckDB 里塞了一个图算法引擎。

它叫 duckdb_petgraph,1151 行 Rust,17 个图算法直接当 SQL 函数用。PageRank、最短路径、社区发现、中心度分析——不需要 Neo4j,不需要 NetworkX,一个 LOAD 语句全搞定。

先给你看切菜的部分。

LOAD 'petgraph_ext.duckdb_extension';

-- 构建图(字符串格式)
SELECT * FROM petgraph_build_graph('g', '[(0,1,2.5),(1,2,1.0)]', 1);

-- 最短路径
SELECT * FROM petgraph_shortest_path('@g', 0, 2);
-- → [0,1,2]  3.5

-- PageRank
SELECT * FROM petgraph_pagerank('@g', 0.85, 20);

-- 强连通分量
SELECT * FROM petgraph_scc('@g');

-- 社区检测
SELECT * FROM petgraph_louvain('@g');

如果你已经有数据在 DuckDB 表里,推荐用 LIST 类型的接口,不需要字符串拼接:

SET VARIABLE n  = (SELECT count(*) FROM edges);
SET VARIABLE src = (SELECT list(src ORDER BY src) FROM edges);
SET VARIABLE dst = (SELECT list(dst ORDER BY src) FROM edges);
SET VARIABLE wgt = (SELECT list(weight ORDER BY src) FROM edges);

SELECT * FROM petgraph_build_graph_list('g', getvariable('n')::INTEGER,
    getvariable('src'), getvariable('dst'), getvariable('wgt'), 1);

● ● ●

为什么做这个

我一直在用 DuckDB。做数据分析的时候,图计算的需求经常冒出来——但要为此单独搭一个 Neo4j 或装一个 NetworkX,太重了。

DuckDB 社区已经有 duckpgq,支持 SQL/PGQ 标准的属性图查询,但它偏"图模式匹配"——找路径、找子图——不太做 PageRank、社区检测、中介中心度这类算法。我想有一个直接用 SQL 就能跑的图算法库。PostgreSQL 有 pgvector 做向量检索,DuckDB 也该有自己的图算法扩展。

正好,Rust 的 petgraph 库提供了一套完整的图数据结构和算法。quack-rs 让写 DuckDB 扩展只需要声明式定义参数和输出列。两样东西拼起来,一个最小可用的版本,一个下午就出来了。

● ● ●

现在有什么

算法
做什么
支持什么图
shortest_path
A* 最短路径,带回溯
有向,带权
connected_components
连通分量(DFS)
无向
pagerank
PageRank,可调 alpha
有向
scc
Tarjan 强连通分量
有向
mst
Kruskal 最小生成树
无向,带权
betweenness_centrality
Brandes 中介中心度
无向
closeness_centrality
紧密度中心度
有向
eigenvector_centrality
特征向量中心度
有向
louvain
Louvain 社区发现
有向
toposort
Kahn 拓扑排序
有向
is_cyclic
环检测
有向
edges / neighbors
图浏览
两种都有

另有 build_graph_list、list_graphs、drop_graph 做图管理。图可以缓存(@name),反复用不会重复解析。

架构图

架构图

现在版本是 0.5.0,已经通过 PR #2383 合入 DuckDB Community Extensions 主仓库。你现在可以 INSTALL petgraph_ext FROM community; LOAD petgraph_ext; 一步加载,不需要自己编译。

● ● ●

和 duckpgq 的关系

duckpgq 做的是 SQL/PGQ 标准——声明式图模式匹配,像这样:

FROM GRAPH_TABLE (snb MATCH (a:Person)-[k:knows]->(b:Person) COLUMNS (a.id, b.id));

duckdb_petgraph 做的是图算法——你把图的边列表喂进去,它返回 PageRank 得分、SCC 编号、最短路径。两者互补:duckpgq 负责"图中的什么和什么有关",duckdb_petgraph 负责"这些东西谁更重要"。

● ● ●

我的诚实评价

这个项目现在是功能完整但工程上有不少要打磨。我自己让三个 AI agent 严格审查了一遍(security + correctness、performance + architecture、code quality),发现了一些不客气的问题:

已知的严重问题:

  • bench.sql
     调用的 API 签名和实际代码不匹配——这个文件是在迭代过程中留下的旧版本,需要重写
  • Louvain 社区发现在测试阶段用的是简化实现,边权重处理需要修正
  • 部分算法(betweenness、closeness)目前对加权图只支持 hop-count,权重支持在计划中

已知的工程改善项:

  • 7 个算法的 SQL 测试还没填充预期输出值——现在只验证"不崩溃"
  • 每次算法调用会重新克隆图数据(应该用 Arc::clone 复用),对大规模缓存图的性能有影响
  • 1151 行挤在单个 lib.rs 里,该拆成模块了
  • 全局图注册表是跨连接共享的,需要加命名空间或 TTL

这些是 0.5.0 提交社区时的真实状态。P0 问题在 PR 合入前已经修完(bench.sql 重写、Louvain 权重修正、graph clone 优化),剩下的工程改善项在后续版本逐步完善。开源项目的节奏就是这样——先做到"能用",再做到"好用",最后才是"完美"。

● ● ●

试一试

如果你用的是 DuckDB v1.5.5+:

INSTALL petgraph_ext FROM community;
LOAD petgraph_ext;
SELECT * FROM petgraph_shortest_path('[(0,1,2.5),(1,2,1.0),(0,2,5.0)]', 0, 2);
-- → [0,1,2]  3.5

从源码编译:

git clone --recurse-submodules [email protected]:alitrack/duckdb_petgraph.git
cd duckdb_petgraph
make configure && make release
duckdb -unsigned -c "LOAD 'build/release/extension/petgraph_ext/petgraph_ext.duckdb_extension'; SELECT * FROM petgraph_shortest_path('[(0,1,2.5),(1,2,1.0),(0,2,5.0)]', 0, 2);"

如果你已经在用 DuckDB 做数据管道,下次遇到"这些用户里谁的传播影响力最大"或者"这两个账号之间有什么共同好友链"的时候,试试不在 SQL 外面绕一圈 Python,直接在 DuckDB 里跑。

GitHub: github.com/alitrack/duckdb_petgraph

MIT 协议,随便用。