Newbie question: How to find users which are doing multiple specific hobbies

Hi,

I’m really new to NEO4J and currently testing some simple Cypher queries.
I want to model users and their hobbies, where one user can have multiple hobbies.
Users and their hobby are created as nodes with the respective labels :User and :Hobby.

I can easily find users that are doing the hobby “Sport” or “Music”:

MATCH (u:User)-[:HAS_HOBBY]->(h:Hobby)
WHERE h.name = "Sport" OR h.name = "Music"
RETURN u

Result: Max, Joe

But how can I find users that are doing both the hobbies “Sport” AND “Music”?
In this case the query should only return the user “Max”.

Many thanks for your help.

Hello @drjonniex and welcome to the Neo4j community :slight_smile:

Try:

MATCH (u:User)-[:HAS_HOBBY]->(h:Hobby)
WITH u, collect(DISTINCT h.name) AS hobbies
WHERE all(x IN hobbies WHERE x IN ["Sport", "Music"])
RETURN u

Regards,
Cobra

Many thank's for your quick and very helpful answer!

1 Like
match (a:User)-[:HAS_HOBBY]->(b:Hobby)
where b.name = "Sport" 
match (a)-[:HAS_HOBBY]->(c:Hobby)
where c.name = "Music"
return a

returns Max

I expanded the example graph from above and added the additional hobby "Dancing" for "Max":

The query suggestion from @Cobra seems not work in this case

MATCH (u:User)-[:HAS_HOBBY]->(h:Hobby)
WITH u, collect(DISTINCT h.name) AS hobbies
WHERE all(x IN hobbies WHERE x IN ["Sport", "Music"])
RETURN u

Result: Joe

As i can understand the query the result seems to be logical. The hobby Sport from user Joe
satisfies

x IN hobbies WHERE x IN ["Sport", "Music"]

and because the user Joe has only one hobby the all predicate is also fulfilled.

The query suggestion from @ameyasoft works and only the user Max is returned.
But the disadvantage is that you have to use a variable name for every hobby
in the query.

I am particularly thinking about how the query can be easily and dynamically constructed when the hobby filter comes from some sort of user interface. The user can select several hobbies via a checkbox.

Hi,

MATCH (u:User)-[:HAS_HOBBY]->(h:Hobby)
WITH u, collect(DISTINCT h.name) AS hobbies
WHERE all(x IN ["Sport", "Music"] WHERE x IN hobbies)
RETURN u

Many thanks.

I would like to use the query syntax from @Cobra to combine multiple filters at once.
For example, to further narrow down the user result set by character details.

It seems to be pretty easy with the query syntax:

MATCH (u:User)-[:HAS_HOBBY]->(h:Hobby)
WITH u, collect(DISTINCT h.name) AS hobbies
WHERE all(x IN ["Sport", "Music"] WHERE x IN hobbies)

WITH u
MATCH (u)-[:HAS_CHARACTER]->(c:CharacterDetail)
WITH u, collect(DISTINCT c.name) AS cdetails
WHERE all(x IN ["Calm", "Spontaneous"] WHERE x IN cdetails)

WITH U
*** NEXT FILTER ***

WITH U
*** NEXT FILTER ***

RETURN u

Are there any performance considerations by applying multiple filters at once
on databases with a large amount of user nodes?

As I understand it would be necessary to create an index on the name property for the
Hobby and CharacterDetail nodes.

1 Like

Normally, indexes are useful when they are directly specified in the WHERE clause, for example: n.id = 0. In your case, you already collected the name for each nodes so I don't think there will be any improvments but you can still try :slight_smile:

Documentation

You could also use subquery with UNION :slight_smile: