使用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

(未經贊成,請勿轉載)

相關文章
相關標籤/搜索