避免跨产品 APOC 查询的方法(使用 hashmap?)?
Methods to avoiding cross-product APOC query (using hashmap?)?
我目前有一个 Neo4J
数据库,其数据结构简单,由大约 4 亿 (:Node {id:String, refs:List[String]})
组成,具有两个属性: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})
关于如何有效地建立这个数据库和关系有什么建议吗?当我第一次将节点添加到数据库时,我对创建散列 table 有模糊的想法,但我不确定如何实现它,尤其是在 Neo4j 中。
谢谢。
如果您首先在 :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 语句只是 returns 每个 b
/ref
对。第二条语句(并行 运行)使用索引查找 a
节点并创建关系。这种分工将更昂贵的语句 运行ning 处理放在并行线程中。 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]
由于这些死锁是取决于多个并行执行试图同时锁定相同节点的竞争条件,您可以通过在失败时重试 "inner statement" 来解决此问题。您也可以尝试将批量大小变小,以降低多次并行重试在时间上重叠的可能性。像这样:
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})
我目前有一个 Neo4J
数据库,其数据结构简单,由大约 4 亿 (:Node {id:String, refs:List[String]})
组成,具有两个属性: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})
关于如何有效地建立这个数据库和关系有什么建议吗?当我第一次将节点添加到数据库时,我对创建散列 table 有模糊的想法,但我不确定如何实现它,尤其是在 Neo4j 中。
谢谢。
如果您首先在 :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 语句只是 returns 每个 b
/ref
对。第二条语句(并行 运行)使用索引查找 a
节点并创建关系。这种分工将更昂贵的语句 运行ning 处理放在并行线程中。 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]
由于这些死锁是取决于多个并行执行试图同时锁定相同节点的竞争条件,您可以通过在失败时重试 "inner statement" 来解决此问题。您也可以尝试将批量大小变小,以降低多次并行重试在时间上重叠的可能性。像这样:
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})