From 2ac45f45d2eb5938667e520f0f1ca271e944c116 Mon Sep 17 00:00:00 2001 From: Giuseppe Villani Date: Mon, 2 May 2022 12:21:03 +0200 Subject: [PATCH] Fixes #2699: The apoc.refactor.mergeNodes remove entities if a list with 2 equal nodes is passed --- .../java/apoc/refactor/GraphRefactoring.java | 8 +++++--- .../apoc/refactor/GraphRefactoringTest.java | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/apoc/refactor/GraphRefactoring.java b/core/src/main/java/apoc/refactor/GraphRefactoring.java index 3686ea0ab4..4ff02d505e 100644 --- a/core/src/main/java/apoc/refactor/GraphRefactoring.java +++ b/core/src/main/java/apoc/refactor/GraphRefactoring.java @@ -278,8 +278,9 @@ private Map generateStandinMap(List> standins) { public Stream mergeNodes(@Name("nodes") List nodes, @Name(value = "config", defaultValue = "{}") Map config) { if (nodes == null || nodes.isEmpty()) return Stream.empty(); RefactorConfig conf = new RefactorConfig(config); + Set nodesSet = new LinkedHashSet<>(nodes); // grab write locks upfront consistently ordered - nodes.stream().distinct().sorted(Comparator.comparingLong(Node::getId)).forEach(tx::acquireWriteLock); + nodesSet.stream().sorted(Comparator.comparingLong(Node::getId)).forEach(tx::acquireWriteLock); final Node first = nodes.get(0); final List existingSelfRelIds = conf.isPreservingExistingSelfRels() @@ -288,7 +289,7 @@ public Stream mergeNodes(@Name("nodes") List nodes, @Name(valu .collect(Collectors.toList()) : Collections.emptyList(); - nodes.stream().skip(1).distinct().forEach(node -> mergeNodes(node, first, conf, existingSelfRelIds)); + nodesSet.stream().skip(1).forEach(node -> mergeNodes(node, first, conf, existingSelfRelIds)); return Stream.of(new NodeResult(first)); } @@ -300,8 +301,9 @@ public Stream mergeNodes(@Name("nodes") List nodes, @Name(valu @Description("apoc.refactor.mergeRelationships([rel1,rel2]) merge relationships onto first in list") public Stream mergeRelationships(@Name("rels") List relationships, @Name(value = "config", defaultValue = "{}") Map config) { if (relationships == null || relationships.isEmpty()) return Stream.empty(); + Set relationshipsSet = new LinkedHashSet<>(relationships); RefactorConfig conf = new RefactorConfig(config); - Iterator it = relationships.iterator(); + Iterator it = relationshipsSet.iterator(); Relationship first = it.next(); while (it.hasNext()) { Relationship other = it.next(); diff --git a/core/src/test/java/apoc/refactor/GraphRefactoringTest.java b/core/src/test/java/apoc/refactor/GraphRefactoringTest.java index 21714ada5b..cedfde3908 100644 --- a/core/src/test/java/apoc/refactor/GraphRefactoringTest.java +++ b/core/src/test/java/apoc/refactor/GraphRefactoringTest.java @@ -32,6 +32,7 @@ import static apoc.util.MapUtil.map; import static apoc.util.TestUtil.testCall; +import static apoc.util.TestUtil.testCallCount; import static apoc.util.TestUtil.testCallEmpty; import static apoc.util.TestUtil.testResult; import static apoc.util.Util.isSelfRel; @@ -523,7 +524,23 @@ public void testInvertRelationship() throws Exception { assertEquals(1L, rel.getProperty("a")); }); } - + + @Test + public void testRefactorWithSameEntities() { + Node node = db.executeTransactionally("CREATE (n:SingleNode) RETURN n", emptyMap(), + r -> Iterators.single(r.columnAs("n"))); + testCall(db, "MATCH (n:SingleNode) CALL apoc.refactor.mergeNodes([n,n]) yield node return node", + r -> assertEquals(node, r.get("node"))); + testCallCount(db, "MATCH (n:SingleNode) RETURN n", 1); + + Relationship rel = db.executeTransactionally("CREATE (n:Start)-[r:REL_TO_MERGE]->(:End) RETURN r", emptyMap(), + r -> Iterators.single(r.columnAs("r"))); + testCall(db, "MATCH (n:Start)-[r:REL_TO_MERGE]->(:End) CALL apoc.refactor.mergeRelationships([r,r]) yield rel return rel", r -> { + assertEquals(rel, r.get("rel")); + }); + testCallCount(db, "MATCH (n:Start)-[r:REL_TO_MERGE]->(:End) RETURN r", 1); + } + @Test public void testCollapseNode() throws Exception { Long id = db.executeTransactionally("CREATE (f:Foo)-[:FOO {a:1}]->(b:Bar {c:3})-[:BAR {b:2}]->(f) RETURN id(b) as id", emptyMap(), result -> Iterators.single(result.columnAs("id")));