I have some nodes saved in neo4j and want to return them as tree. My model looks something like this:
RootNode -HAS_CHILDREN-> Element
Element -HAS_CHILDREN-> Element
Element -HAS_ANOTHER_RELATIONSHIP-> AnotherNode
The query that I run:
const rootNodeAlias = 'r';
const elementAlias = 'e';
const anotherNodeAlias = 'a';
const query "=
MATCH (${rootNodeAlias}:RootNode {id: $id})
// Match the related elements
OPTIONAL MATCH (${rootNodeAlias})-[:HAS_CHILDREN]->(${elementAlias}:Element)
// Fetch the element tree
CALL {
WITH ${elementAlias}
OPTIONAL MATCH path=(${elementAlias})-[rel:HAS_CHILDREN*0..]->(child:Element)-[:HAS_ANOTHER_RELATIONSHIP]->(${anotherNodeAlias}:AnotherNode)
WITH COLLECT(path) AS paths
CALL apoc.convert.toTree(paths) YIELD value AS tree
RETURN tree AS elementTree
}
// Return the root node and its related elements with their children
RETURN ${rootNodeAlias} AS root,
collect(elementTree) AS elementTrees
;"
The problem is that in case an element does not have the HAS_ANOTHER_RELATIONSHIP
with AnotherNode
, then the path here:
OPTIONAL MATCH path=(${elementAlias})-[rel:HAS_CHILDREN*0..]->(child:Element)-[:HAS_ANOTHER_RELATIONSHIP]->(${anotherNodeAlias}:AnotherNode)
will not return the HAS_CHILDREN
relationship either. I tried something like this with optional match:
OPTIONAL MATCH path=(${elementAlias})-[rel:HAS_CHILDREN*0..]->(child:Element) OPTIONAL MATCH (child)-[:HAS_ANOTHER_RELATIONSHIP]->(${anotherNodeAlias}:AnotherNode)
but it didn't work. It returned only the HAS_CHILDREN
relationships.
Is there any way to add inside the path the two relationships, if one of them exist? If this can't be done inside the path, what is the workaround?
Thank you in advance!
If you're on Neo4j > 5.9, you can use quantified path patterns to write:
MATCH (r:RootNode)
OPTIONAL MATCH path = (r)-[:HAS_CHILDREN]->+(:Element)
(()-[:HAS_ANOTHER_RELATIONSHIP]->(:AnotherNode)){0,1}
WITH r, COLLECT(path) AS paths
CALL apoc.convert.toTree(paths) YIELD value AS tree
RETURN r AS root, collect(tree) AS elementTrees
The {0,1}
quantifier will include the final hop over HAS_ANOTHER_RELATIONSHIP
if it exists, without having to use a second OPTIONAL
.
The following will work for version < 5.9, as long as there are no nodes with both labels Element
and AnotherNode
:
MATCH (r:RootNode)
OPTIONAL MATCH path = (r)-[:HAS_CHILDREN*]->(:Element)-[:HAS_ANOTHER_RELATIONSHIP*0..1]->
(:Element|AnotherNode)
WITH r, COLLECT(path) AS paths
CALL apoc.convert.toTree(paths) YIELD value AS tree
RETURN r AS root, collect(tree) AS elementTrees