如何将Cypher查询结果用于GDS算法实现用户聚类?
问题:如何将虚拟关系投影到GDS图中用于用户聚类
我有一个由ClientInfo(用户)和DocInfo(文档)构成的二分图,用来存储客户端的文档阅读记录。我写了一段Cypher查询,能生成并展示用户间基于共同阅读文档的虚拟关系HAS_COMMON_DOCS,现在想把这个查询结果用到GDS算法里做用户聚类,但不知道怎么把查询结果投影到GDS中,试过下面的查询但没法正常运行。
生成虚拟关系的原查询
MATCH (n1:ClientInfo)-[:ACCESSED]->()<-[:ACCESSED]-(n2:ClientInfo) WITH n1, n2, count(*) AS commonDocs WITH n1, n2, commonDocs, count{(n1)-[:ACCESSED]->()} AS n1Docs, count{(n2)-[:ACCESSED]->()} AS n2Docs WHERE commonDocs >=2 RETURN n1, n2, apoc.create.vRelationship(n1, 'HAS_COMMON_DOCS', {commonDocs:commonDocs}, n2)
尝试的错误查询
MATCH (n1:ClientInfo)-[:ACCESSED]->()<-[:ACCESSED]-(n2:ClientInfo) WITH n1, n2, count(*) AS commonDocs WITH n1, n2, commonDocs, count{(n1)-[:ACCESSED]->()} AS n1Docs, count{(n2)-[:ACCESSED]->()} AS n2Docs WHERE commonDocs >= 5 AND n1.peopleCode <> 310962818 WITH n1, n2, apoc.create.vRelationship(n1,'HAS_COMMON_DOCS', {commonDocs:commonDocs}, n2) CALL gds.graph.project( 'users', ['ClientInfo', 'DocInfo'], ['ACCESSED','HAS_COMMON_DOCS'] ) YIELD graphName AS graph, nodeProjection, nodeCount AS nodes, relationshipProjection, relationshipCount AS rels;
核心问题
apoc.create.vRelationship生成的是临时虚拟关系,只在当前查询会话中存在,不会写入数据库。所以gds.graph.project直接指定HAS_COMMON_DOCS关系类型时,根本找不到这个关系。要把这类动态计算的关系投影到GDS图中,必须用Cypher投影而非简单的标签/关系类型投影。
两种可行解决方案
方案1:直接用GDS Cypher投影计算虚拟关系
不需要提前生成虚拟关系,直接在投影阶段通过Cypher语句定义用户间的连接和关系属性,一步到位:
CALL gds.graph.project.cypher( 'user-common-docs', // 节点投影:只保留目标聚类的用户节点,过滤指定用户 'MATCH (c:ClientInfo) WHERE c.peopleCode <> 310962818 RETURN id(c) AS id, labels(c) AS labels', // 关系投影:计算用户间共同文档数,同时应用过滤条件 'MATCH (c1:ClientInfo)-[:ACCESSED]->(d:DocInfo)<-[:ACCESSED]-(c2:ClientInfo) WHERE c1.peopleCode <> 310962818 AND c2.peopleCode <> 310962818 AND c1 <> c2 WITH c1, c2, count(d) AS commonDocs WHERE commonDocs >=5 RETURN id(c1) AS source, id(c2) AS target, commonDocs AS properties' ) YIELD graphName, nodeCount, relationshipCount RETURN graphName, nodeCount, relationshipCount;
- 优势:无需写入临时数据,直接在投影阶段完成关系计算,效率更高
- 提示:如果聚类只针对用户,不需要把文档节点加入投影,能大幅减少计算量
方案2:先将虚拟关系写入数据库(适合重复使用场景)
如果后续需要多次用到HAS_COMMON_DOCS关系,可以先把它转为真实关系写入数据库,再进行普通投影:
// 第一步:创建真实的HAS_COMMON_DOCS关系,用MERGE避免重复创建 MATCH (n1:ClientInfo)-[:ACCESSED]->()<-[:ACCESSED]-(n2:ClientInfo) WITH n1, n2, count(*) AS commonDocs WHERE commonDocs >=5 AND n1.peopleCode <> 310962818 AND n2.peopleCode <> 310962818 AND n1 <> n2 MERGE (n1)-[r:HAS_COMMON_DOCS]->(n2) SET r.commonDocs = commonDocs; // 第二步:进行GDS图投影 CALL gds.graph.project( 'users', 'ClientInfo', { HAS_COMMON_DOCS: { type: 'HAS_COMMON_DOCS', properties: 'commonDocs' } } ) YIELD graphName, nodeCount, relationshipCount RETURN graphName, nodeCount, relationshipCount;
- 优势:关系持久化,后续可重复调用
- 注意:必须用
MERGE而非CREATE,避免重复创建相同关系
后续聚类示例
投影完成后,就可以用GDS的聚类算法(比如Louvain)进行用户聚类:
CALL gds.louvain.stream('user-common-docs') YIELD nodeId, communityId MATCH (c:ClientInfo) WHERE id(c) = nodeId RETURN c.peopleCode AS userId, communityId ORDER BY communityId, userId;
内容的提问来源于stack exchange,提问作者pbh
相关产品推荐
相关产品推荐

