Search This Blog

Saturday, 5 May 2018

Neo4j - Identifying a Nodes Label and a Relations Type

I wanted to find out which all person nodes are associated to a given Movie and in what roles. Consider that my Movie database has several relations -
Person acted in a movie
Person directed a movie
Person reviewed a movie
Person followed a movie ......... and many more.

In a relational database this would probably be setup as separate tables - ACTED table, DIRECTED table etc each of which would map the Person's primary key and the movies key. In Neo4j relations played the role. But If I wanted to find out all of theses above results would I have to fire a separate query for each type of relation ?
Something like
MATCH (a:Person)-[:ACTED_IN]->(m:Movie) WHERE m.title='Cloud Atlas' RETURN a
MATCH (a:Person)-[:DIRECTED]->(m:Movie) WHERE m.title='Cloud Atlas' RETURN a
MATCH (a:Person)-[:REVIEWED]->(m:Movie) WHERE m.title='Cloud Atlas' RETURN a
This would mean firing n cypher queries and processing each result separately. This would be a very inefficient way to do it. Also as the number of possible relations grow, change the code would have to be updated.
Fortunately CYPHER provides a very useful type keyword. The same result can be obtained with a single query as seen below:
public Map<String, String> getAssociationsToMovie(String movieName, int released) {
    try (Transaction tx = graphDb.beginTx()) {
      Map<String, Object> params = new HashMap<>();
      params.put("mName", movieName);
      params.put("releasedYear", released);
      Result result = graphDb.execute(
          "MATCH (p:Person)-[r]->(m:Movie) WHERE m.title='Cloud Atlas' RETURN p.name AS pName, type(r) as assocn",
          params);
      if (!result.hasNext()) {
        return null;
      }
      Map<String, String> soln = new HashMap<>();
      while (result.hasNext()) {
        Map<String, Object> row = result.next();
        String pName = (String) row.get("pName");
        String assocn = (String) row.get("assocn");
        soln.put(pName, assocn);// assuming person has only one relationship to
                                // a given Movie
      }
      return soln;
    }
  }
I tested the same as below:
public static void main(String[] args) {
    MovieDAO movieDAO = new MovieDAO();
    
    Map<String, String> result = movieDAO.getAssociationsToMovie("Cloud Atlas", 2012);
    result.keySet().forEach(key -> System.out.println(key + "  -   " + result.get(key) ));
 }
The output of the above code is:
Lana Wachowski  -   DIRECTED
Tom Hanks  -   ACTED_IN
Tom Tykwer  -   DIRECTED
Hugo Weaving  -   ACTED_IN
Halle Berry  -   ACTED_IN
Stefan Arndt  -   PRODUCED
Jim Broadbent  -   ACTED_IN
Jessica Thompson  -   REVIEWED
Andy Wachowski  -   DIRECTED
David Mitchell  -   WROTE
Similarly consider the case that we have various type of Nodes all asociated with a movie. And we would like to fetch them all in a single query. How do we identify the label of the node ?
Cypher agains come to our rescue with the labels keyword:
MATCH (a)-[r]->(m:Movie) 
WHERE m.title='Cloud Atlas' 
RETURN labels(a) as NodeType, a
The output of the code gives the values as seen here

No comments:

Post a Comment