Image
Top
Navigation

Graph API

Java APIを使ってGraph データベースの操作

それでは次にJava API を使用してGraphデータベースを操作してみましょう。

今回は Java API を使用したグラフデータベースの操作について紹介します.

使用する環境は以下のとおりです

・Java(TM) SE Runtime Environment (build 1.7.0_67-b01)
・OrientDB 1.7.8

1.0 準備

Graph APIを使用するためには、以下のライブラリにクラスパスを通す必要があります。

orient-commons-*.jar
orientdb-core-*.jar
blueprints-core-*.jar
blueprints-orient-graph-*.jar / orientdb-graphdb-*.jar (depending on version)

リモートで接続する場合は以下のJarも追加してください。

orientdb-client-*.jar
orientdb-enterprise-*.jar

また TinkerPop Pipes tool を使用する場合は以下を。

pipes-*.jar

TinkerPop Gremlin を使用する場合は以下を追加してください。

gremlin-java-*.jar
gremlin-groovy-*.jar
groovy-*.jar

1.0 Graphデータベースへの接続

バージョン1.7以降ではデータベースへの接続にはOrientGraphFactoryの使用を推奨しています。
ちなみに、それまでのバージョンではOrientGraphから直接インスタンスを作成する方法でした。

// for 1.7
// AT THE BEGINNING
OrientGraphFactory factory = new OrientGraphFactory("plocal:C:/temp/graph/db").setupPool(1,10);

// EVERY TIME YOU NEED A GRAPH INSTANCE
OrientGraph graph = factory.getTx();
try {
  ...

} finally {
   graph.shutdown();
}

参考までに1.7以前の場合です。

 'OrientGraph graph = new OrientGraph("plocal:C:/temp/graph/db");
import com.tinkerpop.blueprints.impls.orient.OrientGraphFactory;

OrientGraphFactory factory = new OrientGraphFactory("plocal:D:/DB/orientdb/databases/sample1" , "admin" , "admin");

OrientGraphFactory はコンストラクタをみてみると以下の2つが存在しています。

public OrientGraphFactory(String iURL) 
public OrientGraphFactory(String iURL, String iUser, String iPassword)

iUser と iPassword なしのコンストラクタはどういった時に使うのか?と思い、実装をみてみると自動的に admin admin をセットしているようですね。

2.0 Graphデータの登録

Java API からの Vertex(ノード)の登録

今回のデータベースはTwitterのTweetとユーザーのデータをそれぞれノードとして登録し、それにリレーションを付与します。
まずは Java API を直接利用します。


//tweetにはidとTextとCreatedAt、FavoriteCountが存在しそれぞれのアクセサメソッドが存在

//ノードへ Tweet class を作成
    Vertex tweetVertex = graph.addVertex("class:Tweet");
    tweetVertex.setProperty("id", tweet.getId());
    tweetVertex.setProperty("text", tweet.getText());
    tweetVertex.setProperty("created_at", tweet.getCreatedAt());
    tweetVertex.setProperty("favorit", tweet.getFavoriteCount());

同様にUserも追加します。


    Vertex userVertex = graph.addVertex("class:User");
    userVertex.setProperty("name", tweet.getScreenName());
      ...

これで2種類ノードが追加されました。

Edge(リレーション)の登録

それでは次には addEdge を使用して2つノード間にリレーションを作成します。

     Edge tweetEdge = graph.addEdge(null, userVertex , tweetVertex , "post");

これだけで作成できてしまいました。

SQLを使って Vertex(ノード)の登録

次に同じことSQLコマンドで試してみましょう。
まずInsert文に関してですがOrientDBでは通常のInsert 文と若干ことなる記法の Insert 文が書けます。

下記をみていただければ分かる通り、UPDATE 文の構成と似た構文になってるんですね。
なんか地味に好きです、こういうの。

Insert 文によるVertex(ノード)の登録


-- SQL92
insert into Profile (id, name, surname) values (id, 'Jay', 'Miner' )

-- OrientDB での追加構文
insert into Profile SET id = 1234, name = 'Jay', surname = 'Miner'

UPSERT 文を試してみよう

今回は UPSERT 文という OrientDB 独自の SQL を使用します。

UPSERT 文は、WHERE 句で指定した条件のレコードが存在するのであればUPDATE、存在しないのであれば Insert という挙動をするSQLをです。

UPSERT 文例です。


UPDATE Profile SET id= 'a1234', name = 'Jay', surname = 'Miner' UPSERT WHERE id = 'a1234'

プログラムの中で実行してみる

それでは実際にプログラム内で実行してみます。

  String upsertSql = "UPDATE Profile SET id= 'a1234', name = 'Jay', surname = 'Miner' UPSERT WHERE id = 'a1234'";
  graph.command(new OCommandSQL(upsertSql)).execute();

OrientDBではSQLの実行の際には変数を動的にバインドする動的SQL(Prepared Query)が利用できます。
上記のSQLの変数を置き換えてみましょう。


//バインド変数として ? をセット
  String upsertSql = "UPDATE Profile SET id= ?, name = ?, surname = ? UPSERT WHERE id = ?";

//SQLへセットする値。? の順番に合わせて配列を作成する。
  Object[] ages = { 'a1234', 'Jay' , 'Miner',  'a1234' };

