1. Introduction
In this post, I will build a small web application in java by learning how to connect to as well as manipulate on neo4j database. Neo4j offers a rich set of integration possibilities for developers using Java or other JVM-languages. Following the instruction on the website, we will build a small web app for the movie database.
2.Create a project And add neo4j driver dependencies
Here, I use IntelliJ IDEA as dev tool and Maven as project management tool. So, select maven-archetype-webapp.
After that, go to the maven repository website(Link) and add dirver dependency to pom.xml.
<dependency>
<groupId>org.neo4j.app</groupId>
<artifactId>neo4j-server</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>org.neo4j.driver</groupId>
<artifactId>neo4j-java-driver</artifactId>
<version>1.7.2</version>
</dependency>
3. Play with the movie dataSet.
Using the data provided by the neo4j guides. Run all CQL commands and get the result.
Instruction from the guide
The Movie Graph is a mini graph application containing actors and directors that are related through the movies they've collaborated on.
This guide will show you how to:
Create: insert movie data into the graph
Find: retrieve individual movies and actors
Query: discover related actors and directors
Solve: the Bacon Path
After executing the CQL languages, what we will get the graph database
4. Establish Connection.
In order to use the diver, make connection to the neo4j database. we should do the following things.
Provide a connection URL with username and password. The URL has the format: bolt://username:password@localhost
Get a driver instance
From the driver, create a session.
invoke session.run() method by parsing two parameters. 1). Cypher Query Language with parameters 2). Parameters given in the type of java map, with key-value pairs.
It will return result of Statement Result(see Documantation for detail ).It extends the Iterator class. so, it has hasNext() method and next() method to iterate through the result.
Close and session and close the database.
Cypher Query Language with parameters :
For Query String reuse and injection prevention. we will passing the parameters by Parameter Binding. In their document, they say:
Cypher supports querying with parameters. This means developers don't have to resort to string building to create a query. Additionally, parameters make caching of execution plans much easier for Cypher, thus leading to faster query execution times.
Where to use.
Parameters can be used for:
literals and expressions
node and relationship ids
for explicit indexes only: index values and queries
Where cannot be used.
Parameters cannot be used for the following constructs, as these form part of the query structure that is compiled into a query plan:
property keys; so, MATCH (n) WHERE n.$param = 'something' is invalid
relationship types
labels
There are multiple excellent Examples to use CQL With parameters in neo4j documentation
Here, I wrote codes to test connection with Unit Test.
package com.neo4j.movies;
import org.junit.Test;
import org.neo4j.driver.v1.*;
import java.util.HashMap;
import java.util.Map;
public class EnvTest {
@Test
public void testConnection(){
Driver driver = GraphDatabase.driver( "bolt://localhost:7687", AuthTokens.basic("neo4j", "root" ) );
System.out.println("Server is up and Running");
// find all the author names for a given movie name
String query = "MATCH (movie: Movie {title:$title})<-[:ACTED_IN]-(person:Person) RETURN person.name as actor";
Map<String,Object> titleMap = new HashMap<>();
titleMap.put("title","The Matrix");
try{
Session session = driver.session();
StatementResult statementResult = session.run(query,titleMap);
while(statementResult.hasNext()){
System.out.println(statementResult.next().get("actor"));
}
session.close();
}catch (Exception e){
e.printStackTrace();
}
try {
driver.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
Result
INFO: Direct driver instance 99451533 created for server address localhost:7687
Server is up and Running
"Emil Eifrem"
"Hugo Weaving"
"Laurence Fishburne"
"Carrie-Anne Moss"
"Keanu Reeves"
May 14, 2019 3:45:49 PM org.neo4j.driver.internal.logging.JULogger info
INFO: Closing driver instance 99451533
May 14, 2019 3:45:49 PM org.neo4j.driver.internal.logging.JULogger info
INFO: Closing connection pool towards localhost:7687
Process finished with exit code 0
Each Result is in the type of _org.neo4j.driver.v1.Record_, Record is a form of ordered map and, as such, contained values can be accessed by either positional index or textual key.doc.
Record can either be node or relationships. When we retrieve data from the graph database, we either want to retrieve nodes(with properties and values) or a path contains nodes and relationships. Here are two small demos for retrieving nodes and paths.
5. Small Demo
Preparation: Let us create a DriverUtil class to wrap the manipulations on the driver
5.1 Retrieve Node.
Here is the sample code for querying a person by giving its name.
package com.neo4j.movies;
import Util.DriverUtil;
import org.junit.Test;
import org.neo4j.driver.v1.*;
import org.neo4j.driver.v1.types.Node;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.*;
public class FindPersonByName {
public void findPersonByName(String queryName){
Driver driver = DriverUtil.getDriver();
String queryPersonByName = "MATCH (person:Person) WHERE person.name = $name RETURN person";
Map<String,Object> map = new HashMap<>();
map.put("name",queryName);
try(Session session = driver.session()) {
StatementResult statementResult = session.run(queryPersonByName, map);
while(statementResult.hasNext()){
Record record = statementResult.next();
List<Value> valueList = record.values();// Retrieve the values of the underlying map
assertEquals(valueList.size(),1);
// each value is a node actually, so convert to node type
for(Value value: valueList){
Node node = value.asNode();
// iterate through node labels
Iterator<String> labels = node.labels().iterator();
System.out.println("Node labels");
while(labels.hasNext()){
System.out.println(labels.next());
}
// iterate through node property-value pairs
System.out.println("Node key-value pairs ");
Iterator<String> keys = node.keys().iterator();
while(keys.hasNext()){
String property = keys.next();
Object propertyValue = node.get(property);
System.out.println("property: "+ property);
System.out.println("propertyValue: "+ propertyValue);
}
}
}
}catch (Exception e){
e.printStackTrace();
}
try{
DriverUtil.closeDriver();
} catch (Exception e){
e.printStackTrace();
}
}
@Test
public static void main(String[] args) {
FindPersonByName fd = new findPersonByName();
fd.findPersonByName("Emil Eifrem");
}
}
Result
INFO: Direct driver instance 530653666 created for server address localhost:7687
Node labels
Person
Node key-value pairs
property: name
propertyValue: "Emil Eifrem"
property: born
propertyValue: 1978
INFO: Closing connection pool towards localhost:7687
Disconnected from the target VM, address: '127.0.0.1:57996', transport: 'socket'
Process finished with exit code 0
We can see the Result Object by debugging mode.
We can see that Record itself is an unordered Hash Map, and keys are the Strings of the Return Statement in SQL. And the Value object itself is also a HashMap.
If value itself is a node. it contains
- A List of labels
- A List of properties, where properties itself is also a hash map, mapping from property keys to property values.
For all their usages ,see document.
5.2 Retrieve Path(relationship).
Suppose we want to see if a person ACTED_IN a movie and what is his/her role in the movie. If that particular people does not ACTED_IN that movie, return false.
e.g “Keanu Reeves” ACTED_IN “Johnny Mnemonic” as “Johnny Mnemonic”
Lets see the path Object.
It contains:
- An ArrayList of nodes
- An ArrayList of relationships
- An ArrayList of segments which contains the start node, relation and the end node.
package com.neo4j.movies;
import Util.DriverUtil;
import org.junit.Test;
import org.neo4j.driver.v1.*;
import org.neo4j.driver.v1.types.Node;
import org.neo4j.driver.v1.types.Path;
import org.neo4j.driver.v1.types.Relationship;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class FindRolesByPersonNameAndMovieName {
@Test
public void findRolesByPersonNameAndMovieName(String actorName, String movieName){
Driver driver = DriverUtil.getDriver();
String queryRoles = "MATCH p =(person:Person)-[ACTED_IN]->(movie:Movie) WHERE person.name = $actorName " +
"AND movie.title = $movieName RETURN p ";
Map<String,Object> parameters = new HashMap<>();
parameters.put("actorName",actorName);
parameters.put("movieName",movieName);
try(Session session = driver.session()){
StatementResult statementResult = session.run(queryRoles,parameters);
System.out.println("*****relationship*******");
while(statementResult.hasNext()){
Record record = statementResult.next();
List<Value> valuesList = record.values();
// if the movie and actor do not match each other, no searching result.
if(valuesList == null || valuesList.size() == 0) return;
// find the relationship between the movie node and person node
Path path = valuesList.get(0).asPath();
// find all the relationships(edges) along the path
Iterator<Relationship> relationships = path.relationships().iterator();
// find all the nodes along the path
Iterator<Node> nodes = path.nodes().iterator();
// iterate edges along the path
while(relationships.hasNext()){
Relationship relationship = relationships.next();
relationship.startNodeId();
relationship.endNodeId();
String relationType = relationship.type();
System.out.println("start from Node Id: "+ relationship.startNodeId());
System.out.println("end from Node Id: "+ relationship.endNodeId());
System.out.println("The relation type is:" +relationType);
System.out.println("RelationShip key value pairs");
Iterator<String> relationKeys = relationship.keys().iterator();
while(relationKeys.hasNext()){
String relationKey = relationKeys.next();
Object relationValue = relationship.get(relationKey).get(0);
System.out.println(relationKey +":" +relationValue);
}
}
// iterate all nodes along the path
while(nodes.hasNext()){
System.out.println("*****Node*******");
Node node = nodes.next();
System.out.println("Node Id: " + node.id());
Iterator<String> labels = node.labels().iterator();
System.out.println("Node labels");
while(labels.hasNext()){
System.out.println(labels.next());
}
// iterate through node property-value pairs
System.out.println("Node key-value pairs ");
Iterator<String> keys = node.keys().iterator();
while(keys.hasNext()){
String property = keys.next();
Object propertyValue = node.get(property);
System.out.println(property+":"+propertyValue);
}
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
DriverUtil.closeDriver();
}
}
public static void main(String[] args) {
FindRolesByPersonNameAndMovieName findRole = new FindRolesByPersonNameAndMovieName();
findRole.findRolesByPersonNameAndMovieName("Keanu Reeves","Johnny Mnemonic");
}
}
Result
*****relationship*******
start from Node Id: 1
end from Node Id: 161
The relation type is:ACTED_IN
RelationShip key value pairs
roles:"Johnny Mnemonic"
*****Node*******
Node Id: 1
Node labels
Person
Node key-value pairs
name:"Keanu Reeves"
born:1964
*****Node*******
Node Id: 161
Node labels
Movie
Node key-value pairs
tagline:"The hottest data on earth. In the coolest head in town"
title:"Johnny Mnemonic"
released:1995
Notice that: the return type is a path, So, in CQL, We name the path as p. If we want to return all, Using Return *, see CQL manual,link.
6. OGM Mapping And Mapping to JSON
To be continued.