DannyMeister
DannyMeister

Reputation: 1303

Is this the optimal cypher for returning every node of a subtree?

I have a graph of a tree structure (well no, more of a DAG because i can have multiple parents) and need to be able to write queries that return all results in a flat list, starting at a particular node(s) and down.

I've reduced one of my use cases to this simple example. In the ascii representation here, n's are my nodes and I've appended their id. p is a permission in my auth system, but all that is pertinent to the question is that it marks the spot from which I need to recurse downwards to collect nodes which should be returned by the query.

Graph:

   n1
  ^ ^
 /   \
n2    n3<--p
     ^ ^
    /   \
   n4    n5
  ^
 /
n6

If it's helpful, here's the cypher I ran to throw together this quick example:

CREATE path=(n1:n{id:1})<-[:HAS_PARENT]-(n2:n{id:2}), 
      (n1)<-[:HAS_PARENT]-(n3:n{id:3})<-[:HAS_PARENT]-(n4:n{id:4}),
      (n3)<-[:HAS_PARENT]-(n5:n{id:5}),
      (n4)<-[:HAS_PARENT]-(n6:n{id:6})
MATCH (n{id:3})
CREATE (:p)-[:IN]->(n)

Here is the current best query I have:

MATCH (n:n)<--(:p)
WITH collect (n) as parents, (n) as n
OPTIONAL MATCH (c)-[:HAS_PARENT*]->(n)
WITH collect(c) as children, (parents) as parents
UNWIND (parents+children) as tree
RETURN tree

This returns the correct set of results, and unlike some previous attempts I made which did not use any collect/unwind, the results come back as a single column of data as desired.

Is this the most optimal way of making this type of query? It is surprisingly more complex than I thought the simple scenario called for. I tried some queries where I combined the roots ("parents" in my query) with the "children" using a UNION clause, but I could not find a way to do so without repeating the query for the relationship with p. In my real world queries, that's a much more expensive operation which i've reduced down here for the example, so I cannot run it more than once.

Upvotes: 1

Views: 421

Answers (1)

cybersam
cybersam

Reputation: 66999

This might suit your needs:

MATCH (c)-[:HAS_PARENT*0..]->(root:n)<--(:p)
RETURN root, COLLECT(c) AS tree

Each result row will contain a distinct root node and a collection if its tree nodes (including the root node).

Upvotes: 1

Related Questions