MERGE'ing 在 Neo4j 中的超级节点上

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

在我继续使用 芝加哥犯罪数据集的 过程中,我想将犯下的罪行与其在 FBI 犯罪类型 层次结构中的位置联系起来。

这些是我要连接的子图:

我们在每个“犯罪”节点上都有一个“fbiCode”,它指示犯罪属于哪个“犯罪子类别”。

我从以下查询开始将节点连接在一起:


 MATCH (crime:Crime)
WITH crime SKIP {skip} LIMIT 10000

MATCH (subCat:SubCategory {code: crime.fbiCode}) MERGE (crime)-[:CATEGORY]->(subCat) RETURN COUNT(*) AS crimesProcessed

我让这个在 Python 脚本中运行,只要 'crimesProcessed' 返回值 > 0,它就会在每次迭代中将 'skip' 递增 10,000。

首先,“类别”关系的创建速度非常快,但它在大约 100 万个节点中明显减慢了速度。

我分析了查询,但查询计划没有显示任何明显错误。我怀疑我有一个超级节点问题,其中密码运行时遍历所有子类别的关系,以检查其中一个是否指向“MERGE”语句另一侧的犯罪。

我取消了导入作业并编写了一个查询来检查每个子类别有多少关系。从 1,000 到 93,000 不等,这在一定程度上证实了我的怀疑。

Michael 建议调整查询以使用最短路径函数来检查关系是否存在,然后如果关系不存在则使用“CREATE”子句创建它。

最短路径函数的巧妙之处在于它将从基数最低的一侧开始,一旦找到关系就会停止搜索。让我们看一下该版本的查询:


 MATCH (crime:Crime)
WITH crime SKIP {skip} LIMIT 10000

MATCH (subCat:SubCategory {code: crime.fbiCode}) MERGE (crime)-[:CATEGORY]->(subCat) RETURN COUNT(*) AS crimesProcessed

这样效果更好——10,000 个节点在大约 2.5 秒内处理——并且随着更多关系的添加,时间保持不变。这允许我创建所有类别节点,但如果我们使用 CREATE UNIQUE 而不是 MERGE ,我们实际上可以做得更好


 MATCH (crime:Crime)
WITH crime SKIP {skip} LIMIT 10000

MATCH (subCat:SubCategory {code: crime.fbiCode}) MERGE (crime)-[:CATEGORY]->(subCat) RETURN COUNT(*) AS crimesProcessed

使用此查询处理 10,000 个节点需要 250 毫秒 -900 毫秒秒来处理,这意味着我们可以在 5-6 分钟内处理所有节点——好时光!

我不太熟悉“CREATE UNIQUE”代码,所以我不确定它是否始终是“MERGE”的良好替代品,但在这种情况下它可以完成工作。

这里给我的教训是,如果一个查询花费的时间比你想象的要长,它应该尝试使用其他结构/其他结构的组合,看看情况是否有所改善——他们可能会!