首页 > 解决方案 > 避免跨产品 APOC 查询的方法(使用 hashmap?)?

问题描述

我目前有一个Neo4J数据库,它的数据结构简单,由大约 4 亿个组成(:Node {id:String, refs:List[String]}),具有两个属性: An id,它是一个字符串,以及refs,它是一个字符串列表。

我需要搜索所有这些节点以识别它们之间的关系。如果一个节点在另一个鼻子id的列表中,则存在这些定向关系。ref一个简单的查询可以完成我想要的(但太慢了):

MATCH (a:Node), (b:Node) 
WHERE ID(a) < ID(b) AND a.id IN b.refs
CREATE (b)-[:CITES]->(a)

我可以使用apoc.periodic.iterate,但查询仍然太慢:

CALL apoc.periodic.iterate(
"MATCH (a:Node), (b:Node) 
WHERE ID(a) < ID(b) 
AND a.id IN b.refs RETURN a, b",
"CREATE (b)-[:CITES]->(a)",
{batchSize:10000, parallel:false,iterateList:true})

关于如何有效地建立这个数据库和关系的任何建议?当我第一次将节点添加到数据库时,我对创建哈希表的想法很模糊,但我不确定如何实现这一点,尤其是在 Neo4j 中。

谢谢你。

标签: databaseneo4jgraph-databases

解决方案


如果您首先在 上创建索引:Node(id),如下所示:

CREATE INDEX ON :Node(id);

那么这个查询应该能够利用索引快速找到每个a节点:

MATCH (b:Node)
UNWIND b.refs AS ref
MATCH (a:Node)
WHERE a.id = ref
CREATE (b)-[:CITES]->(a);

目前,Cypher 执行计划程序不支持在直接比较 2 个属性的值时使用索引。在上面的查询中,WHERE子句将属性与变量进行比较,因此可以使用索引。

省略了ID(a) < ID(b)测试,因为您的问题没有说明需要以这种方式订购本机节点 ID。

[更新 1]

如果您想并行运行创建步骤,请尝试使用 APOC 过程apoc.periodic.iterate

CALL apoc.periodic.iterate(
  "MATCH (b:Node) UNWIND b.refs AS ref RETURN b, ref",
  "MATCH (a:Node {id: ref}) CREATE (b)-[:CITES]->(a)",
  {batchSize:10000, parallel:true})

传递给过程的第一个 Cypher 语句只返回每个b/ref对。第二条语句(并行运行)使用索引来查找a节点并创建关系。这种工作分工将更昂贵的处理放在并行线程中运行的语句中。该iterateList: true选项被省略,因为我们(可能)希望第二条语句为每个b/ref对并行运行。

[更新 2]

如果并行执行尝试将关系添加到相同的节点,您可能会遇到死锁错误(因为每个并行事务将尝试写锁定每个新关系的结束节点)。为避免仅涉及b节点的死锁,您可以执行以下操作以确保b不并行处理节点:

CALL apoc.periodic.iterate(
  "MATCH (b:Node) RETURN b",
  "UNWIND b.refs AS ref MATCH (a:Node {id: ref}) CREATE (b)-[:CITES]->(a)",
  {batchSize:10000, parallel:true})

但是,如果并行执行可以尝试写锁定相同的a节点(或者如果任何b节点也可以用作a节点),这种方法仍然容易出现死锁。但至少希望这个附录能帮助你理解这个问题。

[更新 3]

由于这些死锁是依赖于多个并行执行尝试同时锁定相同节点的竞争条件,因此您可以通过在失败时重试“内部语句”来解决此问题。您还可以尝试减小批量大小,以减少多个并行重试在时间上重叠的可能性。像这样的东西:

CALL apoc.periodic.iterate(
  "MATCH (b:Node) RETURN b",
  "UNWIND b.refs AS ref MATCH (a:Node {id: ref}) CREATE (b)-[:CITES]->(a)",
  {batchSize: 1000, parallel: true, retries: 100})

推荐阅读