As new starter I stuck on a possible failure on my queries or the DB model. I wanted to implement the menu structure of my application as well in the neo4j database in order to benefit from main/submenue correlations.
So there are MAIN and SUB options whereas a MAIN can have several SUB's but must not.
An example:
HOME (main)
ADMIN (main) - USER (sub)
DESK (main) - EDIT (sub)
- CREATE (sub)
a.s.o.
While creating the nodes I build a relationship between MAIN and SUB nodes with "is_parent".
So my first challenge is to find read the structure from the DB again and build an related array to construct the menu structure.
I was trying:
# Identify all Menu-Options of the main Menu
$queryString = 'MATCH (main:MENU) RETURN main;';
$query = new Everyman\Neo4j\Cypher\Query($client, $queryString);
$result = $query->getResultSet();
foreach ($result as $row) {
# Echo for debug
echo '<hr>'.$row['main']->getProperty('name') . "<br>";
$query_sub = '
MATCH (main:MENU {name: "'.$row['main']->getProperty('name').'"})-[:is_parent]->sub:SUBMENU)
RETURN sub;
';
$query_sub = new Everyman\Neo4j\Cypher\Query($client, $query_sub);
$result_sub = $query_sub->getResultSet();
foreach ($result_sub as $row_sub) {
# echo for debug
echo 'SUB:'.$row_sub['sub']->getProperty('name') . "<br>";
}
}
But it seems I get every menu option double - as if I would travers every graph twice, thus I guess
a) either I havent understood queries correct (most likely as these are my 2nd try) b) my DB model is not correct, maybe Imiss another PARENT-NODE that connects all MAIN MENU nodes (right now they are not connected, only the SUB is connected to the related MAIN).
I use neo4jphp for connecting to the DB.
Every hint is very appreciated.
Thanks!
P.S:
I made this schema for the data as an example (btw. maybe there is a way to make the queries more compact?):
create (home:MENU:MAIN { name: "HOME" })
create (admin:MENU:MAIN { name: "ADMIN" })
create (blog:MENU:MAIN { name: "BLOG" })
create (support:MENU:MAIN { name: "SUPPORT" })
create (user:MENU:SUB { name: "USER" })
create (groups:MENU:SUB { name: "GROUPS" })
create (local:MENU:SUB2 { name: "LOCAL" })
create (extern:MENU:SUB2 { name: "EXTERN" })
create (edit:MENU:SUB { name: "EDIT" })
create (create:MENU:SUB { name: "CREATE" })
create (delete:MENU:SUB { name: "DELETE" })
CREATE (admin)-[:IS_PARENT]->(user)
CREATE (admin)-[:IS_PARENT]->(groups)
CREATE (groups)-[:IS_PARENT]->(local)
CREATE (groups)-[:IS_PARENT]->(extern)
CREATE (blog)-[:IS_PARENT]->(edit)
CREATE (blog)-[:IS_PARENT]->(create)
CREATE (blog)-[:IS_PARENT]->(delete)
So there are nodes which are not connected by a relationship, but marked by the label.
When I use the queries below I get all menu options that have a relation to the sub-menue. How would I alter the query to get ALL main menu options (so SUPPORT and HOME as well) and the sub.menu-options like
ADMIN GROUPS true ADMIN USER true BLOG DELETE true BLOG CREATE true BLOG EDIT true HOME NULL true SUPPORT NULL true
In SQL I had use something like a LEFT JOIN but in Cypher I am unsure. OPTIONAL was not helping, I guess there is another way?
Not sure what your data looks like, do your sub-menus also have a :MENU label?
I'd probably label all menu nodes with :Menu and then have an additional label for :Main and :Sub
then you can fetch all in one go, even with arbitrary depth:
MATCH (m:Menu)-[:IS_PARENT]->(s:Sub)
RETURN m.name, s.name, m:Main
the names of the menu, submenu pairs and true if m is a main menu, false otherwise
MATCH (m:Main)
MATCH p = (m)-[:IS_PARENT*]->(s:Sub)
RETURN m.name, extract(n in tail(nodes(p)) | n.name) as menus
ORDER BY m.name ASC, length(p) asc
this one returns all the paths from the root menus with the root menu name, and then all submenu names as an array ordered by root menu name and ascending lenght of the path