Skip to content

Commit

Permalink
[NOID] Return a node even if no relationships exist (#4168)
Browse files Browse the repository at this point in the history
  • Loading branch information
gem-neo4j authored Aug 12, 2024
1 parent 8a5d4f8 commit 8d4fa33
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 2 deletions.
11 changes: 10 additions & 1 deletion core/src/main/java/apoc/convert/Json.java
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,6 @@ public Stream<MapResult> toTree(
@Procedure("apoc.paths.toJsonTree")
@Description(
"apoc.paths.toJsonTree([paths],[lowerCaseRels=true], [config]) creates a stream of nested documents representing the graph as a tree by traversing outgoing relationships")
// todo optinally provide root node
public Stream<MapResult> pathsToTree(
@Name("paths") List<Path> paths,
@Name(value = "lowerCaseRels", defaultValue = "true") boolean lowerCaseRels,
Expand All @@ -254,6 +253,16 @@ public Stream<MapResult> pathsToTree(
allPaths.forEach(path -> {
// This api will always return relationships in an outgoing fashion ()-[r]->()
var pathRelationships = path.relationships();
// If no relationships exist in the path, then add the node by itself
if (!pathRelationships.iterator().hasNext()) {
Node currentNode = path.startNode();
Long currentNodeId = currentNode.getId();

if (!visitedInOtherPaths.contains(currentNodeId)) {
nodesToKeepInResult.add(currentNodeId);
}
tree.computeIfAbsent(currentNode.getId(), (id) -> toMap(currentNode, nodes));
}
pathRelationships.iterator().forEachRemaining((currentRel) -> {
Node currentNode = currentRel.getStartNode();
Long currentNodeId = currentNode.getId();
Expand Down
64 changes: 63 additions & 1 deletion core/src/test/java/apoc/convert/PathsToJsonTreeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public void clear() {
}

@Test
public void testToTreeSimplePath() throws Exception {
public void testToTreeSimplePath() {
/* r:R
a:A --------> b:B
*/
Expand Down Expand Up @@ -90,6 +90,68 @@ public void testToTreeSimplePath() throws Exception {
}
}

@Test
public void testSingleNode() {
// a:A
db.executeTransactionally("CREATE (a: A {nodeName: 'a'})");

var query = "MATCH path = (n)\n"
+ "WITH COLLECT(path) AS paths\n"
+ "CALL apoc.paths.toJsonTree(paths, true, {sortPaths: false}) YIELD value AS tree\n"
+ "RETURN tree";

try (Transaction tx = db.beginTx()) {
Result result = tx.execute(query);
var rows = result.stream().collect(Collectors.toList());
var expectedRow = "{" + " \"tree\":{"
+ " \"nodeName\":\"a\","
+ " \"_type\":\"A\","
+ " \"_id\":0"
+ " }"
+ "}";
assertEquals(rows.size(), 1);
assertEquals(parseJson(expectedRow), rows.get(0));
}
}

@Test
public void testSingleDisjointNodes() {
// a:A
db.executeTransactionally("CREATE (a: A {nodeName: 'a'}), (b: B {nodeName: 'b'}), (c: C {nodeName: 'c'})");

var query = "MATCH path = (n)\n"
+ "WITH COLLECT(path) AS paths\n"
+ "CALL apoc.paths.toJsonTree(paths, true, {sortPaths: false}) YIELD value AS tree\n"
+ "RETURN tree";

try (Transaction tx = db.beginTx()) {
Result result = tx.execute(query);
var rows = result.stream().collect(Collectors.toList());
var expectedRowA = "{" + " \"tree\":{"
+ " \"nodeName\":\"a\","
+ " \"_type\":\"A\","
+ " \"_id\":0"
+ " }"
+ "}";
var expectedRowB = "{" + " \"tree\":{"
+ " \"nodeName\":\"b\","
+ " \"_type\":\"B\","
+ " \"_id\":1"
+ " }"
+ "}";
var expectedRowC = "{" + " \"tree\":{"
+ " \"nodeName\":\"c\","
+ " \"_type\":\"C\","
+ " \"_id\":2"
+ " }"
+ "}";
assertEquals(rows.size(), 3);
assertEquals(parseJson(expectedRowA), rows.get(0));
assertEquals(parseJson(expectedRowB), rows.get(1));
assertEquals(parseJson(expectedRowC), rows.get(2));
}
}

@Test
public void testToTreeSimpleReversePath() {
/* r:R
Expand Down

0 comments on commit 8d4fa33

Please sign in to comment.