有效地将 UUID 分配给 Neo4j 中的连接组件

Efficiently assigning UUIDs to connected components in Neo4j

我使用 Neo4j 图形算法库中的 algo.unionFind 函数将我的图形划分为约 400,000 个连通分量。

同一连通分量内的每个节点 n 具有相同的 n.partition 值。但是,现在我想为每个连接的组件分配一个 UUID,以便连接组件中的每个节点 n 都将 n.uuid 填充一个组件 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"

最好的方法是在 Cypher 中 install the APOC plug-in to Neo4j so that you can use the 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

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

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

为此,您需要按分区值收集节点,这意味着每个不同的分区只有一行。然后创建 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(),以避免堆问题。