This week, Dave Poole published introductory but insightful article on Neo4j where he used SQL-Views on System tables to generate Cypher statements that insert the database meta data into Neo4j for further inspection.
Getting more insights about the overall structure of your Graph Database is a common need among our users, I published an article a while ago, that used the Neo4j-Shell and some JavaScript driving the Core-API to achieve this goal.
After our data model training, our dutch trainer Ron van Weverwijk, posed that question again, being one of the obstacles for attendees of our classes.
Then it made click and I thought: "That’s supereasy to do in Cypher itself"
As sample database I want to use a variant of our WorldCup dataset. For lack of other data on the plane, I used the match Germany vs. Portugal.
MERGE (year:Year {year: 2014})
MERGE (host:Country {name: "Brazil"})
MERGE (worldCup:WorldCup {name: "WorldCup "+host.name+" "+year.year})
MERGE (host)<-[:HOSTED_BY]-(worldCup)
MERGE (year)<-[:IN_YEAR]-(worldCup)
MERGE (stadium:Stadium {name: "Arena Fonte Nova, Salvador"})
MERGE (stadium)-[:STADIUM_OF]->(worldCup)
MERGE (home:Country {name: "Germany"})
MERGE (worldCup)<-[:PARTICIPATED_IN]-(homeTeam:Team {name: "Team "+home.name+" in "+worldCup.name})<-[:NAMED_TEAM]-(home)
MERGE (away:Country {name: "Portugal"})
MERGE (worldCup)<-[:PARTICIPATED_IN]-(awayTeam:Team {name: "Team "+away.name+" in "+worldCup.name})<-[:NAMED_TEAM]-(away)
MERGE (worldCup)-[:CONTAINS]->(match:Match {id: "G"}) ON CREATE SET match.name = home.name +" vs "+away.name
MERGE (match)-[:PLAYED_IN_STADIUM]->(stadium)
MERGE (match)-[:HOME_TEAM]->(homeTeam)
MERGE (match)-[:AWAY_TEAM]->(awayTeam)
FOREACH (p IN ["Müller","Götze","Özil","Khedira","Höwedes","Boateng","Mertesacker","Hummels","Lahm","Kroos","Neuer"] |
MERGE (player:Player {name:p})
MERGE (player)-[:IN_TEAM]->(homeTeam)
MERGE (player)-[:PLAYED_IN]->(match)
)
FOREACH (p IN ["Patricio","Pepe","Alves","Coentrao","Meireles","Ronaldo","Almeida","Moutinho","Nani","Pereira","Veloso"] |
MERGE (player:Player {name:p})
MERGE (player)-[:IN_TEAM]->(awayTeam)
MERGE (player)-[:PLAYED_IN]->(match)
)
FOREACH (goal IN [{name:"Müller",minutes:12},{name:"Hummels",minutes:32},{name:"Müller",minutes:45},{name:"Müller",minutes:78}] |
MERGE (player:Player {name:goal.name})
MERGE (player)<-[:SCORED_GOAL {time:goal.minutes}]-(match)
);
A tiny slice of the WorldCup-Graph looks like this, the real dataset has all WorldCups, Players, Matches, Scores and Countries.
After all it was pretty simple to create the meta-graph, this query is similar to the one "What is connected and how, that you find in the Neo4j-Browser-Sidebar", which looks like this.
MATCH (a)-[r]->(b)
WITH labels(a) AS a_labels,type(r) AS rel_type,labels(b) AS b_labels
RETURN distinct *
LIMIT 100;
We can use this result easily to create a network of :Node:Meta and :Relationship:Meta nodes representing the graph structure of our domain.
MATCH (a)-[r]->(b)
WITH labels(a) AS a_labels,type(r) AS rel_type,labels(b) AS b_labels
UNWIND a_labels as l
UNWIND b_labels as l2
MERGE (a:Node:Meta {name:l})
MERGE (b:Node:Meta {name:l2})
MERGE (a)-[:OUTGOING]->(:Relationship:Meta {name:rel_type})-[:INCOMING]->(b)
RETURN distinct l as first_node, rel_type as connected_by, l2 as second_node
MATCH (m:Meta)-[r]-()
RETURN m,r
So you can clearly see the nodes and the relationships that connect them in this meta-graph representation.