Neo4j作为目前最流行的图数据库之一,除了基础的CRUD和Cypher查询能力之外,还提供了专门的图形数据科学库GDS(Graph Data Science),里面封装了几十种经过优化的图算法,包括页面排名、社区检测、最短路径、节点相似度等。想在项目中真正用上这些算法,需要理解它的工作模式:算法不是直接在数据库原始数据上跑,而是先要把数据加载到内存中的图投影里,再对投影执行计算。这一点和很多人想象中的用法不太一样,也是初学者最容易踩坑的地方。本文通过多个实用示例,带你完整走一遍GDS算法的调用流程。

GDS插件安装与图投影的创建
使用GDS之前,必须先安装对应版本的插件。GDS版本要和Neo4j数据库版本匹配,例如Neo4j 5.x对应GDS 2.x系列。安装方式有两种:如果使用Neo4j Desktop,直接在插件面板里一键安装即可;如果是Docker或服务器部署,需要下载jar包放到plugins目录,并在配置文件neo4j.conf中允许加载该包:
# neo4j.conf 中添加 dbms.security.procedures.unrestricted=gds.* # Docker 启动示例 docker run -d \ --name neo4j-gds \ -p 7474:7474 -p 7687:7687 \ -e NEO4J_AUTH=neo4j/password123 \ -e NEO4J_PLUGINS='["graph-data-science"]' \ neo4j:5
安装完成后重启服务,用CALL gds.version()验证是否生效。接下来创建图投影,这是调用一切算法的前提。图投影相当于把数据库中指定的节点和关系抽取一份放到内存里,可以只选取感兴趣的标签和关系类型,从而减少内存占用。例如有一个社交网络,Person节点之间有FOLLOWS关系,创建投影的语句如下:
// 创建名为social的原生投影 CALL gds.graph.project( 'social', 'Person', 'FOLLOWS' ) YIELD graphName, nodeCount, relationshipCount; // 查看当前已有的图投影 CALL gds.graph.list();
如果需要带属性或关系权重,可以在第三个参数里用对象形式声明。投影创建好之后会常驻内存,用完建议调用gds.graph.drop删除释放资源,否则在数据量大时容易造成内存压力。算法名称后面带stream或write等后缀,代表不同的结果输出模式,这是GDS调用语法的一个核心设计。
常用算法的调用示例:中心性与社区检测
页面排名是最经典的节点重要性算法,可以用来找出社交网络中的关键人物,或者知识图谱中最核心的概念节点。stream模式会把计算结果以行的形式返回,适合进一步查询和筛选:
// 页面排名,stream模式直接返回结果
CALL gds.pageRank.stream('social')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS person, score
ORDER BY score DESC LIMIT 10;如果想把结果持久化到节点属性上,方便后续直接查询,就改用write模式。度中心性算法则更轻量,统计每个节点的入度或出度,快速判断节点的活跃程度:
// 页面排名结果写回节点属性
CALL gds.pageRank.write('social', {
writeProperty: 'pageRankScore'
})
YIELD nodePropertiesWritten, ranIterations;
// 度中心性
CALL gds.degree.stream('social')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS person, score
ORDER BY score DESC;社区检测方面,标签传播算法Louvain和Label Propagation最常用,可以把图中联系紧密的节点划分成若干社群,应用场景包括用户分群、推荐系统的相似人群挖掘等。以Louvain为例:
// Louvain社区检测
CALL gds.louvain.stream('social')
YIELD nodeId, communityId
RETURN communityId,
collect(gds.util.asNode(nodeId).name) AS members
ORDER BY size(members) DESC;运行后会得到每个社群的成员列表,communityId相同的节点属于同一个社区。需要注意的是,Louvain适合中等规模数据且追求较高质量划分的场景,而Label Propagation速度更快,适合超大规模图的粗略划分,两者可以按需选择。
最短路径算法与Java程序中调用GDS
路径类算法在关系网络中应用极广,比如交通网络中的最优路线、反欺诈中挖掘资金流转链条。GDS提供了Dijkstra和A*等加权最短路径算法。假设FOLLOWS关系上有weight属性表示关注成本,可以这样查询两个节点之间的最短路径:
// 先匹配起止节点,再调用Dijkstra算法
MATCH (source:Person {name: '张三'})
MATCH (target:Person {name: '李四'})
CALL gds.shortestPath.dijkstra.stream('social', {
sourceNode: id(source),
targetNode: id(target),
relationshipWeightProperty: 'weight'
})
YIELD index, sourceNode, targetNode,
totalCost, nodeIds, costs, path
RETURN totalCost,
[n IN nodes(path) | n.name] AS route,
costs;除了在Cypher中直接调用,业务系统更常见的做法是通过Java驱动在程序里执行这些算法查询。下面的示例演示了用Java驱动连接Neo4j并执行页面排名的完整流程:
import org.neo4j.driver.*;
public class GdsDemo {
public static void main(String[] args) {
try (Driver driver = GraphDatabase.driver(
"bolt://localhost:7687",
AuthTokens.basic("neo4j", "password123"))) {
try (Session session = driver.session()) {
// 执行GDS页面排名查询
Result result = session.run(
"CALL gds.pageRank.stream('social') " +
"YIELD nodeId, score " +
"RETURN gds.util.asNode(nodeId).name AS name, score " +
"ORDER BY score DESC LIMIT 5");
while (result.hasNext()) {
Record record = result.next();
System.out.printf("节点: %s, 得分: %.4f%n",
record.get("name").asString(),
record.get("score").asDouble());
}
}
}
}
}对于追求更高性能的场景,GDS还支持以mutate模式把算法结果写入图投影本身,再供后续算法级联使用,例如先运行Louvain得到社区ID,再按社区分组运行节点相似度算法,这种管道式组合在复杂的推荐和风控建模中非常实用。最后提醒几点实践经验:图投影会占用堆内存,生产环境要合理配置dbms.memory.heap.max_size;每次算法迭代前确认投影是否需要重建,避免用到过期的拓扑数据;对大图先用sample模式或子图投影验证逻辑,再全量运行,可以有效降低试错成本。掌握这些模式之后,Neo4j的几十种图形算法基本都可以按同样的套路调用起来。