0

I would like to create an octree structure in Neo4j using Cypher. I would like to create something similar to the illustration below:

octree

Any ideas on how this could be implemented without having to create each node and relationship "manually"?

Frank Pavageau
  • 11,477
  • 1
  • 43
  • 53
edwpea
  • 1
  • 1

3 Answers3

1

You can use Cypher to generate a tree if you know its height in advance. For simplicity, I generated binary trees (with a branching factor of 2).

WITH 0 as root, range(1,2) AS branches
WITH root as root, branches AS l1s, branches AS l2s
UNWIND l1s AS l1
UNWIND l2s AS l2
MERGE (n0:TreeNode {name: root})
MERGE (n1:TreeNode {name: l1})
MERGE (n2:TreeNode {name: l1+"_"+l2})
MERGE (n0)-[:X]->(n1)
MERGE (n1)-[:X]->(n2)

This results in the following tree:

enter image description here

Explanation: for a tree of k levels, we copy the branches variables k-1 times, and unwind each list. This creates a Cartesian product, hence producing the leaf nodes. For (full) binary trees of k levels, this results in 2^(k-1) leaf nodes. (This also works for octrees which will have 8^(k-1) levels.)

We combine the numbers of the levels with underscores to create unique variable names for each level. The ids can be queried as such:

WITH 0 as root, range(1,2) AS branches
WITH root as root, branches AS l1s, branches AS l2s
UNWIND l1s AS l1
UNWIND l2s AS l2
RETURN root, l1, l1+"_"+l2

This results in:

╒════╤═══╤═════════╕
│root│l1 │l1+"_"+l2│
╞════╪═══╪═════════╡
│0   │1  │1_1      │
├────┼───┼─────────┤
│0   │1  │1_2      │
├────┼───┼─────────┤
│0   │2  │2_1      │
├────┼───┼─────────┤
│0   │2  │2_2      │
└────┴───┴─────────┘

Now all we have to do is create the nodes and relationships, while paying attention that nodes/edges are only created once. This is ensured by using the MERGE. (MERGE might seem tricky at first, but there are good explanations.)

If you want to add additional levels, update the query as such:

  • define a new variable from branches, e.g. l3s
  • unwind the new variable, e.g. to l3
  • create additional nodes for the new level with the variable name appended, e.g. MERGE (n3:TreeNode {name: l1+"_"+l2+"_"+l3})
  • create new edges from the previous level, e.g. MERGE (n2)-[:X]->(n3)

Of course, you can also use numbers for the nodes. Instead of appending undercores, you need to generate a new numeric "id" for each node.

WITH range(1,2) AS branches
WITH branches AS l1s, branches AS l2s
UNWIND l1s AS l1
UNWIND l2s AS l2
MERGE (n0:TreeNode {number: 0})
MERGE (n1:TreeNode {number: l1})
MERGE (n2:TreeNode {number: 2*l1+l2})
MERGE (n0)-[:X]->(n1)
MERGE (n1)-[:X]->(n2)

The result:

enter image description here

Gabor Szarnyas
  • 4,410
  • 3
  • 18
  • 42
1

You can do it in Cypher by creating the root:

CREATE (root:Root:Leaf);

then repeating the query adding a level as many times as you need (at some point the transaction will get too large, though):

MATCH (n:Leaf)
REMOVE n:Leaf
FOREACH (i IN range(0, 7) |
    CREATE (n)-[:CONTAINS]->(:Node:Leaf {value: coalesce(n.value, "") + toString(i)}));
Frank Pavageau
  • 11,477
  • 1
  • 43
  • 53
0

Im not sure if cypher alone can do it, but you could use a programming language and connect to neo4j to create the nodes and relationships.
In PHP for example:

function create_children($parent){

    print "\n$parent: ";
    for ($i=0; $i<=7;$i++) {
        $node_id = (int) "$parent"."$i";
        $children[] = $node_id;
        print "$node_id,";
        // create children nodes
        // CREATE (child:node) SET node_id = $node_id 
        //create relationship here
        // MATCH (parent:node) where node_id = $parent
        // CREATE (parent)-[r:parent_of]->(child)
    }
    return $children;

}


function create_tree ($root, $depth) {
    if ($depth ==0) return;
    else{
        $children = create_children($root);
        $depth--;
        foreach ($children as $child) {
            create_tree($child, $depth);
        }
    }
}


// MAIN

// CREATE (parent:node) SET node_id=0;
create_tree(0,3);

Of course where the cypher statements are you need to connect to your neo4j instance and execute those statements.
If you aren't sure how to do that you could just print out the cypher statements and then paste them into a neo shell or browser
here is the output of running create_tree(0,2) The output shows the parent followed by its eight children

0: 00,01,02,03,04,05,06,07,
00: 00,01,02,03,04,05,06,07,
01: 10,11,12,13,14,15,16,17,
02: 20,21,22,23,24,25,26,27,
03: 30,31,32,33,34,35,36,37,
04: 40,41,42,43,44,45,46,47,
05: 50,51,52,53,54,55,56,57,
06: 60,61,62,63,64,65,66,67,
07: 70,71,72,73,74,75,76,77,

let me know if that's what you were looking for

Albert S
  • 2,552
  • 1
  • 22
  • 28