Breadcrumb string of parent/child node relationships


(George Luft) #1

Hi, there. I've been struggling with this for a bit, so I thought I'd ask here.

How can I get a breadcrumb-like string of the parent/child relationships of nodes in a nested structure?

I basically have a hierarchical group structure that I'm trying to represent, almost like a directory path. It doesn't go very many levels deep (just 3-4), and it doesn't loop or otherwise go infinite.

I'm trying to create strings (as node properties) like this:

'/Root/group/subgroup1/subgroup2' for Group node subgroup2
'/Root/group/subgroup1' for Group node subgroup1
'/Root/group' for Group node group
and just 'Root' for any of the root Group nodes

I can get pretty close with the Cypher below for a single level, but I can't figure out if/how I need to do this recursively or otherwise.

MATCH (p:Group)-[:CONTAINS]->(m:Group) RETURN "/" + p.name + "/" + m.name as trail

which returns strings like this for each node that is contained by another.
"/Group/subgroup"

I've played around with using APOC to expand and UNWIND nodes in a subgraph, too. I need to figure out how to chase back up to the root, and only run down to the current node (and not beyond).

Any suggestions...?


(Andrew Bowman) #2

So for this one, apoc.text.join() will be a big help.

The idea is, given the name of a group (we're assuming group names are unique), we'll use a variable-length relationship to define a pattern that goes up to a root :Group node (we ensure it's a root node by making a restriction that there is no :CONTAINS relationship to it).

Once we have that path, from root to the node in question, we take the nodes of that path and extract the name for each node, then we'll use the join function to place a slash between each name:

MATCH path = (root:Group)-[:CONTAINS*]->(m:Group) 
WHERE m.name = $name AND NOT ()-[:CONTAINS]->(root)
WITH [node in nodes(path) | node.name] as trailNames
RETURN "/" + apoc.text.join(trailNames, "/") as trail

(George Luft) #3

Thanks, Andrew. That did the trick.

I made a slight modification since I just need the whole list and not a parameterized $name value.

MATCH path = (root:Group)-[:CONTAINS*]->(m:Group) 
WHERE  NOT ()-[:CONTAINS]->(root)
WITH [node in nodes(path) | node.name] as trailNames
RETURN "/" + apoc.text.join(trailNames, "/") as trail

Actually, I'm doing this as part of an import and need to set each group to have a property. But I have enough to work with here.