Search code examples
neo4jcypheracl

In- and excluding nodes in a cypher query


Good morning,

I want to build a structure in Neo4J where I can handle my users and groups (kind of ACL). The idea is to have for each user and for each group a node with all the details. The groups shall become a graph where a root group will have sub-groups that can have also sub-groups without limit. The relation will be -[:IS_SUBGROUP_OF]- - so far nothing exciting. Every user will be related to a group with -[:IS_MEMBER_OF]- to have a clear assignment. Of course a user can be a member of 1 or more groups. Some users will have a different relation like -[:IS_LEADER_OF]- to identify teamlead of the groups.

My tasks:

  1. Assignment: I can query each member of a group with a simple query, I can even query members of the subgroups using the current logged in and asking user:

    MATCH (d1:Group:Local) -- (c:User) MATCH (d:User) -[:IS_MEMBER_OF|IS_LEADER_OF]- (g:Group:Local)-[:IS_SUBGROUP_OF*0..]->(d1) WHERE c.login = userLogin RETURN DISTINCT d.lastname, d.firstname

I get every related user to every group of the current user and below (subgroups). Maybe you have a hint how I cna improve the query or the model.

  1. Approval

Here I am stucked as I want to have all users of the current group from the querying user and all members of all subgroups - except the leader of the current group. The reason behind is that a teamlead shall not be able to approve actions for himself but though for every other member of his group and all members of subgroups including their teamleads.

I tried to use the relations -[:IS_LEADER_OF]- to exclude them but than I loose also the teamleads of the subgroups. Does anyone has an idea how I would either change the model or how I can query the graph to get all users except the teamlead of the current group?

Thanks for your time, Balael

* EDIT *

I think I am getting close, I just need to understand the results of those both queries:

MATCH (d:User) -- (g:Group) WHERE g.uuid = "xx" 
RETURN  d.lastname, d.firstname

Returns all user in this group no matter what relationship (leader / member)

MATCH (d:User) -- (g:Group), (g)--(c:User{uuid:"yy"})
RETURN  d.lastname, d.firstname

Returns all user of that group except the user c. I would have expected to get c as well in the list with d-users as c is part of that group and should be found with (d:User).

I do not understand the difference between both queries, maybe someone has a hint for me?


Solution

  • You can simplify your query slightly (however this should not have an impact on performance):

    MATCH (d:User) -[:IS_MEMBER_OF|IS_LEADER_OF]- (g:Group:Local)-[:IS_SUBGROUP_OF*0..]->(d1:Group:Local)--(c:User{login:"userlogin"})
    RETURN DISTINCT d.lastname, d.firstname
    

    Don't completely understand your question, but I assume you want to make sure that d1 and c are not connected by a IS_LEADER_OF relationship. If so, try:

    MATCH (d:User) -[:IS_MEMBER_OF|IS_LEADER_OF]- (g:Group:Local)-[:IS_SUBGROUP_OF*0..]->(d1:Group:Local)-[r]-(c:User{login:"userlogin"})
    WHERE type(r)<>'IS_LEADER_OF'
    RETURN DISTINCT d.lastname, d.firstname
    

    following up on * EDIT * in the question

    In a MATCH you specify a path. By definition a path does not use the same relationship twice. Otherwise there is a danger to run into infinite recursion. Looking at the second query in the "EDIT" section above: the right part matches yy's relationship to the group whereas the left part matches all user related to this group. To prevent multiple usage of the same relationship the left part does not hit use yy