Reputation: 25790
I have the following Nodes tree structure, for example:
(n1:Node)-[:DEPENDS_ON]->(n2:Node)
(n4:Node)-[:DEPENDS_ON]->(n2:Node)
(n5:Node)-[:DEPENDS_ON]->(n1:Node)
(n5:Node)-[:DEPENDS_ON]->(n4:Node)
I may have any such combinations where one Node depends on another.
How do I write the Cypher query to return all :Node lists ordered by their dependencies, so that the least dependent ones come first?
Additionally, is it possible to handle cyclical dependencies with a Cypher query and fail the query in such cases?
Upvotes: 1
Views: 120
Reputation: 1391
I'm assuming the count of dependencies is the number of distinct nodes that can be reached by -[:DEPENDS_ON]->
. So here's a suggestion:
// Raise an exception if a cycle is found
CALL apoc.util.validate(
EXISTS { (n:Node)-[:DEPENDS_ON]->+(n) },
'Cyclical dependencies exists',
[0]
)
// Find all leaves connected to each other
// The first two NOT EXISTS check for leaves
// The third NOT EXISTS removes duplicates
MATCH (a:Node)-[:DEPENDS_ON]-*(b:Node)
WHERE NOT EXISTS { (a)-[:DEPENDS_ON]->(:Node) }
AND NOT EXISTS { (b)-[:DEPENDS_ON]->(:Node) }
AND a <= b
AND NOT EXISTS {
MATCH (a)-[:DEPENDS_ON]-+(c:Node)
WHERE NOT EXISTS { (c)-[:DEPENDS_ON]->(:Node) }
AND c < a
}
WITH a, collect(DISTINCT b) AS connectedLeaves
// Find all nodes connected to those sets of leaves
// a is the group key, so each disconnected dependency graph is kept separate
UNWIND a + connectedLeaves AS leaf
MATCH (n:Node)-[:DEPENDS_ON]->*(leaf)
WITH a, collect(DISTINCT n) AS connected
// Collect into list ordered by number of dependencies
RETURN COLLECT {
WITH connected
UNWIND connected AS n
MATCH (n:Node)-[:DEPENDS_ON]->*(t:Node)
WITH n, count(DISTINCT t) AS numDependencies
RETURN n ORDER BY numDependencies
} AS connectedDependencies;
Results with your example data:
+----------------------------------------------------------------------+
| connectedDependencies |
+----------------------------------------------------------------------+
| [(:Node {id: 2}), (:Node {id: 4}), (:Node {id: 1}), (:Node {id: 5})] |
+----------------------------------------------------------------------+
Upvotes: 1