Skip to content

Commit

Permalink
Fixes #4157: apoc.dv.queryAndLink how to specify direction (#4222)
Browse files Browse the repository at this point in the history
* Fixes #4157: apoc.dv.queryAndLink how to specify direction

* test fixes

* refactoring
  • Loading branch information
vga91 authored Dec 6, 2024
1 parent 32192db commit 4e2f977
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 139 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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").
|===
10 changes: 10 additions & 0 deletions docs/asciidoc/modules/ROOT/pages/virtual-resource/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
239 changes: 101 additions & 138 deletions extended-it/src/test/java/apoc/dv/DataVirtualizationCatalogTest.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package apoc.dv;

import apoc.create.Create;
import apoc.dv.DataVirtualizationCatalog;
import apoc.load.Jdbc;
import apoc.load.LoadCsv;
import apoc.util.TestUtil;
Expand All @@ -20,18 +19,16 @@
import org.neo4j.test.rule.DbmsRule;
import org.neo4j.test.rule.ImpermanentDbmsRule;

import org.junit.jupiter.api.AfterAll;
import org.testcontainers.containers.JdbcDatabaseContainer;
import org.testcontainers.containers.MySQLContainer;

import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static apoc.ApocConfig.APOC_IMPORT_FILE_ENABLED;
import static apoc.ApocConfig.apocConfig;
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;
Expand Down Expand Up @@ -64,64 +61,22 @@ 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<String> labels = List.of("Person");
Map<String, Object> map = Map.of("type", "CSV",
"url", url, "query", query,
"desc", desc,
"labels", labels);

final Consumer<Map<String, Object>> 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<String, Object> 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(db, "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)),
"CALL apoc.dv.queryAndLink(hook, $relType, $name, $queryParams, $config) yield path " +
"RETURN path ",
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();
Expand All @@ -131,64 +86,53 @@ public void testVirtualizeCSV() {
});

}

