Search code examples
neo4jcypher

Neo4j recursive cypher query resulting in nested JSON structure


I am trying to figure out cypher query in order to get nested JSON structure as a result. Below I present an example of the graph.

Graph

MATCH (user:User {name:"User_1"})
OPTIONAL MATCH (user)-[rel*]->(subUser:User)
RETURN *

Query above allows me to get all the nodes and relationships required to transform everything to JSON structure I want but that requires me to process everything after getting the result from querying the database. To achieve that I need to match identity of nodes and relationship in order to get the nested JSON. I was wondering if it is possible to achieve that directly from building cypher query. Important thing is that we do not know how many levels of "child" Users we have starting from User_1

Expected JSON structure:

{ 
    "user": "User_1",
    "children": [
        {
            "user": "User_2",
            "children": [
                {
                    "user": "User_5",
                    "children": []
                }
            ]
        },{
            "user": "User_3",
            "children": [
                {
                    "user": "User_6",
                    "children": []
                }
            ]
        },{
            "user": "User_4",
            "children": []
        }
    ]
}

Is it possible?


Solution

  • As suggested in the comments by @nimrod serok, you can use the apoc.convert.toTree method, it will give you the tree-structured JSON, as desired, with one caveat, the keys of the JSON will be different. For the data:

    MERGE (u1:User{name: 'User1'})
    MERGE (u2:User{name: 'User2'})
    MERGE (u3:User{name: 'User3'})
    MERGE (u4:User{name: 'User4'})
    MERGE (u5:User{name: 'User5'})
    MERGE (u6:User{name: 'User6'})
    MERGE (u1)-[:POINTS]->(u2)-[:POINTS]->(u5)
    MERGE (u1)-[:POINTS]->(u3)-[:POINTS]->(u6)
    MERGE (u1)-[:POINTS]->(u4)
    

    The query:

    MATCH (user:User {name:"User1"})
    OPTIONAL MATCH path = (user)-[:POINTS*]->(subUser:User)
    WITH collect(path) AS paths
    CALL apoc.convert.toTree(paths, true, {nodes: {User: ['name']}})
    YIELD value
    RETURN value
    

    produces the output:

    {
      "_type": "User",
      "name": "User1",
      "_id": 4,
      "points": [
        {
          "_type": "User",
          "name": "User3",
          "_id": 6,
          "points": [
            {
              "_type": "User",
              "name": "User6",
              "_id": 9
            }
          ]
        },
        {
          "_type": "User",
          "name": "User2",
          "_id": 5,
          "points": [
            {
              "_type": "User",
              "name": "User5",
              "_id": 8
            }
          ]
        },
        {
          "_type": "User",
          "name": "User4",
          "_id": 7
        }
      ]
    }
    

    as you can see, the relationship type POINTS, comes in place of children, and the key name comes for the user name. The other fields _type and _id can be ignored.