Returning an entire hierarchy (tree) using neo4j/cypher
Asked Answered
W

3

10

I have a graph which has a hierarchy of categories in it (similar to product categories on a shopping site, e.g., Clothing --> Mens --> Shirts --> Short Sleeve --> ...). I have a few use cases where I need to retrieve the entire hierarchy as a tree (nested ruby and/or javascript objects in this case). The only solution I've been able to come up with is to use NODES() to retrieve each unique path and then transform it into nested objects in the client. The performance of this is obviously a problem.

MATCH (store:Store),
      (store)-[:hasCategory]->(category:Category),
      categories=(category)-[:narrower*0..]->(:Category)
WHERE store.name = {name}
RETURN store, NODES(categories) AS categories

This returns result rows that are basically paths like:

[store, [category1, narrower_category1, narrower_category2, ...]]

What's the proper way to handle this in cypher without numerous trips back to the server or a massive fetch of data like the above query?

Whatsoever answered 23/1, 2015 at 22:48 Comment(0)
W
4

What's the proper way to handle this in cypher without numerous trips back to the server or a massive fetch of data like the above query?

If you have a big hierarchy, you're going to have two basic options: get it a few levels of the hierarchy at a time (necessitating trips back to the server to get the next chunk) or you can fetch the whole thing like you're doing. I don't see a third option, so it's probably not possible to get a big hierarchy without either of those features.

What you're doing seems OK, but without further clarification your question seems impossible. What's wrong with what you're currently doing? How do you use the hierarchy and why do you need it all at once? E.g. if I was on amazon, they have a huge shopping hierarchy. Usually they'll only show me the top levels first (Menswear, Women's clothes, electronics). Then when I click on Electronics, they'll show me the next level ("E-book readers", "computer", etc). That's usually the way to go, IMHO - multiple trips to the database, one level of hierarchy at a time. This lends itself to a tree-view and AJAX calls on a web page. When the user expands the tree, you do an AJAX call back to the server, and populate the children.

Each call in the hierarchy would probably be:

MATCH (category:Category { id: "whatever user picked" })-[:narrower]->(children:Category)
RETURN children
ORDER BY children.name;

If this incremental approach (one level at a time) won't work for your use case, then you're back to fetching the whole thing, which inevitably will be "one massive fetch of data".

Warchaw answered 24/1, 2015 at 16:22 Comment(0)
M
2

I solved a similar problem with this query:

MATCH p=((s)-[*0..]->(x))
where id(s)=3
return x,id(x),id(startNode(last(relationships(p))))

Here s is the root node identified by its id. The result is a table with each node, its id and its parent's id. The root node has a parent id of null.

It shouldn't be hard to rebuild the hierarchy with this information.

Misfit answered 14/7, 2018 at 22:43 Comment(0)
C
0

You can use the toTree function from the apoc library

MATCH (store:Store),
      (store)-[:hasCategory]->(category:Category),
      categories=(category)-[:narrower*0..]->(:Category)
WHERE store.name = {name}
WITH COLLECT(categories) as paths
CALL apoc.convert.toTree(paths)
YIELD value
RETURN value

This will return you a json with nodes and relationships. You can also pass an additional config parameter to omit some properties, but it didn't work for me.

Collector answered 29/9, 2022 at 20:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.