Neo4j cypher - how to find all the nodes that are related to the list of nodes

I have nodes named "options". Users select these options. I need a chpher request that works like this:

Select the users who have selected all the options listed.

MATCH (option:Option)<-[:CHOSE]-(user:User) WHERE option.Key IN ['1','2','2'] Return user 

This query gives me users who have chosen option (1), option (2) and option (3), and also gives me a user who has only chosen option (2).

I only need users who have selected all of them - option (1), option (2) and option (3).

+6
source share
4 answers

For the whole cypher solution (you donโ€™t know if this is better than Chrisโ€™s answer, you have to test and compare), you can collect option.Key for each user and filter out those who do not have option.Key for each value in your list

 MATCH (u:User)-[:CHOSE]->(opt:Option) WITH u, collect(opt.Key) as optKeys WHERE ALL (v IN {values} WHERE v IN optKeys) RETURN u 

or compare all the parameters whose keys are in your list, and the users who selected them, collect these parameters for each user and compare the size of the collection of options with the size of your list (if you do not give duplicates in your list of users with a set of options of equal size selected all options)

 MATCH (u:User)-[:CHOSE]->(opt:Option) WHERE opt.Key IN {values} WITH u, collect(opt) as opts WHERE length(opts) = length({values}) // assuming {values} don't have duplicates RETURN u 

Or they should limit the results for users connected to all parameters whose key values โ€‹โ€‹are specified in {values}, and you can change the length of the collection parameter without changing the request.

+10
source

If the number of options is limited, you can do:

 MATCH (user:User)-[:Chose]->(option1:Option), (user)-[:Chose]->(option2:Option), (user)-[:Chose]->(option3:Option) WHERE option1.Key = '1' AND option2.Key = '2' AND option3.Key = '3' RETURN user.Id 

This will only return the user with all three parameters.

This is a bit muddy, since obviously you have 3 lines, where you have 1, but I don't know how to do what you want using the IN keyword.

If you code it, it's pretty simple to create a WHERE and MATCH WHERE , but still not perfect .:(

EDIT - Example

Turns out there are some string manipulations (!) Here, but you can always cache bits. Important is the use of Params , which would allow neo4j to cache requests and provide faster responses on every call.

 public static IEnumerable<User> GetUser(IGraphClient gc) { var query = GenerateCypher(gc, new[] {"1", "2", "3"}); return query.Return(user => user.As<User>()).Results; } public static ICypherFluentQuery GenerateCypher(IGraphClient gc, string[] options) { ICypherFluentQuery query = new CypherFluentQuery(gc); for(int i = 0; i < options.Length; i++) query = query.Match(string.Format("(user:User)-[:CHOSE]->(option{0}:Option)", i)); for (int i = 0; i < options.Length; i++) { string paramName = string.Format("option{0}param", i); string whereString = string.Format("option{0}.Key = {{{1}}}", i, paramName); query = i == 0 ? query.Where(whereString) : query.AndWhere(whereString); query = query.WithParam(paramName, options[i]); } return query; } 
+2
source
 MATCH (user:User)-[:CHOSE]->(option:Option) WHERE option.key IN ['1', '2', '3'] WITH user, COUNT(*) AS num_options_chosen WHERE num_options_chosen = LENGTH(['1', '2', '3']) RETURN user.name 

This will only lead to the return of users who have relations with all parameters with the given keys in the array. It is assumed that between users and parameters there are no multiple links [: CHOSE]. . If a user can have multiple relations [: CHOSE] with one parameter, you will have to add some conventions as needed.

I tested the above query with the following dataset:

 CREATE (User1:User {name:'User 1'}), (User2:User {name:'User 2'}), (User3:User {name:'User 3'}), (Option1:Option {key:'1'}), (Option2:Option {key:'2'}), (Option3:Option {key:'3'}), (Option4:Option {key:'4'}), (User1)-[:CHOSE]->(Option1), (User1)-[:CHOSE]->(Option4), (User2)-[:CHOSE]->(Option2), (User2)-[:CHOSE]->(Option3), (User3)-[:CHOSE]->(Option1), (User3)-[:CHOSE]->(Option2), (User3)-[:CHOSE]->(Option3), (User3)-[:CHOSE]->(Option4) 

And I get only "User 3" as the output.

+2
source

For shorter lists, you can use path predicates in your WHERE :

 MATCH (user:User) WHERE (user)-[:CHOSE]->(:Option { Key: '1' }) AND (user)-[:CHOSE]->(:Option { Key: '2' }) AND (user)-[:CHOSE]->(:Option { Key: '3' }) RETURN user 

Benefits:

  • Clear for reading
  • Easy to create dynamic length lists

Disadvantages:

  • For each different length, you will have a different query that needs to be parsed and cached by Cypher. Too many dynamic queries will keep track of how your cache hit passes through the floor, query compilation work increases, and query performance decreases.
+1
source

All Articles