@Test
public void testVirtualizeJDBC() {
String name = "jdbc_vr";
String desc = "country details";
List<Label> labels = List.of(Label.label("Country"));
List<String> labelsAsString = List.of("Country");
final String query = "SELECT * FROM country WHERE Name = ?";
final String url = mysql.getJdbcUrl() + "?useSSL=false";
Map<String, Object> map = Map.of("type", "JDBC",
"url", url, "query", query,
"desc", desc,
"labels", labelsAsString);

testCall(db, "CALL apoc.dv.catalog.add($name, $map)",
Map.of("name", name, "map", map),
public void testVirtualizeCSVWithCustomDirectionIN() {
CsvTestResult result = getCsvCommonResult(db);

final String relType = "LINKED_TO";
testCall(db, "MATCH (hook:Hook) WITH hook " +
"CALL apoc.dv.queryAndLink(hook, $relType, $name, $queryParams, $config) yield path " +
"RETURN path ",
Map.of("name", result.name(), "queryParams", result.queryParams(), "relType", relType, "config", Map.of("header", true, "direction", "IN")),
(row) -> {
assertEquals(name, row.get("name"));
assertEquals(url, row.get("url"));
assertEquals("JDBC", row.get("type"));
assertEquals(labelsAsString, row.get("labels"));
assertEquals(desc, row.get("desc"));
assertEquals(List.of("?"), row.get("params"));
});

testCallEmpty(db, "CALL apoc.dv.query($name, ['Italy'], $config)", Map.of("name", name,
"config", Map.of("credentials", Map.of("user", mysql.getUsername(), "password", mysql.getPassword()))));
Path path = (Path) row.get("path");
Node hook = path.endNode();
assertEquals(result.hookNodeName(), hook.getProperty("name"));
assertEquals(List.of(Label.label("Hook")), hook.getLabels());
Node node = path.startNode();

String country = "Netherlands";
List<String> queryParams = List.of(country);
assertEquals(result.personName(), node.getProperty("name"));
assertEquals(result.personAge(), node.getProperty("age"));
assertEquals(List.of(Label.label("Person")), node.getLabels());

testCall(db, "CALL apoc.dv.query($name, $queryParams, $config)",
Map.of("name", name, "queryParams", queryParams,
"config", Map.of("credentials", Map.of("user", mysql.getUsername(), "password", mysql.getPassword()))),
(row) -> {
Node node = (Node) row.get("node");
assertEquals(country, node.getProperty("Name"));
assertEquals(labels, node.getLabels());
Relationship relationship = path.lastRelationship();
assertEquals(node, relationship.getStartNode());
assertEquals(hook, relationship.getEndNode());
assertEquals(relType, relationship.getType().name());
});

String hookNodeName = "node to test linking";

db.executeTransactionally("create (:Hook {name: $hookNodeName})", Map.of("hookNodeName", hookNodeName));
}

@Test
public void testVirtualizeJDBC() {
VirtualizeJdbcResult result = getVirtualizeJdbcCommonResult(db, mysql);

final String relType = "LINKED_TO_NEW";
testCall(db, "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,
"CALL apoc.dv.queryAndLink(hook, $relType, $name, $queryParams, $config) yield path " +
"RETURN path ",
Map.of("name", result.name(), "queryParams", result.queryParams(), "relType", relType,
"config", Map.of("credentials", Map.of("user", mysql.getUsername(), "password", mysql.getPassword()))),
(row) -> {
Path path = (Path) row.get("path");
Node node = path.endNode();
assertEquals(country, node.getProperty("Name"));
assertEquals(labels, node.getLabels());
assertEquals(result.country(), node.getProperty("Name"));
assertEquals(result.labels(), 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();
Expand All @@ -199,64 +143,53 @@ public void testVirtualizeJDBC() {
}

@Test
public void testVirtualizeJDBCWithParameterMap() {
String name = "jdbc_vr";
String desc = "country details";
List<Label> labels = List.of(Label.label("Country"));
List<String> labelsAsString = List.of("Country");
final String query = "SELECT * FROM country WHERE Name = $name AND HeadOfState = $head_of_state AND Code2 = $CODE2";
final String url = mysql.getJdbcUrl() + "?useSSL=false";
Map<String, Object> map = Map.of("type", "JDBC",
"url", url, "query", query,
"desc", desc,
"labels", labelsAsString);
public void testVirtualizeJDBCWithCustomDirectionIN() {
VirtualizeJdbcResult result = getVirtualizeJdbcCommonResult(db, mysql);

testCall(db, "CALL apoc.dv.catalog.add($name, $map)",
Map.of("name", name, "map", map),
final String relType = "LINKED_TO_NEW";
testCall(db, "MATCH (hook:Hook) WITH hook " +
"CALL apoc.dv.queryAndLink(hook, $relType, $name, $queryParams, $config) yield path " +
"RETURN path ",
Map.of("name", result.name(), "queryParams", result.queryParams(), "relType", relType,
"config", Map.of(
"credentials", Map.of("user", mysql.getUsername(), "password", mysql.getPassword()),
"direction", "IN"
)),
(row) -> {
assertEquals(name, row.get("name"));
assertEquals(url, row.get("url"));
assertEquals("JDBC", row.get("type"));
assertEquals(labelsAsString, row.get("labels"));
assertEquals(desc , row.get("desc"));
assertEquals(List.of("$name", "$head_of_state", "$CODE2"), row.get("params"));
});

testCallEmpty(db, "CALL apoc.dv.query($name, {name: 'Italy', head_of_state: '', CODE2: ''}, $config)",
Map.of("name", name, "config", Map.of("credentials", Map.of("user", mysql.getUsername(), "password", mysql.getPassword()))));
Path path = (Path) row.get("path");
Node hook = path.endNode();
assertEquals(result.hookNodeName(), hook.getProperty("name"));
assertEquals(List.of(Label.label("Hook")), hook.getLabels());

String country = "Netherlands";
String code2 = "NL";
String headOfState = "Beatrix";
Map<String, Object> queryParams = Map.of("name", country, "CODE2", code2, "head_of_state", headOfState);
Node node = path.startNode();
assertEquals(result.country(), node.getProperty("Name"));
assertEquals(result.labels(), node.getLabels());

testCall(db, "CALL apoc.dv.query($name, $queryParams, $config)",
Map.of("name", name, "queryParams", queryParams,
"config", Map.of("credentials", Map.of("user", mysql.getUsername(), "password", mysql.getPassword()))),
(row) -> {
Node node = (Node) row.get("node");
assertEquals(country, node.getProperty("Name"));
assertEquals(labels, node.getLabels());
Relationship relationship = path.lastRelationship();
assertEquals(node, relationship.getStartNode());
assertEquals(hook, relationship.getEndNode());
assertEquals(relType, relationship.getType().name());
});
}

String hookNodeName = "node to test linking";

db.executeTransactionally("create (:Hook {name: $hookNodeName})", Map.of("hookNodeName", hookNodeName));
@Test
public void testVirtualizeJDBCWithParameterMap() {
VirtualizeJdbcWithParameterResult result = getVirtualizeJdbcWithParamsCommonResult(db, mysql);

final String relType = "LINKED_TO_NEW";
testCall(db, "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,
Map.of("name", result.name(), "queryParams", result.queryParams(), "relType", relType,
"config", Map.of("credentials", Map.of("user", mysql.getUsername(), "password", mysql.getPassword()))),
(row) -> {
Path path = (Path) row.get("path");
Node node = path.endNode();
assertEquals(country, node.getProperty("Name"));
assertEquals(labels, node.getLabels());
assertEquals(result.country(), node.getProperty("Name"));
assertEquals(result.labels(), 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();
Expand All @@ -265,6 +198,36 @@ public void testVirtualizeJDBCWithParameterMap() {
assertEquals(relType, relationship.getType().name());
});
}


@Test
public void testVirtualizeJDBCWithParameterMapAndDirectionIN() {
VirtualizeJdbcWithParameterResult result = getVirtualizeJdbcWithParamsCommonResult(db, mysql);

final String relType = "LINKED_TO_NEW";
testCall(db, "MATCH (hook:Hook) WITH hook " +
"CALL apoc.dv.queryAndLink(hook, $relType, $name, $queryParams, $config) yield path " +
"RETURN path ",
Map.of("name", result.name(), "queryParams", result.queryParams(), "relType", relType,
"config", Map.of("credentials", Map.of("user", mysql.getUsername(), "password", mysql.getPassword()),
"direction", "IN"
)),
(row) -> {
Path path = (Path) row.get("path");
Node hook = path.endNode();
assertEquals(result.hookNodeName(), hook.getProperty("name"));
assertEquals(List.of(Label.label("Hook")), hook.getLabels());

Node node = path.startNode();
assertEquals(result.country(), node.getProperty("Name"));
assertEquals(result.labels(), node.getLabels());

Relationship relationship = path.lastRelationship();
assertEquals(node, relationship.getStartNode());
assertEquals(hook, relationship.getEndNode());
assertEquals(relType, relationship.getType().name());
});
}

@Test
public void testRemove() {
Expand All @@ -280,7 +243,7 @@ public void testRemove() {

db.executeTransactionally("CALL apoc.dv.catalog.add($name, $map)",
Map.of("name", name, "map", map));

testCallEmpty(db, "CALL apoc.dv.catalog.remove($name)", Map.of("name", name));
}

Expand Down
Loading

0 comments on commit 4e2f977

Please sign in to comment.