Neo4j: 如何通过Cypher删除数据库中的所有重复关系?

6 投票
2 回答
3661 浏览
提问于 2025-04-18 02:14

我有一个非常大的数据库,里面有很多节点(超过1000万)。整个数据库里只有一种关系。不过,很多节点之间有重复的关系。我现在有一个cypher脚本,可以找到所有有重复关系的节点对,然后用一个python脚本逐个清理,只留下每对节点之间的一个唯一关系。

match (a)-[r]->(b) with a,b, count(*) as c where c>1 return a.pageid, b.pageid, c LIMIT 100000;

这个方法在小数据库上效果不错,但在大数据库上运行时,最终会因为内存不足而出错(即使我不断增加服务器的配置也没用)。

所以,我的问题有两个方面: 1) 有没有什么方法可以给关系加索引(现在是没有的),这样能加快处理速度? 2) 有没有一种cypher查询,可以快速(或者至少可靠地)删除数据库中所有重复的关系,只留下每对节点之间的一个唯一关系(前提是它们之间已经有关系)?

附注:我在一个运行ubuntu(12.x)的AWS服务器上使用neo4j 2.0.1。

再附注:我知道有一个答案:stackoverflow,但他问的是更具体的问题(针对两个已知的节点),而且那个覆盖整个数据库的答案现在好像不再适用了(可能是语法变了?)

谢谢大家的帮助!

2 个回答

2

这是一个经过修正的答案版本(通过插入WITH rr这一部分),可以在更新后的neo4j版本中使用,并且应该会更快(因为它只在需要的时候创建新的TAIL列表):

MATCH (a)-[r]->(b)
WITH a, b, COLLECT(r) AS rr
WHERE SIZE(rr) > 1
WITH rr
LIMIT 100000
FOREACH (r IN TAIL(rr) | DELETE r);

[更新]

如果你只想删除类型相同的重复关系,可以这样做:

MATCH (a)-[r]->(b)
WITH a, b, TYPE(r) AS t, COLLECT(r) AS rr
WHERE SIZE(rr) > 1
WITH rr
LIMIT 100000
FOREACH (r IN TAIL(rr) | DELETE r);
8

在链接的StackOverflow问题中,你遇到了什么错误?试试把:换成|FOREACH里,我看到的唯一可能导致问题的语法差别就是这个。用2.x的方式表达同样的意思,假设你的数据库里只有一种关系类型,可能是这样的:

MATCH (a)-[r]->(b)
WITH a, b, TAIL (COLLECT (r)) as rr
FOREACH (r IN rr | DELETE r)

我觉得WITH管道在没有重复的情况下会带着空的尾部,我不太清楚遍历一个空集合的成本有多高——我的感觉是,最好是在WITH之后用过滤器来引入限制,像这样:

MATCH (a)-[r]->(b)
WITH a, b, TAIL (COLLECT (r)) as rr
WHERE length(rr) > 0 LIMIT 100000
FOREACH (r IN rr | DELETE r)

因为这个查询根本不涉及属性(和你的查询不同,你的查询会返回(a)和(b)的属性),所以我觉得对于像你这样的中等规模图来说,它应该不会占用太多内存,但你还是需要试试限制的效果。

如果内存仍然是个问题,那么如果有办法限制一下要处理的节点(不涉及属性),那也是个好主意。如果你的节点可以通过标签区分,试着一次只运行一个标签的查询:

MATCH (a:A)-[r]->(b) //etc..
MATCH (a:B)-[r]->(b) //etc..

撰写回答