# How to get tree structure that has access rights on each node

**URL:** <https://community.neo4j.com/t/how-to-get-tree-structure-that-has-access-rights-on-each-node/50064>\
**Category:** Cypher\
**Created:** [January 6, 2022, 1:51pm UTC](https://community.neo4j.com/t/how-to-get-tree-structure-that-has-access-rights-on-each-node/50064 "2022-01-06T13:51:04Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![bobsabates](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/bobsabates/32/21703_2.png) [@bobsabates](https://community.neo4j.com/u/bobsabates)\
**Post date:** [January 6, 2022, 1:51pm UTC](https://community.neo4j.com/t/how-to-get-tree-structure-that-has-access-rights-on-each-node/50064/1 "2022-01-06T13:51:04Z")

</div>

I have a tree structure like a folder structure so with a project with nested project without a depth limit, each node has access rights on them.

Here is my graph:

 ![Screenshot 2022-01-06 at 14.11.45](https://us1.discourse-cdn.com/flex021/uploads/neo4jcommunity/original/3X/7/3/73ad50719d2754b8f0c50b788cfacf5a3a18b1f7.png)

Here is my query:

```auto
MATCH (a:Account {name: "bob"})-[r:VIEWER | EDITOR]->(c:Project)

MATCH (c)<-[:IS_PARENT*]-(p)
WHERE (p)<-[:VIEWER | EDITOR]-(a)

WITH TYPE(r) as relation, p, collect(distinct c) AS children

RETURN {name: p.name, Children: [c in children | {name: c.name, access:relation}]}

```

Here is my result:

```auto
{
  "name": "project test",
  "Children": [
    {
      "access": "VIEWER",
      "name": "cohort"
    },
    {
      "access": "VIEWER",
      "name": "experience"
    }
  ]
}
{
  "name": "project test",
  "Children": [
    {
      "access": "EDITOR",
      "name": "protocol"
    },
    {
      "access": "EDITOR",
      "name": "nested child"
    }
  ]
}
{
  "name": "cohort",
  "Children": [
    {
      "access": "EDITOR",
      "name": "nested child"
    }
  ]
}

```

And this is what I want to get:

```auto
        {
          name: "Project 1",
          access: "VIEWER",
          children: [
            {
              name: "cohort",
              access: "VIEWER",
              children: [
                {
                  name: "nested",
                  access: "EDITOR",
                },
              ]
            },
            {
              name: "protocol",
              access: "EDITOR",
            },
            {
              name: "expererience",
              access: "VIEWER",
            }
          ]
        }

```

My problem is that the result is split in two results, and `nested child` isn't nested in `cohort` .

An other thing that is tricky is that I don't want to get a node if I don't have a relation with it.

For example here I removed the relation between `bob` and `cohort` :

 ![Screenshot 2022-01-06 at 14.10.40](https://us1.discourse-cdn.com/flex021/uploads/neo4jcommunity/original/3X/d/1/d1ce7e6235641ac11b9e6c358f7a0aa25d82e294.png)

So I must not get `cohort` in my result, like this:

```auto
 {
          name: "Project 1",
          access: "VIEWER",
          children: [
            {
              name: "nested child",
              access: "EDITOR",
            },
            {
              name: "protocol",
              access: "EDITOR",
            },
            {
              name: "expererience",
              access: "VIEWER",
            }
          ]
        }

```

Here is my data if you want to try:

```auto
MERGE (project:Project:RootProject {name: "project test"})
MERGE (child1:Project {name: "cohort"})
MERGE (child2:Project {name: "protocol"})
MERGE (child3:Project {name: "experience"})
MERGE (child4:Project {name: "nested child"})

MERGE (project)-[:IS_PARENT]->(child1)
MERGE (project)-[:IS_PARENT]->(child2)
MERGE (project)-[:IS_PARENT]->(child3)
MERGE (child1)-[:IS_PARENT]->(child4)

MERGE (bob:Account {name: "bob"})
 MERGE (bob)-[:EDITOR]->(child4)
 MERGE (bob)-[:EDITOR]->(child2)
 MERGE (bob)-[:VIEWER]->(child3)
MERGE (bob)-[:VIEWER]->(child1)
 MERGE (bob)-[:VIEWER]->(project)

```

I have tried a lot of things but I never get a good result.

```auto

```

---

<div class="post-metadata">

**Author:** ![filantrop](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/filantrop/32/20927_2.png) [@filantrop](https://community.neo4j.com/u/filantrop)\
**Post date:** [January 7, 2022, 8:15am UTC](https://community.neo4j.com/t/how-to-get-tree-structure-that-has-access-rights-on-each-node/50064/2 "2022-01-07T08:15:09Z")

</div>

Hi,  
To begin with you could try this procedure:

> **[apoc.convert.toTree - APOC Extended Documentation](https://neo4j.com/labs/apoc/4.3/overview/apoc.convert/apoc.convert.toTree/)**
>
> This section contains reference documentation for the apoc.convert.toTree procedure.

And there is an example here:

> <https://stackoverflow.com/questions/44923357/neo4j-return-nested-json>

I will test if I can fix the filtering as soon as I have time.

---

<div class="post-metadata">

**Author:** ![filantrop](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/filantrop/32/20927_2.png) [@filantrop](https://community.neo4j.com/u/filantrop)\
**Post date:** [January 7, 2022, 9:34am UTC](https://community.neo4j.com/t/how-to-get-tree-structure-that-has-access-rights-on-each-node/50064/3 "2022-01-07T09:34:31Z")

</div>

To accomplish this I've to create a temporarily CHILD relation.  
Didn't find any apoc that can create a virtual paths from nodes. Which would be a better solution.

To rerun the solution you have to delete the child relations at the beginning ohterwise there will be doubles.

Firstly find all longest paths, and remove all nodes in the paths that don't have an outlink of IS\_PARENT.  
Then create child relations between them.

```auto
MATCH path=(a:Account {name: "bob"})-[:VIEWER | EDITOR]->(c:Project)-[:IS_PARENT *]->(dest:Project)
WHERE not exists((dest)-[:IS_PARENT]->())
with [n in nodes(path) where (n)<-[:VIEWER | EDITOR]-()|n] as nodes
call apoc.nodes.link(nodes,'CHILD')

```

Then you can run the following to get result that is near what you want.

```auto
MATCH path=(s)-[:CHILD *]->(d)
with collect(path) as paths
call apoc.convert.toTree(paths) yield value
return value

```

Clean up the CHILD relations

```auto
match ()-[C:CHILD]->() DELETE C

```

---

<div class="post-metadata">

**Author:** ![glilienfield](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/glilienfield/32/27534_2.png) [@glilienfield](https://community.neo4j.com/u/glilienfield)\
**Post date:** [January 8, 2022, 4:11pm UTC](https://community.neo4j.com/t/how-to-get-tree-structure-that-has-access-rights-on-each-node/50064/4 "2022-01-08T16:11:41Z")

</div>

I wasn't able to get exactly what you want. Your structure will require an iterative algorithm to get the child nesting you want. I was able to get the following output, which gives you the child nodes along each path. If you are going to process this output in a program, such as java, you could parse it and rearrange the paths into nested children. How this helps a little. I didn't bother trying to simply the query, since it is not what you wanted.

Query:  
MATCH (a:Account {name: "bob"})-[r:VIEWER | EDITOR]-\>(c:Project)  
MATCH path=(rootNode)-[:IS\_PARENT\*]-\>(c)  
MATCH (rootNode)\<-[q:VIEWER | EDITOR]-(a)  
WHERE NOT exists(()-[:IS\_PARENT]-\>(rootNode))  
WITH rootNode, type(q) as rootType, type(r) as nodeType, path  
CALL {  
WITH path, nodeType  
UNWIND tail(nodes(path)) as node  
WITH {access:nodeType, name:node.name} as grouped  
return collect(grouped) as groupedNodesOnPath  
}  
WITH rootNode, rootType, collect(groupedNodesOnPath) as children  
RETURN rootNode{.name, access:rootType, children:children}

Output:  
{  
"access": "VIEWER",  
"children": [  
[  
{  
"access": "VIEWER",  
"name": "cohort"  
}  
],  
[  
{  
"access": "VIEWER",  
"name": "experience"  
}  
],  
[  
{  
"access": "EDITOR",  
"name": "protocol"  
}  
],  
[  
{  
"access": "EDITOR",  
"name": "cohort"  
},  
{  
"access": "EDITOR",  
"name": "nested child"  
}  
]  
],  
"name": "project test"  
}

---

<div class="post-metadata">

**Author:** ![bobsabates](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/bobsabates/32/21703_2.png) [@bobsabates](https://community.neo4j.com/u/bobsabates)\
**Post date:** [January 10, 2022, 3:40pm UTC](https://community.neo4j.com/t/how-to-get-tree-structure-that-has-access-rights-on-each-node/50064/5 "2022-01-10T15:40:04Z")

</div>

@filantrop  
Hi thanks you for your answer it helps a lot, It is nearly exactly what i want.  
I have 3 questions:

1. When i add a new depth `(cohort) -> (nested child) -> (new child)`  
i get multiple results, event if the first one is good, can i do something to only get one result? because with each depth i will get more results

What i get :

```auto
{
  "_type": "Project:RootProject",
  "name": "project test",
  "_id": 12,
  "child": [
    {
      "_type": "Project",
      "name": "cohort",
      "_id": 14,
      "child": [
        {
          "_type": "Project",
          "name": "nested child",
          "_id": 20,
          "child": [
            {
              "_type": "Project",
              "name": "sous projet",
              "_id": 34
            }
          ]
        }
      ]
    },
    {
      "_type": "Project",
      "name": "protocol",
      "_id": 16
    },
    {
      "_type": "Project",
      "name": "experience",
      "_id": 18
    }
  ]
}

//------------------Result 2-------------------------
{
  "_type": "Project",
  "name": "cohort",
  "_id": 14,
  "child": [
    {
      "_type": "Project",
      "name": "nested child",
      "_id": 20,
      "child": [
        {
          "_type": "Project",
          "name": "sous projet",
          "_id": 34
        }
      ]
    }
  ]
}

//-----------------Result 3--------------------------
{
  "_type": "Project",
  "name": "nested child",
  "_id": 20,
  "child": [
    {
      "_type": "Project",
      "name": "sous projet",
      "_id": 34
    }
  ]
}

```

My second question is:  
Is there a way to add the type of relation `bob` has with each `child` in the same query ?

```auto
"Children": [
    {
      "access": "EDITOR",
      "name": "protocol"
    },
    {
      "access": "EDITOR",
      "name": "nested child"
    }
  ]

```

And my last question:  
If i remove the relation between `bob` and the `RootProject`, the query doesn't return a correct result, how can we fix this ?

---

<div class="post-metadata">

**Author:** ![glilienfield](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/glilienfield/32/27534_2.png) [@glilienfield](https://community.neo4j.com/u/glilienfield)\
**Post date:** [January 10, 2022, 10:27pm UTC](https://community.neo4j.com/t/how-to-get-tree-structure-that-has-access-rights-on-each-node/50064/6 "2022-01-10T22:27:49Z")

</div>

Question 1: If I am understanding your statement, you are getting multiple paths from your query, which you don't want. These are resulting from each of the following match clause;

MATCH path=(rootNode)-[:IS\_PARENT\*]-\>(c)

This results in a path for each of the nodes that has an outgoing 'IS\_PARENT' relationship. I assumed in my query that you did not want that, but wanted the path from the root of the graph. I accomplished that by added the following where clause:

WHERE NOT exists(()-[:IS\_PARENT]-\>(rootNode))

This eliminates those nodes that have an incoming 'IS\_PARENT' relationship, which indicate that are not at the root of the graph. This should eliminate the multiple paths, and return just one result starting from the root of the graph. In your case, that is the 'project test' node.

Question 2. Isn't that what the 'access' attribute is for each child? If I misunderstand, clarify and I will try to help.
