使用Neo4j和Java进行大数据分析 第2部分

本文的第一部分介绍了Neo4j及其Cypher查询语言。若是您已经阅读了第1部分,那么您已经了解了为何Neo4j和其余图形数据库特别受html

社交图形
或网络中用户之间关系建模的影响。您还在开发环境中安装了Neo4j,并概述了使用此数据存储的基本概念 - 即节点和关系。

而后,咱们使用Cypher查询语言对Neo4j中的一个家庭进行建模,包括年龄,性别和家庭成员之间的关系等我的属性。咱们建立了一些朋友来扩大咱们的社交图,而后添加键/值对来生成每一个用户看过的电影列表。最后,咱们查询了咱们的数据,使用图形分析来搜索一个用户没有看到但可能喜欢的电影。java

Cypher查询语言与SQL等传统数据查询语言不一样。Cypher并无考虑像表和外键关系这样的事情,而是强迫您考虑节点,节点之间的天然关系以及各个节点之间能够在各个关系之间进行的各类遍历。使用Cypher,您能够建立本身的心理模型,了解真实世界的实体如何相互关联。须要一些练习来擅长编写Cypher查询,可是一旦你理解了它们的工做方式,即便很是复杂的查询也是有意义的。node

在使用Cypher查询语言对Neo4j中的社交图建模并使用该社交图编写查询后,编写Java代码以对该图执行查询很是简单。在本文中,您将学习如何将Neo4j与Java Web客户端应用程序集成,您可使用它来查询咱们在第1部分中建立的社交图。数据库

设置您的Neo4j项目

咱们的第一步是建立一个新的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驱动程序

接下来,建立一个Neo4j Driver,以下所示:网络

Driver driver = GraphDatabase.driver( "bolt://localhost:7687", AuthTokens.basic("neo4j", "neo4j"));复制代码

GraphDatabase类有一个叫作静态方法driver()接受一个链接Neo4j的URL和AuthToken。您可使用默认用户名和密码“neo4j” 建立基本AuthTokensession

Driver与Neo4j的促进通讯。咱们经过要求Driver建立Session对象来执行查询,以下所示:app

Session session = driver.session();复制代码

Session界面

org.neo4j.driver.v1.Session接口对Neo4j执行事务。在最简单的形式中,咱们能够执行继承自的run()方法。而后,将开始一个事务,运行咱们的语句,并提交该事务。Sessionorg.neo4j.driver.v1.StatementRunnerSessionmaven

StatementRunner接口定义了的几个变型run()方法。这是咱们将使用的那个:

StatementResult run(String statementTemplate, Map<String,Object> statementParameters)复制代码

声明参数

statementTemplate是一个包含咱们的Cypher查询的StringstatementParameters包括咱们将使用的命名参数。例如,咱们可能想要建立具备指定名称和年龄的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()));
}复制代码

最后,若是您正在执行的是要约束到一个单一事务的多条语句,你能够自由地绕过Sessionrun()方法的自动交易管理和明确本身管理的事务。例如:

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()),则提交事务; 不然交易将被回滚。您能够经过调用Transactionfailure()方法明确失败交易。

记录对象

您可能已经观察到SessionTransaction类中的run()方法都返回一个StatementResult实例。StatementResult接口能够访问Record的列表,Record对象能够有一个或多个Value对象。

与从JDBC的ResultSet检索值相似, Record容许您经过索引或按名称检索值。返回的Value对象能够经过调用Node.asNode()方法或原语(如 String或整数),经过调用其余asXXX()方法之一转换为Neo4j 。前面几节中的示例主要返回节点,但最后一个示例将一我的的名称做为String返回。这就是为何该Value对象在其返回类型中提供灵活性的缘由。

Java中的示例应用程序

如今咱们将学习到目前为止所学到的知识,并将Java中的示例应用程序组合在一块儿。基于第1部分中的建模和查询示例,此应用程序建立Person对象,查找全部Person对象,查找a的全部朋友Person,并查找Person已看过的全部电影。

清单1和清单2建立了定义 Person和a的Java类Movie。清单3显示了咱们的测试类的源代码:Neo4jClient

清单1. Person.java

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;
    }
}复制代码

清单2. Movie.java

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;
    }
}复制代码

清单3. Neo4jClient.java

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());
        }
    }
}复制代码

示例app:Neo4j客户端类

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”并使用NodedasNode()方法来转换。

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_SEENMovie节点的全部关系。而后它返回电影标题属性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文件,咱们用它来构建和运行咱们的应用程序。

清单4. Neo4jClient应用程序的Maven POM

<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依赖项,而后定义三个插件:

  • maven-compiler-plugin将Java构建版本设置为1.8。
  • maven-jar-plugin使得主类设置为可执行的JAR文件,com.geekcap.javaworld.neo4j.Neo4jClient并包含libJAR文件目录中的全部文件CLASSPATH
  • maven-dependency-plugin将全部依赖项复制到项目构建目录的lib文件夹中。

构建并运行您的Neo4j客户端应用程序

您如今可使用如下命令构建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已建立并可以验证结果。

第2部分的结论

Neo4j是一个管理高度相关数据的图形数据库。咱们经过回顾图形数据库的需求开始了这种探索,尤为是在查询关系中三个以上的分离度时。在开发环境中使用Neo4j进行设置后,咱们花了大部分时间来了解Neo4j的Cypher查询语言。咱们创建了一个家庭关系网络,并使用Cypher查询了这些关系。咱们在该文章中的重点是学习如何以

图形方式思考
。这是Neo4j的强大功能,也是大多数开发人员掌握的最具挑战性的功能。

在第2部分中,您学习了如何编写链接到Neo4j并执行Cypher查询的Java应用程序。咱们采用最简单(手动)的方法将Java与Neo4j集成。一旦掌握了基础知识,您可能想要探索将Java与Neo4j集成的更高级方法 - 例如使用Neo4j的对象图形映射(OGM)库,Neo4j-OGM和Spring Data。

英文原文:https://www.javaworld.com/article/3269575/big-data-analytics-with-neo4j-and-java-part-2.html

更多文章欢迎访问: http://www.apexyun.com

公众号:银河系1号

联系邮箱:public@space-explore.com

(未经赞成,请勿转载)

相关文章
相关标签/搜索