//executeの引数にagesをセット
  graph.command(new OCommandSQL(upsertSql)).execute(ages);

Return句

さて、これでJava APIと同様にSQLを使ってノードを作成することができました。
ただ、Java APIを利用した場合は

    Vertex tweetVertex = graph.addVertex("class:Tweet");

といった形で Vertex のインスタンスをスコープ内で保持していますのでそのままaddEdge()に利用することが可能でした。

SQLコマンドで実行した場合はどうするのかというと、OrientDB では SQL に RETURN 句というのが存在し、それを利用することによりUPDATE後の返り値を、更新件数もしくは更新前後のレコードのいずれかを取得することができます。


// UPSERT の RETURN AFTER を追加
  String upsertSql = "UPDATE Profile SET id= ?, name = ?, surname = ? UPSERT RETURN AFTER WHERE id = ?";

//SQLへセットする値。? の順番に合わせて配列を作成する。
  Object[] ages = { 'a1234', 'Jay' , 'Miner',  'a1234' };

//IterableなVertexが返却される
 Iterable<Vertex> result =  graph.command(new OCommandSQL(upsertSql)).execute(ages);

//1件
Vertex v = result.iterator().next();

4.0 サンプルプログラム

無事SQL コマンドでも無事にVertexの取得ができました。それでは上記の組み合わせでサンプルプログラムを書いてみましょう。

Twitterから指定したキーワードでツイートを取得し、ユーザーの情報とツイートの内容に分解します。
それぞれをノードとして登録かつそれらをリレーションで結びます。

本題のOrientDBからずれてしまうのでTwitterへのアクセス周りの解説は無しですが、twitter4jという素晴らしいライブラリを利用しているので非常に簡単です。

それではソースのなかにコメントの形式で説明を書いていきます。

ここにコードを入れます
package sample;

import java.util.Map;

import twitter4j.Query;
import twitter4j.QueryResult;
import twitter4j.RateLimitStatus;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.User;
import twitter4j.auth.OAuth2Token;
import twitter4j.conf.ConfigurationBuilder;

import com.orientechnologies.orient.core.sql.OCommandSQL;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.impls.orient.OrientGraphFactory;
import com.tinkerpop.blueprints.impls.orient.OrientGraphNoTx;

public class GraphTest{

    public static void main(String[] args) throws TwitterException {
        // Twitter4Jのセットアップ
        ConfigurationBuilder builder = new ConfigurationBuilder();
        builder.setApplicationOnlyAuthEnabled(true);
        builder.setJSONStoreEnabled(true);
        Twitter twitter = new TwitterFactory(builder.build()).getInstance();

        // exercise & verify
        twitter.setOAuthConsumer("あなたのキーで", "あなたのキーで");
        OAuth2Token token = twitter.getOAuth2Token();

        // Search for tweets that
        Query query = new Query("orientdb");

        query.setCount(100);
        query.setLang("en");
        QueryResult tweets = twitter.search(query);

        insertOrientDB(tweets);
    }

    private static void insertOrientDB(QueryResult result) {
        //ここからOrientDBの操作を開始

        OrientGraphFactory factory = new OrientGraphFactory("plocal:C:/temp/graph/db3").setupPool(1, 10);

        //今回はトランザクションを利用しない形で利用してみます
        OrientGraphNoTx graph = factory.getNoTx();


        //TweetとUser の2つのクラスのノードを作成します。
        try {
            graph.createVertexType("Tweet");
            graph.createVertexType("User");

            // 取得したTweetをループしながら処理します

            for (Status status : result.getTweets()) 
            {
              // Step 1. Tweet ノードの作成

                // Tweet用の UPSERT 文を作成
                String upsertTweetSql = "UPDATE Tweet SET id=?, text=?,created_at=? UPSERT RETURN AFTER WHERE id=?";
                // バインド変数への値を配列化
                Object[] ages1 = { status.getId(),
                        status.getText().replaceAll("'", "\\,"),
                        status.getCreatedAt(), status.getId() };

                // SQLを実行する。返り値として変更後のノードを取得する。
                Iterable<Vertex> result1 = graph.command(new OCommandSQL(upsertTweetSql )).execute(ages1);

                Vertex tweetVertex = result1.iterator().next();

              // Step 2. User ノードの作成

                // Tweetからユーザーの情報を取得
                User user = status.getUser();

                // User 用の UPSERT 文を作成
                String upsertUserSql = "UPDATE User SET id= ? , screen_name= ? , ,image=? UPSERT RETURN AFTER WHERE id= ?";

                // バインド変数への値を配列化
                Object[] ages2 = { user.getId(), user.getScreenName(),
                        user.getProfileImageURL(), status.getId() };

                Iterable<Vertex> result2 = graph.command(new OCommandSQL(upsertUserSql )).execute(ages2);
                // SQLを実行する。返り値として変更後のノードを取得する。

                Vertex userVertex = result2.iterator().next();


             // Step 3. User から Tweet へのリレーションを作成
             // addEdgeを利用し2つのノードに対して"post"というプロパティを設定したリレーションを作成

                Edge postEdge = graph.addEdge("class:Tweet", userVertex, tweetVertex , "post");
            }
            System.out.println("end");

        } catch (Exception e) {
            System.err.println(e);
        }finally{

    factory.close();
         factory.shutdown();
                }
    }

}