向Neo4j中的连接组件有效分配UUID

时间:2018-10-15 19:01:47

标签: neo4j cypher graph-algorithm

我已使用Neo4j Graph Algorithms库中的algo.unionFind函数将图形划分为约40万个连接的组件。

同一连接组件内的每个节点n具有相同的n.partition值。但是,现在我想为每个连接的组件分配一个UUID,以便在连接的组件中的每个节点n都将使用一个组件UUID填充n.uuid。最有效的方法是什么?

当前,我正在获取所有n.partition值的列表,然后遍历每个分区并运行Cypher查询以更新该分区的所有节点以具有生成的UUID。我使用的是Python包装器py2neo,此过程非常缓慢。

编辑:

我当前使用的Cypher查询是:

MATCH (n)
RETURN DISTINCT n.partition AS partition

获取分区ID的列表,然后迭代调用:

MATCH (n)
WHERE n.partition = <PARTITION_ID>
SET n.uuid = <GENERATED_UUID>

每个分区ID。

编辑2: 我可以使用以下查询来了解〜180k / 400k的连接组件:

CALL apoc.periodic.iterate(
"MATCH (n)
WITH n.partition as partition, COLLECT(n) as nodes
RETURN partition, nodes, apoc.create.uuid() as uuid",
"FOREACH (n in nodes | SET n.uuid = uuid)",
{batchSize:1000, parallel:true}
)

在出现堆错误之前:"neo4j.exceptions.ClientError: Failed to invoke procedure `apoc.periodic.iterate`: Caused by: java.lang.OutOfMemoryError: Java heap space"

2 个答案:

答案 0 :(得分:0)

最好的方法是将install the APOC plug-in转到Neo4j,以便可以在Cypher中使用UUID function apoc.create.uuid()。 (以便可以在同一交易中生成和分配)

要为每个分区创建1个uuid,您将需要使用WITH将uuid存储在一个临时变量中。它将按行运行,因此一旦拥有一个分区,就需要执行此操作

USING PERIODIC COMMIT 5000 // commit every 5k changes
MATCH (n)
WITH DISTINCT n.partition as p // will exclude null
WITH p, apoc.create.uuid() as uuid // create reusable uuid
// now just match and assign
MATCH (n)
WHERE n.partition = p
SET n.uuid = uuid

或按照InverseFalcon的建议

MATCH (n)
WHERE exists(n.partition) // to filter out nulls
WITH n.partition as p, collect(n) as nodes // collect nodes so each row is 1 partition, and it's nodes
WITH p, nodes, apoc.create.uuid() as uuid // create reusable uuid
FOREACH (n in nodes | SET n.uuid = uuid) // assign uuid to each node in collection

第一个查询是更定期提交的查询,因为它不需要将所有内容加载到内存中就可以开始进行分配。但是,如果没有perodic commit语句,它将最终将所有内容加载到内存中,因为它必须保留事务日志。一旦到达提交点,就可以清除事务日志以减少内存使用量。

如果您的数据集不是太大,那么第二个查询应该更快,因为通过在第一个节点扫描之后将所有内容保存在内存中,它不需要运行另一个节点扫描来查找所有节点。定期提交在这里无济于事,因为如果您炸开堆,几乎可以肯定会在初始扫描/收集阶段。

答案 1 :(得分:0)

为此,您需要按节点的分区值收集节点,这意味着每个不同的分区只有一行。然后创建UUID(它将每行执行一次),然后可以使用FOREACH应用于分区中的每个节点:

MATCH (n)
// WHERE exists(n.partition) // only if there are nodes in the graph without partitions
WITH n.partition as partition, collect(n) as nodes
WITH partition, nodes, randomUUID() as uuid
FOREACH (n in nodes | SET n.uuid = uuid)

根据图形中节点的数量,您可能需要将此与某些批处理(例如apoc.periodic.iterate())结合起来,以避免堆问题。