Mahesha999
Mahesha999

Reputation: 24941

Getting first root of a leaf in neo4j

I have follwing simple graph:

CREATE (:leaf)<-[:rel]-(:nonleaf)<-[:rel]-(:nonleaf)<-[:rel]-(:nonleaf)<-[:rel]-(r1:nonleaf{root:true})<-[:rel]-(r2:nonleaf{root:true})

I want to get the first ancestor starting from (:leaf) with root:true set on it. That is I want to get r1. For this I wrote following cypher:

MATCH (:leaf)<-[*]-(m)<-[*]-(r:nonleaf{root:true}) WHERE m.root<>true OR NOT exists(m.root) 
RETURN r

But it returned both (r1) and (r2). Same happened for following cypher:

MATCH shortestPath((l:leaf)<-[*]-(r:nonleaf{root:true}))
RETURN r

Whats going on here?

Update
Ok after thinking more, it clicked to my mind that (r2) is also returned because on path from (:leaf) to (r2), there are nodes with no root property set on them (this should have clicked to me earlier, pretty much obvious, subtle interpretation mistake). In other words, it returns (:nonleaf{root:true}) if "for at least one m" following condition is true: m.root<>true OR NOT exists(m.root). The requirement here is that the condition should be valid for "all ms" on the path, "not at least one m". Now it remains to figure out how to put this in cypher and my grip on cypher isnt that tight...

Upvotes: 2

Views: 363

Answers (2)

Dave Bennett
Dave Bennett

Reputation: 11216

You just need to adjust your where condition a little so that it says "and the :nonleaf node before the root :nonleaf node matched is not itself marked as a root node". I think this will satisfy your needs.

MATCH (l:leaf)<-[*]-(r:nonleaf {root: true})
WHERE NOT (:nonleaf {root: true})<--(r)
RETURN r

UPDATED

Reading the updated example in the comments, I thought of another way to solve your problem using the APOC procedure apoc.path.expandConfig.

It does require a slight change to your data. Each root: true node should have a :root label set on it. Here is an update statement...

MATCH (n:nonleaf {root:true})
SET n:root
RETURN n

And here is the updated query

MATCH (leaf:leaf {name: 'leaf'})
WITH leaf
CALL apoc.path.expandConfig(leaf, {relationshipFilter:'<rel',labelFilter:'/root'} ) yield path
RETURN last(nodes(path))

Upvotes: 1

Leon
Leon

Reputation: 32554

You can enforce that there is a single root node on the matched path with the help of the single() predicate function:

MATCH p=(:leaf)<-[*]-(r:nonleaf{root:true})
WHERE SINGLE(m IN nodes(p) WHERE exists(m.root) AND m.root=true )
RETURN r

Upvotes: 1

Related Questions