转载

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

本文的 第一部分 介绍了Neo4j及其Cypher查询语言。如果您已经阅读了第1部分,那么您已经了解了为什么Neo4j和其他图形数据库特别受 社交图形 或网络中用户之间关系建模的影响。您还在开发环境中安装了Neo4j,并概述了使用此数据存储的基本概念 - 即节点和关系。

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

Cypher查询语言与SQL等传统数据查询语言不同。Cypher并没有考虑像表和外键关系这样的事情,而是强迫您考虑节点,节点之间的自然关系以及各个节点之间可以在各个关系之间进行的各种遍历。使用Cypher,您可以创建自己的心理模型,了解真实世界的实体如何相互关联。需要一些练习来擅长编写Cypher查询,但是一旦你理解了它们的工作方式,即使非常复杂的查询也是有意义的。

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

设置您的Neo4j项目

我们的第一步是创建一个新的Maven项目:

mvn archetype:generate -DgroupId=com.geekcap.javaworld -DartifactId=neo4j-example -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

打开您的 pom.xml 文件并添加Neo4j驱动程序,在撰写本文时版本为1.4.1:

<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” 创建基本 AuthToken

Driver 与Neo4j的促进通信。我们通过要求 Driver 创建 Session 对象来执行查询,如下所示:

Session session = driver.session();

Session界面

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

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 并包含 lib JAR文件目录中的所有文件 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。

原文  http://www.apexyun.com/shi-yong-neo4jhe-javajin-xing-da-shu-ju-fen-xi-di-2bu-fen/
正文到此结束
Loading...