Join the community at Nodes 2022, our free virtual event on November 16 - 17.
β01-13-2020 09:31 PM
I'm trying to use the all()
Predicate Function in my query, but it's not working as expected, so I decided to experiment a little with the Movies graph.
Given that I want to find all paths where actors acted in movies that were released after 2000, I tried using the all()
function, like so -
MATCH path=(p:Person)-[:ACTED_IN]->(movie:Movie)
WHERE all(n in nodes(path) WHERE (n:Movie) AND n.released > 2000)
RETURN path
LIMIT 20
This is similar to the example given in the documentation, that says -
MATCH p =(a)-[*1..3]->(b)
WHERE a.name = 'Alice' AND b.name = 'Daniel' AND ALL (x IN nodes(p) WHERE x.age > 30)
RETURN p
All nodes in the returned paths will have an
age
property of at least '30' .
However, my query doesn't return anything.
If I change the query to see all nodes
in the path -
MATCH path=(p:Person)-[:ACTED_IN]->(movie:Movie)
RETURN nodes(path)
LIMIT 20
Then there are several nodes in the path where the movie was released after 2000 -
Is there something I'm missing about how all()
works?
β01-13-2020 09:52 PM
HI manish ,
MATCH path=(p:Person)-[:ACTED_IN]->(movie:Movie)
WHERE all(n in nodes(path) WHERE (n:Movie) AND n.released > 2000)
RETURN path
LIMIT 20
in this query , when you do ALL() , it means all the nodes in that path must qualify the filter you put inside that i.e n: Movie AND n.released > 2000
But , here, when you do nodes(path) , it includes the p:Person node too , so the person node cannot qualify the n:Movie filter .. since one node failed the condintion, ALL () will also fail .. thats why it wont return any output in anycase.
Instead, you can do the following ,
MATCH path=(p:Person)-[:ACTED_IN]->(movie:Movie)
WHERE movie.released > 2000
WITH p , movie LIMIT 20
RETURN p , COLLECT(movie) as movies
β01-13-2020 10:23 PM
Hello, Thank You for responding!
I have a question about this -
I was under the impression that my query would only consider nodes of type Movie
in nodes(path)
, because I specifically added in this clause - WHERE (n:Movie)
,
before adding AND n.released 2000
.
I thought this would select only the Movie
nodes from the path, and apply the released > 2000
filter on those Movie
nodes, because obviously the released
property exists only on Movie
nodes.
β01-13-2020 10:40 PM
nodes(path) -> this function will return all nodes the in the path ,
ex : nodes(path) = [ personNode , movieNode]
ALL (x IN nodes(path) WHERE (condition))
so , you are iterating through [ personNode , movienode,movie node] and applying filter ,
the filter fails with person node and so the ALL function .
When you do n:Movie ,you are asking to filter nodes(note that you are not selecting) which have label Movie, this means your filter returns true for all movie nodes but false for person node.
but ALL() wants all the rows passed to satisfy the where condition (ie.return true)
WHERE is for filtering the selected rows ..MATCH does the actual select part
β01-14-2020 11:28 AM
Consider the results in the image you posted. Each row here contains a list of the nodes in the path (or at least it displays their properties). Because of your pattern (p:Person)-[:ACTED_IN]->(movie:Movie)
, each path will consist of two nodes: a :Person node and a :Movie node that the person acted in.
Since you're using all(n in nodes(path) ...
, that means, per path, every one of those nodes needs to adhere to your WHERE clause: every node needs to be a :Movie node (which is impossible because of your pattern), and each node needs a released
property > 2000.
If you had used any()
instead of all()
, then the predicate would be satisfied by finding, in the path, at least one node satisfying the conditions, and you would have gotten results back.
β01-14-2020 03:49 PM
Thank You! I guess the comparison between all()
and any()
makes it clear. For some reason, I thought using WHERE
, like (WHERE (n:Movie)
), inside all()
would first filter out all Movie
nodes, and then apply the all()
predicate for all such nodes.
β01-14-2020 03:53 PM
I can see how that could seem confusing. You would need to filter the list used separately.
For example, if we wanted to filter the nodes in the path to only be :Movie nodes and then filter by the released property you could do this:
MATCH path=(p:Person)-[:ACTED_IN]->(movie:Movie)
WHERE all(n in [node in nodes(path) WHERE node:Movie] WHERE (n:Movie) AND n.released > 2000)
RETURN path
LIMIT 20
So rather than just use nodes(path)
, we need to filter this first, in this case using a list comprehension to filter the list such that every node in nodes(path) meets the criteria of being a :Movie node. Now that the list is filtered to only be :Movie nodes (these should be single-element lists now), the all()
list predicate may evaluate to true for some of the paths matched.
β01-14-2020 04:09 PM
Thank You so much! Your explanation makes it a lot more clearer.
Just so I know I'm getting it right -
this is done using this list comprehension - [node in nodes(path) WHERE node:Movie]
If this works, then, from what I think, the resulting list should contain only Movie
nodes? If that is the case, then would it be correct to remove the redundant WHERE (n:Movie)
in the any()
function, so that it becomes -
WHERE all(n in [node in nodes(path) WHERE node:Movie] WHERE n.released > 2000)
I did run this query, and it seems to return something, so I'm guessing this must be correct -
β01-14-2020 05:23 PM
Yes, just like that!
Of course the usage of all()
makes more sense in a longer pattern, one with multiple :Movies nodes, or a pattern where you may not know which node types are involved or how many, but you want to make sure that all :Movies nodes in the path were released after 2000.