From 837ccda3501d28153bfd4bef8f2ef465e5136fde Mon Sep 17 00:00:00 2001 From: Giuseppe Villani Date: Fri, 6 Dec 2024 15:45:49 +0100 Subject: [PATCH] [NOID] Fixes #4157: apoc.dv.queryAndLink how to specify direction (#4222) * Fixes #4157: apoc.dv.queryAndLink how to specify direction * test fixes * refactoring --- .../apoc.dv/apoc.dv.queryAndLink.adoc | 10 + .../ROOT/pages/virtual-resource/index.adoc | 10 + .../apoc/dv/DataVirtualizationCatalog.java | 25 +- .../dv/DataVirtualizationCatalogTest.java | 261 +++++++++--------- .../dv/DataVirtualizationCatalogTestUtil.java | 243 ++++++++++++++++ 5 files changed, 410 insertions(+), 139 deletions(-) create mode 100644 full/src/test/java/apoc/dv/DataVirtualizationCatalogTestUtil.java diff --git a/docs/asciidoc/modules/ROOT/pages/overview/apoc.dv/apoc.dv.queryAndLink.adoc b/docs/asciidoc/modules/ROOT/pages/overview/apoc.dv/apoc.dv.queryAndLink.adoc index 2fcd7ce073..28011167b8 100644 --- a/docs/asciidoc/modules/ROOT/pages/overview/apoc.dv/apoc.dv.queryAndLink.adoc +++ b/docs/asciidoc/modules/ROOT/pages/overview/apoc.dv/apoc.dv.queryAndLink.adoc @@ -35,3 +35,13 @@ apoc.dv.queryAndLink(node :: NODE?, relName :: STRING?, name :: STRING?, params |path|PATH? |=== +== Configuration parameters + +The procedures support the following config parameters: + +.Config parameters +[opts=header] +|=== +| name | type | default | description +| direction | String | "OUT" | The direction of the relationships, i.e. outgoing ("OUT") or incoming ("IN"). +|=== \ No newline at end of file diff --git a/docs/asciidoc/modules/ROOT/pages/virtual-resource/index.adoc b/docs/asciidoc/modules/ROOT/pages/virtual-resource/index.adoc index 666ab8bbcf..57db73eb2b 100644 --- a/docs/asciidoc/modules/ROOT/pages/virtual-resource/index.adoc +++ b/docs/asciidoc/modules/ROOT/pages/virtual-resource/index.adoc @@ -113,6 +113,16 @@ phrase over the previous query. image::apoc.dv.jdbc-queryAndLink.png[scaledwidth="100%"] +The default direction of the relationships is outgoing (i.e. `{direction: "OUT"}`), but it is possible to reverse it by the config parameters. +Example: + +[source,cypher] +---- +MATCH (hook:Hook) WITH hook +CALL apoc.dv.queryAndLink(hook, $relType, $name, $queryParams, { direction: "IN" }) yield path +RETURN path +---- + === Listing the Virtualized Resource Catalog The apoc.dv.catalog.list procedure returns a list with all the existing Virtualized resources and their descriptions. It takes no parameters. diff --git a/full/src/main/java/apoc/dv/DataVirtualizationCatalog.java b/full/src/main/java/apoc/dv/DataVirtualizationCatalog.java index 3a77a4f8f6..b881b8e818 100644 --- a/full/src/main/java/apoc/dv/DataVirtualizationCatalog.java +++ b/full/src/main/java/apoc/dv/DataVirtualizationCatalog.java @@ -25,6 +25,7 @@ import apoc.result.VirtualPath; import apoc.result.VirtualRelationship; import java.util.Map; +import java.util.Objects; import java.util.stream.Stream; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; @@ -103,10 +104,30 @@ public Stream queryAndLink( VirtualizedResource vr = new DataVirtualizationCatalogHandler(db, apocConfig.getSystemDb(), null).get(name); final RelationshipType relationshipType = RelationshipType.withName(relName); final Pair> procedureCallWithParams = vr.getProcedureCallWithParams(params, config); + + String direction = (String) config.getOrDefault("direction", Direction.OUT.name()); + return tx.execute(procedureCallWithParams.first(), procedureCallWithParams.other()).stream() .map(m -> (Node) m.get(("node"))) - .map(n -> new VirtualRelationship(node, n, relationshipType)) - .map(r -> new VirtualPath.Builder(r.getStartNode()).push(r).build()) + .map(n -> getVirtualRelationship(node, n, direction, relationshipType)) + .map(r -> { + VirtualPath virtualPath = new VirtualPath(r.getStartNode()); + virtualPath.addRel(r); + return virtualPath; + }) .map(PathResult::new); } + + private VirtualRelationship getVirtualRelationship( + Node node, Node n, String direction, RelationshipType relationshipType) { + if (Objects.equals(direction.toUpperCase(), Direction.OUT.name())) { + return new VirtualRelationship(node, n, relationshipType); + } + return new VirtualRelationship(n, node, relationshipType); + } + + enum Direction { + IN, + OUT; + } } diff --git a/full/src/test/java/apoc/dv/DataVirtualizationCatalogTest.java b/full/src/test/java/apoc/dv/DataVirtualizationCatalogTest.java index 3b4292d88a..c39ced1282 100644 --- a/full/src/test/java/apoc/dv/DataVirtualizationCatalogTest.java +++ b/full/src/test/java/apoc/dv/DataVirtualizationCatalogTest.java @@ -18,7 +18,7 @@ */ package apoc.dv; -import static apoc.util.TestUtil.getUrlFileName; +import static apoc.dv.DataVirtualizationCatalogTestUtil.*; import static apoc.util.TestUtil.testCall; import static apoc.util.TestUtil.testCallEmpty; import static apoc.util.TestUtil.testResult; @@ -32,7 +32,6 @@ import apoc.util.TestUtil; import java.util.List; import java.util.Map; -import java.util.function.Consumer; import java.util.stream.Collectors; import org.apache.commons.lang3.exception.ExceptionUtils; import org.junit.After; @@ -84,45 +83,7 @@ public static void tearDownContainer() { @Test public void testVirtualizeCSV() { - final String name = "csv_vr"; - final String url = getUrlFileName("test.csv").toString(); - final String desc = "person's details"; - final String query = "map.name = $name and map.age = $age"; - List labels = List.of("Person"); - Map map = Map.of("type", "CSV", "url", url, "query", query, "desc", desc, "labels", labels); - - final Consumer> assertCatalogContent = (row) -> { - assertEquals(name, row.get("name")); - assertEquals(url, row.get("url")); - assertEquals("CSV", row.get("type")); - assertEquals(List.of("Person"), row.get("labels")); - assertEquals(desc, row.get("desc")); - assertEquals(query, row.get("query")); - assertEquals(List.of("$name", "$age"), row.get("params")); - }; - - testCall(db, "CALL apoc.dv.catalog.add($name, $map)", Map.of("name", name, "map", map), assertCatalogContent); - - testCall(db, "CALL apoc.dv.catalog.list()", assertCatalogContent); - - String personName = "Rana"; - String personAge = "11"; - - Map queryParams = Map.of("name", personName, "age", personAge); - testCall( - db, - "CALL apoc.dv.query($name, $queryParams, $config)", - Map.of("name", name, "queryParams", queryParams, "config", Map.of("header", true)), - (row) -> { - Node node = (Node) row.get("node"); - assertEquals(personName, node.getProperty("name")); - assertEquals(personAge, node.getProperty("age")); - assertEquals(List.of(Label.label("Person")), node.getLabels()); - }); - - String hookNodeName = "node to test linking"; - - db.executeTransactionally("create (:Hook {name: $hookNodeName})", Map.of("hookNodeName", hookNodeName)); + CsvTestResult result = getCsvCommonResult(db); final String relType = "LINKED_TO"; testCall( @@ -130,16 +91,24 @@ public void testVirtualizeCSV() { "MATCH (hook:Hook) WITH hook " + "CALL apoc.dv.queryAndLink(hook, $relType, $name, $queryParams, $config) yield path " + "RETURN path ", - Map.of("name", name, "queryParams", queryParams, "relType", relType, "config", Map.of("header", true)), + Map.of( + "name", + result.name, + "queryParams", + result.queryParams, + "relType", + relType, + "config", + Map.of("header", true)), (row) -> { Path path = (Path) row.get("path"); Node node = path.endNode(); - assertEquals(personName, node.getProperty("name")); - assertEquals(personAge, node.getProperty("age")); + assertEquals(result.personName, node.getProperty("name")); + assertEquals(result.personAge, node.getProperty("age")); assertEquals(List.of(Label.label("Person")), node.getLabels()); Node hook = path.startNode(); - assertEquals(hookNodeName, hook.getProperty("name")); + assertEquals(result.hookNodeName, hook.getProperty("name")); assertEquals(List.of(Label.label("Hook")), hook.getLabels()); Relationship relationship = path.lastRelationship(); @@ -150,56 +119,45 @@ public void testVirtualizeCSV() { } @Test - public void testVirtualizeJDBC() { - String name = "jdbc_vr"; - String desc = "country details"; - List