0
votes

In my Neo4j application I have a composite Decision node. Each Decision can have an unlimited number and depth of descendant Decision nodes.

I have a Cypher query that returns descendant decision id's for a certain parent decision id:

MATCH (d:Decision)-[:CONTAINS*]->(descendantDecision) 
WHERE id(d) = {decisionId} 
RETURN id(descendantDecision) as id

I need to modify this function in order to return only non-shared descendant decision nodes id's.

For example I have a following hierarchy:

enter image description here

The new Cypher query for parent D3 {decisionId} must return following descendant decisions id's : D4, D6, D7, D8

Please note that shared node with id D7(shared with D4 and D8) must be returned because this node is only shared inside of D3 inner hierarchy but shared node with id D5 must not be returned because it is shared with some other node (D9) from an external hierarchy to D3.

So, the following nodes(marked with a green sign - D4, D6, D7, D8) must be returned:

enter image description here

Please help me to create this new Cypher query.

2

2 Answers

3
votes

The relevant criteria here is that each member of the result set must only have inbound relationships from nodes being descendents of the original node. So the statement looks like:

MATCH (d:Decision)-[:CONTAINS*0..]->(descendantDecision)  
WHERE d.name=3  
WITH collect(descendantDecision) AS allDescendants
UNWIND allDescendants AS descendant 
MATCH (descendant)<-[:CONTAINS]-(parent)  
WITH descendant, collect(parent) AS parents, allDescendants
WHERE ALL (x IN parents WHERE x IN allDescendants)  // check if all inbound start at a descendant
RETURN descendant

For completeness I've created a neo4j console setup for this: http://console.neo4j.org/?id=ufyu0u

3
votes

Here is an alternative variant to @sarmbruster's suggestion:

MATCH (d:Decision)-[:CONTAINS*0..]->(descendantDecision)
WHERE d.name=3
WITH collect(DISTINCT descendantDecision) AS allDescendants 
UNWIND allDescendants AS descendant
WITH allDescendants, descendant
WHERE ALL (p IN (descendant)<-[:CONTAINS]-() 
             WHERE last(nodes(p)) IN allDescendants)
RETURN descendant