本文的第一部分介绍了Neo4j及其Cypher查询语言。若是您已经阅读了第1部分,那么您已经了解了为何Neo4j和其余图形数据库特别受html
而后,咱们使用Cypher查询语言对Neo4j中的一个家庭进行建模,包括年龄,性别和家庭成员之间的关系等我的属性。咱们建立了一些朋友来扩大咱们的社交图,而后添加键/值对来生成每一个用户看过的电影列表。最后,咱们查询了咱们的数据,使用图形分析来搜索一个用户没有看到但可能喜欢的电影。java
Cypher查询语言与SQL等传统数据查询语言不一样。Cypher并无考虑像表和外键关系这样的事情,而是强迫您考虑节点,节点之间的天然关系以及各个节点之间能够在各个关系之间进行的各类遍历。使用Cypher,您能够建立本身的心理模型,了解真实世界的实体如何相互关联。须要一些练习来擅长编写Cypher查询,可是一旦你理解了它们的工做方式,即便很是复杂的查询也是有意义的。node
在使用Cypher查询语言对Neo4j中的社交图建模并使用该社交图编写查询后,编写Java代码以对该图执行查询很是简单。在本文中,您将学习如何将Neo4j与Java Web客户端应用程序集成,您可使用它来查询咱们在第1部分中建立的社交图。数据库
咱们的第一步是建立一个新的Maven项目:apache
mvn archetype:generate -DgroupId=com.geekcap.javaworld -DartifactId=neo4j-example -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false复制代码
打开您的pom.xml
文件并添加Neo4j驱动程序,在撰写本文时版本为1.4.1:bash
<dependency>
<groupId>org.neo4j.driver</groupId>
<artifactId>neo4j-java-driver</artifactId>
<version>1.4.1</version>
</dependency> 复制代码
接下来,建立一个Neo4j Driver
,以下所示:网络
Driver driver = GraphDatabase.driver( "bolt://localhost:7687", AuthTokens.basic("neo4j", "neo4j"));复制代码
本GraphDatabase
类有一个叫作静态方法driver()
接受一个链接Neo4j的URL和AuthToken
。您可使用默认用户名和密码“neo4j” 建立基本AuthToken
。session
在Driver
与Neo4j的促进通讯。咱们经过要求Driver
建立Session
对象来执行查询,以下所示:app
Session session = driver.session();复制代码
该org.neo4j.driver.v1.Session
接口对Neo4j执行事务。在最简单的形式中,咱们能够执行继承自的run()
方法。而后,将开始一个事务,运行咱们的语句,并提交该事务。Sessionorg.neo4j.driver.v1.StatementRunnerSession
maven
该StatementRunner
接口定义了的几个变型run()
方法。这是咱们将使用的那个:
StatementResult run(String statementTemplate, Map<String,Object> statementParameters)复制代码
该statementTemplate
是一个包含咱们的Cypher查询的String
,statementParameters
包括咱们将使用的命名参数。例如,咱们可能想要建立具备指定名称和年龄的Person
:
session.run("CREATE (person: Person {name: {name}, age: {age}})",
parameters("name", person.getName(), "age", person.getAge()));复制代码
{name}和{age}
命名,能够经过传递解析为String
值的Map
。每一个String
都包含属性的名称,而且必须与模板中的值匹配。该parameters
方法一般从Values
对象静态导入:
import static org.neo4j.driver.v1.Values.parameters复制代码
一个Session
已经完成后,你须要经过调用它的close()
方法来关闭。为方便起见,该Session
对象实现了java.lang.AutoCloseable
接口,所以从Java 7开始,您能够在try-with-resources语句中执行它,例如:
try (Session session = driver.session()) {
session.run("CREATE (person: Person {name: {name}, age: {age}})",
parameters("name", person.getName(), "age", person.getAge()));
}复制代码
最后,若是您正在执行的是要约束到一个单一事务的多条语句,你能够自由地绕过Session
的run()
方法的自动交易管理和明确本身管理的事务。例如:
try (Session session = driver.session()) {
try (Transaction tx = session.beginTransaction()) {
tx.run("CREATE (person: Person {name: {name}, age: {age}})",
parameters("name", person.getName(), "age", person.getAge()));
tx.success();
}
}复制代码
该调用Session.beginTransaction()
返回一个Transaction
可用于运行Cypher语句的对象。执行Cypher语句后,必须调用tx.success()
或try-with-resources语句将回滚事务。该Transaction
实现AutoCloseable
。若是事务被标记为成功(经过调用success()
),则提交事务; 不然交易将被回滚。您能够经过调用Transaction
的failure()
方法明确失败交易。
您可能已经观察到Session
和Transaction
类中的run()
方法都返回一个StatementResult
实例。StatementResult
接口能够访问Record
的列表,Record
对象能够有一个或多个Value
对象。
与从JDBC的ResultSet
检索值相似, Record
容许您经过索引或按名称检索值。返回的Value
对象能够经过调用Node.asNode()
方法或原语(如 String
或整数),经过调用其余asXXX()
方法之一转换为Neo4j 。前面几节中的示例主要返回节点,但最后一个示例将一我的的名称做为String
返回。这就是为何该Value
对象在其返回类型中提供灵活性的缘由。
如今咱们将学习到目前为止所学到的知识,并将Java中的示例应用程序组合在一块儿。基于第1部分中的建模和查询示例,此应用程序建立Person
对象,查找全部Person
对象,查找a的全部朋友Person
,并查找Person
已看过的全部电影。
清单1和清单2建立了定义 Person
和a的Java类Movie
。清单3显示了咱们的测试类的源代码:Neo4jClient
。
package com.geekcap.javaworld.neo4j.model;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}复制代码
package com.geekcap.javaworld.neo4j.model;
public class Movie {
private String title;
private int rating;
public Movie() {
}
public Movie(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getRating() {
return rating;
}
public void setRating(int rating) {
this.rating = rating;
}
}复制代码
package com.geekcap.javaworld.neo4j;
import com.geekcap.javaworld.neo4j.model.Movie;
import com.geekcap.javaworld.neo4j.model.Person;
import org.neo4j.driver.v1.*;
import org.neo4j.driver.v1.types.Node;
import java.util.HashSet;
import java.util.Set;
import static org.neo4j.driver.v1.Values.parameters;
public class Neo4jClient {
/**
* Neo4j Driver, used to create a session that can execute Cypher queries
*/
private Driver driver;
/**
* Create a new Neo4jClient. Initializes the Neo4j Driver.
*/
public Neo4jClient() {
// Create the Neo4j driver
driver = GraphDatabase.driver( "bolt://localhost:7687", AuthTokens.basic("neo4j", "neo4j"));
}
/**
* Create a new Person.
* @param person The Person to create
*/
public void createPerson(Person person) {
// Create a Neo4j session. Because the Session object is AutoCloseable, we can use a try-with-resources statement
try (Session session = driver.session()) {
// Execute our create Cypher query
session.run("CREATE (person: Person {name: {name}, age: {age}})",
parameters("name", person.getName(), "age", person.getAge()));
}
}
/**
* Finds all Person objects in the Neo4j database.
* @return A set of all Person objects in the Neo4j database.
*/
public Set<Person> findAllPeople() {
// Create a set to hold our people
Set<Person> people = new HashSet<>();
// Create a Neo4j session
try (Session session = driver.session()) {
// Execute our query for all Person nodes
StatementResult result = session.run("MATCH(person:Person) RETURN person");
// Iterate over the response
for (Record record: result.list()) {
// Load the Neo4j node from the record by the name "person", from our RETURN statement above
Node person = record.get("person").asNode();
// Build a new person object and add it to our result set
Person p = new Person();
p.setName(person.get("name").asString());
if (person.containsKey("age")) {
p.setAge(person.get("age").asInt());
}
people.add(p);
}
}
// Return the set of people
return people;
}
/**
* Returns the friends of the requested person.
*
* @param person The person for which to retrieve all friends
* @return A Set that contains all Person objects for which there is a FRIEND relationship from
* the specified person
*/
public Set<Person> findFriends(Person person) {
// A Set to hold our response
Set<Person> friends = new HashSet<>();
// Create a session to Neo4j
try (Session session = driver.session()) {
// Execute our query
StatementResult result = session.run("MATCH (person: Person {name: {name}})-[:FRIEND]-(friend: Person) RETURN friend",
parameters("name", person.getName()));
// Iterate over our response
for (Record record: result.list()) {
// Create a Person
Node node = record.get("friend").asNode();
Person friend = new Person(node.get("name").asString());
// Add the person to the friend set
friends.add(friend);
}
}
// Return the set of friends
return friends;
}
/**
* Find all movies (with rating) seen by the specified Person.
*
* @param person The Person for which to find movies seen
* @return A Set of Movies (with ratings)
*/
public Set<Movie> findMoviesSeenBy(Person person) {
Set<Movie> movies = new HashSet<>();
try (Session session = driver.session()) {
// Execute our query
StatementResult result = session.run("MATCH (person: Person {name: {name}})-[hasSeen:HAS_SEEN]-(movie:Movie) RETURN movie.title, hasSeen.rating",
parameters("name", person.getName()));
// Iterate over our response
for (Record record: result.list()) {
Movie movie = new Movie(record.get("movie.title").asString());
movie.setRating(record.get("hasSeen.rating").asInt());
movies.add(movie);
}
}
return movies;
}
/**
* Helper method that prints a person set to the standard output.
* @param people The set of Person objects to print to the standard output
*/
public static void printPersonSet(Set<Person> people) {
for (Person person: people) {
StringBuilder sb = new StringBuilder("Person: ");
sb.append(person.getName());
if (person.getAge()>0) {
sb.append(" is " + person.getAge() + " years old");
}
System.out.println(sb);
}
}
/**
* Test methods
*/
public static void main(String ... args) {
Neo4jClient client = new Neo4jClient();
client.createPerson(new Person("Duke", 22));
Set<Person> people = client.findAllPeople();
System.out.println("ALL PEOPLE");
printPersonSet(people);
Set<Person> friendsOfMichael = client.findFriends(new Person("Michael"));
System.out.println("FRIENDS OF MICHAEL");
printPersonSet(friendsOfMichael);
Set<Movie> moviesSeenByMichael = client.findMoviesSeenBy(new Person("Michael"));
System.out.println("MOVIES MICHAEL HAS SEEN:");
for (Movie movie: moviesSeenByMichael) {
System.out.println("Michael gave the movie " + movie.getTitle() + " a rating of " + movie.getRating());
}
}
}复制代码
在Neo4jClient
类在其构造中建立的Neo4j Driver
。而后它的方法使用Driver
来建立一个Session
对象以执行Cypher查询。createPerson()
方法使用“name”和“age”的命名参数执行Cypher查询CREATE (person:Person {...})
。parameters()
方法将这些参数绑定到指定Person
的名称和年龄属性。
findAllPeople()
方法查找Person
数据库中的全部对象。请注意,此方法会返回全部人,所以若是您有不少人,则可能须要向响应中添加LIMIT
。这是一个例子:
MATCH (person:Person) RETURN person LIMIT 25复制代码
在这种状况下,咱们返回完整Person
节点,所以我从Record
中获取“person”并使用Noded
的asNode()
方法来转换。
findFriends()
方法执行相同的操做,但它执行不一样的Cypher查询:
MATCH (person: Person {name: {name}})-[:FRIEND]-(friend: Person) RETURN friend复制代码
咱们要求具备指定名称的人,而后查找该人FRIEND
的关系,找到全部Person
节点,为每一个节点命名为“朋友”。所以,当咱们从Record
中检索响应时,咱们要求“朋友”并将其转换为Node
。
最后,该findMoviesSeenBy()
方法执行如下Cypher查询:
MATCH (person: Person {name: {name}})-[hasSeen:HAS_SEEN]-(movie:Movie) RETURN movie.title, hasSeen.rating复制代码
此查询从指定人员开始,并遵循HAS_SEEN
与Movie
节点的全部关系。而后它返回电影标题属性movie.title
和评级为hasSeen.rating
。
为了作到这一点,咱们必须在咱们的HAS_SEEN
关系中指定一个变量名hasSeen
。由于咱们要求电影标题和评级,咱们从如下各项中单独检索Record:
:
record.get("movie.title").asString()
record.get("hasSeen.rating").asInt()复制代码
该main()
方法建立一个新的Neo4jClient
,建立一个22岁的名为“Duke”(提示:就像Java同样)的Person
。它找到了迈克尔的朋友和他所见过的电影。
清单4显示了Maven pom.xml
文件,咱们用它来构建和运行咱们的应用程序。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.geekcap.javaworld</groupId>
<artifactId>neo4j-example</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>neo4j-example</name>
<url>http://maven.apache.org</url>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Import Neo4j -->
<dependency>
<groupId>org.neo4j.driver</groupId>
<artifactId>neo4j-java-driver</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.geekcap.javaworld.neo4j.Neo4jClient</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>install</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>复制代码
此pom.xml文件导入neo4j-java-driver
依赖项,而后定义三个插件:
com.geekcap.javaworld.neo4j.Neo4jClient
并包含lib
JAR文件目录中的全部文件CLASSPATH
。lib
文件夹中。您如今可使用如下命令构建Neo4j客户端应用程序:
mvn clean install复制代码
您能够target
使用如下命令从目录运行它:
java -jar neo4j-example-1.0-SNAPSHOT.jar复制代码
您应该看到相似于如下内容的输出:
ALL PEOPLE
Person: Steven is 45 years old
Person: Jordyn
Person: Michael is 16 years old
Person: Katie
Person: Koby
Person: Duke is 22 years old
Person: Grant
Person: Rebecca is 7 years old
Person: Linda
Person: Charlie is 16 years old
FRIENDS OF MICHAEL
Person: Charlie
Person: Grant
Person: Koby
MOVIES MICHAEL HAS SEEN:
Michael gave movie Avengers a rating of 5复制代码
务必导航到您的Neo4j Web界面并执行一些查询!您应该看到Duke已建立并可以验证结果。
Neo4j是一个管理高度相关数据的图形数据库。咱们经过回顾图形数据库的需求开始了这种探索,尤为是在查询关系中三个以上的分离度时。在开发环境中使用Neo4j进行设置后,咱们花了大部分时间来了解Neo4j的Cypher查询语言。咱们创建了一个家庭关系网络,并使用Cypher查询了这些关系。咱们在该文章中的重点是学习如何以
在第2部分中,您学习了如何编写链接到Neo4j并执行Cypher查询的Java应用程序。咱们采用最简单(手动)的方法将Java与Neo4j集成。一旦掌握了基础知识,您可能想要探索将Java与Neo4j集成的更高级方法 - 例如使用Neo4j的对象图形映射(OGM)库,Neo4j-OGM和Spring Data。
更多文章欢迎访问: http://www.apexyun.com
公众号:银河系1号
联系邮箱:public@space-explore.com
(未经赞成,请勿转载)