cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

Breadcrumb Query

Hi there, I am trying to create a filetree-like query where I need to get the user's Breadcrumb up the tree. Since we want to implement folder/file sharing, we have to cut the breadcrumb off the moment we have a "broken chain" in the permissions. The data looks like this:

RED -> root folder
GREEN -> folder
BLUE -> user

If we query the bottom folder (green) then we expect the two folders above that to also be in the breadcrumb because the user (blue) :HAS_ACCESS to the folders. But since there is a "broken link" on the first folder, we don't expect to see the Root (red) and the first folder in the Breadcrumb. My query currently looks like this:

MATCH (u:User {ID: 1})-[:HAS_ACCESS]->(folder:Doc:Folder {ID: 2})

OPTIONAL MATCH path = (folder)<-[:PARENT_OF*]-(docs:Doc)
WHERE exists((u)-[:HAS_ACCESS]->(docs))

RETURN path

This returns the full tree:

Not what I'm looking for. Changing the WHERE clause to:

WHERE (u)-[:HAS_ACCESS]->(docs)

Gives an error: Coercion of list to boolean is deprecated. Please consider using NOT isEmpty(...) instead.

That being said, neither of these options feels like the correct way to go, they were both sort of just a shot in the dark. Are there any good ways to terminate a path at the point where some relationship does not exist?

Bonus question: how does one return just the longest path. Eg. In the case where we have

(f1)-[:PARENT_OF]->(f2)-[:PARENT_OF]->(f3)

I want the path: f1->f2->f3 and not also include f1->f2 as a separate path.

I know the example above is a bit contrived, so take it with a pinch of salt. Thanks in advance!

1 ACCEPTED SOLUTION

To answer your second question first, you could do something like the following to get the longest path:

match p = (:Doc)-[:PARENT_OF*]->(:Doc)  //whatever your match criteria is
with p, length(p) as Len
order by Len desc
limit 1
return p

Your approach is on target. The mistake you made was in your where clause. You are only testing whether the 'top-level' parent has a 'HAS_ACCESS' relationship to the user. It is not testing whether every document node in the path has the relationship to the user.

You could try the following. Note the variable path has a minimum length of '0', allowing it to match if the folder does not have any parents. You need to explicitly start at '0', as length '*' has a minimum of 1.

MATCH (u:User {ID: 1})
MATCH (folder:Doc:Folder {ID: 2})
MATCH p = (folder)<-[:PARENT_OF*0..]-(docs:Doc)
WHERE all(i in nodes(p) where exists ((u)-[:HAS_ACCESS]->(i)))
WITH p, length(p) as len
ORDER BY len DESC
LIMIT 1
RETURN p

Note, the warning is due to the fact that the ability to use a path as a predicate in a 'where' clause is deprecated. This is what it is referring to as 'coercion of a list to boolean' is deprecated. The path in the where clause is being converted to a boolean value. The new syntax is to use 'exists( (n)-->(m) )' instead, where the 'exists' method returns a boolean based on the existence or absence of the path.

View solution in original post

2 REPLIES 2

To answer your second question first, you could do something like the following to get the longest path:

match p = (:Doc)-[:PARENT_OF*]->(:Doc)  //whatever your match criteria is
with p, length(p) as Len
order by Len desc
limit 1
return p

Your approach is on target. The mistake you made was in your where clause. You are only testing whether the 'top-level' parent has a 'HAS_ACCESS' relationship to the user. It is not testing whether every document node in the path has the relationship to the user.

You could try the following. Note the variable path has a minimum length of '0', allowing it to match if the folder does not have any parents. You need to explicitly start at '0', as length '*' has a minimum of 1.

MATCH (u:User {ID: 1})
MATCH (folder:Doc:Folder {ID: 2})
MATCH p = (folder)<-[:PARENT_OF*0..]-(docs:Doc)
WHERE all(i in nodes(p) where exists ((u)-[:HAS_ACCESS]->(i)))
WITH p, length(p) as len
ORDER BY len DESC
LIMIT 1
RETURN p

Note, the warning is due to the fact that the ability to use a path as a predicate in a 'where' clause is deprecated. This is what it is referring to as 'coercion of a list to boolean' is deprecated. The path in the where clause is being converted to a boolean value. The new syntax is to use 'exists( (n)-->(m) )' instead, where the 'exists' method returns a boolean based on the existence or absence of the path.

That's exactly what I needed, thanks so much!