neo4jcypherfamily-tree

Fetching nested data with NEO4J in Family Tree


First of all, I'm new to NEO4J and to CYPHER. So I'm twerking here and there to figure out to get the result I want.

Below is my graph. Let's say it's a simple family tree.

data structure

I have come up with this simple cypher query to fetch the direct descendants of the node

MATCH (p:Person {username: "SETHLORDM"})<-[r:CHILD_OF]-(p2) 
RETURN {current: p, children: collect(p2)}

and the text version of the result is as below

text version of cipher

The above is okay, but I want to get the text result as follows if it's doable with NEO4J.

[
  {
    "username": "SETHLORDM",
    "location": "NO_LOCATION",
    "children": [
      {
        "username": "TESTNODE_1",
        "location": "LEFT",
        "children": [
          {
            "username": "TESTNODE_3",
            "location": "LEFT",
            "children": []
          },
          {
            "username": "TESTNODE_4",
            "location": "RIGHT",
            "children": []
          }
        ],
      },
      {
        "username": "TESTNODE_2",
        "location": "RIGHT",
        "children": [
          {
            "username": "TESTNODE_5",
            "location": "RIGHT",
            "children": []],
          },
                    {
            "username": "TESTNODE_6",
            "location": "RIGHT",
            "children": []],
          }
        ],
      }
    ],
  }
]

Any help regarding this would be highly appreciated. Thank you


Solution

  • One way to approach it is using apoc.convert.toTree (using the plugin apoc). This can create the tree structure that you are looking for. But, since your tree is bottom-up, the result will be same, meaning each node will point its parent. If you want to get the results as you want, using this method, you will have to change your relations.

    For example, using this data:

    MERGE (a:Person{key: 1, username: "SETHLORDM"})
    MERGE (b:Person{key: 2})
    MERGE (c:Person{key: 3})
    MERGE (d:Person{key: 4})
    MERGE (e:Person{key: 5})
    MERGE (f:Person{key: 6})
    MERGE (g:Person{key: 7})
    
    MERGE (b)-[:CHILD_OF]-(a) 
    MERGE (c)-[:CHILD_OF]-(a) 
    MERGE (d)-[:CHILD_OF]-(b)
    MERGE (e)-[:CHILD_OF]-(b) 
    MERGE (f)-[:CHILD_OF]-(c) 
    MERGE (g)-[:CHILD_OF]-(c) 
    

    and this query:

    MATCH path = (p:Person {username: "SETHLORDM"})<-[r:CHILD_OF*..2]-(p2)
    WITH collect(path) AS paths
    CALL apoc.convert.toTree(paths)
    YIELD value
    RETURN value;
    

    will give this result:

    
      "_type": "Person",
      "child_of": [
        {
          "_type": "Person",
          "child_of": [
            {
              "_type": "Person",
              "_id": 243,
              "key": 5
            },
            {
              "_type": "Person",
              "_id": 242,
              "key": 4
            }
          ],
          "_id": 240,
          "key": 2
        },
        {
          "_type": "Person",
          "child_of": [
            {
              "_type": "Person",
              "_id": 245,
              "key": 7
            },
            {
              "_type": "Person",
              "_id": 244,
              "key": 6
            }
          ],
          "_id": 241,
          "key": 3
        }
      ],
      "_id": 239,
      "key": 1,
     
     "username": "SETHLORDM"
    }
    

    But changing the links to this:

    MERGE (a)-[:CHILDREN]-(b) 
    MERGE (a)-[:CHILDREN]-(c) 
    MERGE (b)-[:CHILDREN]-(d)
    MERGE (b)-[:CHILDREN]-(e) 
    MERGE (c)-[:CHILDREN]-(f) 
    MERGE (c)-[:CHILDREN]-(g) 
    

    And adjusting the query to:

    MATCH path = (p:Person {username: "SETHLORDM"})-[r:CHILDREN*..2]->(p2)
    WITH collect(path) AS paths
    CALL apoc.convert.toTree(paths)
    YIELD value
    RETURN value;
    

    Will provide:

    {
      "_type": "Person",
      "_id": 246,
      "children": [
        {
          "_type": "Person",
          "_id": 247,
          "children": [
            {
              "_type": "Person",
              "_id": 249,
              "key": 4
            },
            {
              "_type": "Person",
              "_id": 250,
              "key": 5
            }
          ],
          "key": 2
        },
        {
          "_type": "Person",
          "_id": 248,
          "children": [
            {
              "_type": "Person",
              "_id": 252,
              "key": 7
            },
            {
              "_type": "Person",
              "_id": 251,
              "key": 6
            }
          ],
          "key": 3
        }
      ],
      "key": 1,
      "username": "SETHLORDM"
    }
    

    Which is now similar to what you wanted...

    Bonus: if you are using apoc, you can replace the MATCH query by apoc.path.expandConfig which should be more efficient to larger graphs.