APOC 提供了一系列函数来重新绑定节点和关系
-
apoc.node.rebind(node)
-
apoc.rel.rebind(rel)
-
apoc.any.rebind(map/list/paths/…)
为何使用这些函数
与 3.5 版本及之前不同,在 Neo4j 4+ 版本中,实体持有对其源事务的引用。
例如,当我们在一个事务中创建节点 n1,在新事务中创建节点 n2,然后通过 org.neo4j.graphdb.Node.createRelationshipTo()
(即 n1.createRelationshipTo(n2)
)执行时,这可能会导致问题。
这是我们执行以下操作时发生的情况
// node creation
CREATE (:Article {content: 'contentBody'});
// iterate all (:Article) nodes and new transaction and rel creation
CALL apoc.periodic.iterate('MATCH (art:Article) RETURN art',
'CREATE (node:Category) with art, node call apoc.create.relationship(art, "CATEGORY", {b: 1}, node) yield rel return rel', {});
基本上,我们创建一个节点 (:Article)
,然后 apoc.periodic.iterate 的第二个参数打开一个新事务并创建一个节点 (:Category)
,最后我们尝试通过 apoc.create.relationship(它底层使用 org.neo4j.graphdb.Node.createRelationshipTo()
)在 (:Article)
和 (:Category)
之间创建关系。
如果我们尝试执行第二个查询,apoc.periodic.iterate 将返回一个类似于以下的 errorMessage
Failed to invoke procedure `apoc.create.relationship`: Caused by: org.neo4j.graphdb.NotFoundException: Node[10] is deleted and cannot be used to create a relationship": 1
如何解决
为了解决之前的 apoc.periodic,iterate
问题,我们可以利用第一个语句返回的内部 ID,然后通过 ID 匹配节点,这意味着执行 MATCH (n) WHERE id(n) = id(nodeId)
(此操作称为重新绑定)。
也就是说
CALL apoc.periodic.iterate('MATCH (art:Article) RETURN id(art) as id',
'CREATE (node:Category) WITH id, node MATCH (art) where id(art) = id
WITH art, node call apoc.create.relationship(art, "CATEGORY", {b: 1}, node) yield rel return rel', {});
或者,我们可以使用 apoc.node.rebind
函数包装需要重新绑定的节点,如下所示:
CALL apoc.periodic.iterate('MATCH (art:Article) RETURN art',
'CREATE (node:Category) with art, node call apoc.create.relationship(art, "CATEGORY", {b: 1}, node) yield rel return rel', {});
对于关系,我们可以使用 apoc.rel.rebind
// other operations...
MATCH (:Start)-[rel:REL]->(:End) /*...*/ RETURN apoc.rel.rebind(rel)
我们还可以使用 apoc.any.rebind(ANY)
来重新绑定放置在 map、list、path 或这三者组合中的多个实体。这将返回与参数中传入的结构相同的结构,但实体已被重新绑定。例如:
CREATE (a:Foo)-[r1:MY_REL]->(b:Bar)-[r2:ANOTHER_REL]->(c:Baz) WITH a,b,c,r1,r2
RETURN apoc.any.rebind({first: a, second: b, third: c, rels: [r1, r2]}) as rebind
CREATE p1=(a:Foo)-[r1:MY_REL]->(b:Bar), p2=(:Bar)-[r2:ANOTHER_REL]->(c:Baz)
RETURN apoc.any.rebind([p1, p2]) as rebind