JavaでJanusGraphを組み込む方法

JanusGraphはPythonやJavascriptなど、様々な言語用のドライバを提供しており、それらの言語からGremlinサーバーにアクセスできるようになっているが、DB自体を組み込んだ(Embeddedな)アプリケーションを作ろうと思った場合、選択肢は事実上JavaとGroovyに限られる。

今回はJavaでJanusGraph(with Berkeley DB)を組み込んだアプリケーションの作り方を説明する。

![JanusGraph-article01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235258/d87c9305-b758-5f90-d8cd-883b7a05f614.png)

DBの設定と永続化データはそれぞれディレクトリに保存されるが、それ以外は単体で完結するシンプルなアプリ構成。

## JDKのインストールと環境変数

JanusGraphを動かすだけならJREだけでも良かったが、開発を行うのであればJDKが必要になる。バージョンはJava 8となる。Oracleからダウンロードするとアカウントの登録が必要になるみたいなので、今回は[AdoptOpenJDK](https://adoptopenjdk.net/)を選択した。

URLにアクセスし、1.で`OpenJDK 8 (LTS)`を選択し、2.で`HotSpot`を選択する(両方ともデフォルト)。表示されるファイルの中から、自分のOSに合うJDKをダウンロードする。

私が使用したのはVersion [jdk8u242-b08(8.0.242.08)](https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/tag/jdk8u242-b08)、Windows x64用。

インストール方法は割愛する。インストールしたら、JDKのディレクトリを環境変数JAVA_HOMEとして登録する。私の場合だと以下のようになった。

```bat
JAVA_HOME=c:\Program Files\AdoptOpenJDK\jdk-8.0.242.08-hotspot
```

## Apache NetBeans

開発に使うIDEは実際何でも良いが、ここではApache NetBeansを用いる方法を説明する。Eclipseも試してみたが、どうもすんなりいかなかったので諦めた次第。

### インストール

ダウンロードは以下から行う。

[Apache NetBeans](https://netbeans.apache.org/download/index.html)

バージョンは11.3を選択した(Windows向けにはインストーラが用意されていたので)。なお、環境変数JAVA_HOMEが設定されていないとインストール時にエラーが発生する。

インストーラはすべて標準の設定のまま進めてインストールを完了させた。

### プロジェクトの作成

NetBeansを起動すると、以下のような画面が表示される。

![netbeans_01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235258/909b16ee-56e0-d317-9aae-a3ade96304a3.jpeg)


メニューから「File」-「New Project...」を選択する。

初期状態で`Java with Maven`の`Java Application`が選択されているので、そのまま「Next >」ボタンを押す。

![netbeans_02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235258/1c7a5ccf-9453-854f-a8d5-e720ca155a11.jpeg)


初回だとPlugin等のダウンロードが必要だというメッセージが表示されるので、「Download and Activate」ボタンを押して進める。

次にプロジェクトの名前やディレクトリの場所を設定する。いずれも適当で構わない。全て確認したら「Finish」ボタンでプロジェクト作成を完了する。

![netbeans_03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235258/a59f1a5b-eadd-f247-76ef-8e1e14df89cf.jpeg)

### 依存関係の追加(JanusGraphの参照)

左上の`Project`、`Files`、`Services`と並んでいるところから、`Files`を選択して、プロジェクトに存在するファイルのツリーを表示させる。

![netbeans_04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235258/21f96206-3c9b-eff3-d41a-adb0ec6c0cc3.jpeg)



`pom.xml`をダブルクリックして開く。ここにJanusGraphの依存情報を記載する。

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany</groupId>
    <artifactId>janusexample</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.janusgraph</groupId>
            <artifactId>janusgraph-core</artifactId>
            <version>0.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.janusgraph</groupId>
            <artifactId>janusgraph-berkeleyje</artifactId>
            <version>0.5.1</version>
        </dependency>
    </dependencies>
</project>
```

`<dependencies>`から`</dependencies>`までの間を追加した。`janusgraph-core`はその名の通りJanusGraphのコア部分。`janusgraph-berkeleyje`は組み込みBerkeley DBを使うために必要なライブラリ。その他、目的によって必要なライブラリを適宜追加しよう。

|ライブラリ名(Artifact ID)|機能|
|---|---|
|janusgraph-all|JanusGraph関係のライブラリを全部ひとまとめにしたもの。面倒だったらこれ1つpom.xmlに記述したら全部使える|
|janusgraph-backend-testutils|バックエンドDBのテストコレクション|
|janusgraph-berkeleyje|Berkeley DBをバックエンドにしたグラフDB実装|
|janusgraph-bigtable|Google Cloud Bigtable用ドライバ|
|janusgraph-core|JanusGraphのコアライブラリ群|
|janusgraph-cql|Apache CassandraをバックエンドにしたグラフDB実装|
|janusgraph-dist|配布アーカイブ生成用(我々ユーザーが使うことは恐らくない)
|janusgraph-doc|ドキュメント|
|janusgraph-driver|サーバーに接続するためのドライバ|
|janusgraph-es|ElasticSearchをバックエンドにしたインデクシング実装|
|janusgraph-examples|サンプルコード集|
|janusgraph-hadoop|Apache Hadoopを用いたグラフ解析エンジン|
|janusgraph-hbase-parent|Apache HBase関連のライブラリ|
|janusgraph-inmemory|インメモリのグラフDB実装|
|janusgraph-lucene|Luceneを用いたインデクシング実装|
|janusgraph-server|JanusGraph製のGremlinサーバー実装|
|janusgraph-solr|Apache Solrを用いたインデクシング実装|
|janusgraph-test|テストコード|

### DB設定ファイルの追加

今回はBerkeley DBを組み込むようにしたいのでそのような設定ファイルをプロジェクトに追加する。

ファイルツリーのルートディレクトリに`conf`ディレクトリを作成して、その中にファイル`embedded.properties`を追加する(ファイル名はなんでもよい)。

![netbeans_05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235258/ba9f6f22-c93b-17cd-e405-3341e81a5a1d.jpeg)
![netbeans_06.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235258/480f9737-a199-c537-5066-bdf57471f73e.jpeg)
![netbeans_07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235258/0aded2e9-b565-5685-b927-9ed2ff90f781.jpeg)
![netbeans_08.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235258/9ef599d6-f4a9-0e83-4a3f-e738c150e374.jpeg)

ファイルの中には以下の3行を書き込んでおく。

```ini:embedded.properties
gremlin.graph=org.janusgraph.core.JanusGraphFactory
storage.backend=berkeleyje
storage.directory=../db/berkeley
```

### プログラムの作成

プログラムのソースコードを追加する。`src`ディレクトリの一番深いとこに作成されているプロジェクト名と同名のディレクトリに自前のクラスファイルを作成する。名前は`JanusExample.java`とする。

![netbeans_09.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235258/cd19952f-a41b-a814-d0a5-55b6afbcea9b.jpeg)


コードの内容は最初は以下のようにする。

```java:JanusExample.java
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.janusgraph.core.JanusGraphFactory;

public class JanusExample {
    public static void main(String args[])
    {
        Graph graph = JanusGraphFactory.open("conf/embedded.properties");
        System.out.println(graph.toString());
        graph.close();
    }
}
```

これはグラフを開いて閉じるだけの簡単なサンプル。ビルドはメニューの「Run」「Build Project」(WindowsだとF11キー)。実行は「Run」「Run Project」(WindowsだとF6キー)から行える。初回はMavenがいろいろとダウンロードするのでかなり時間がかかる。実行すると、`db`ディレクトリが生成され、そこにデータが記憶される。

クエリを行うにはGraphTraversalSourceを取得すればよい。戻り値の取得は静的型付け言語なのでなかなか大変だが、うまくやってほしい。

```java
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.janusgraph.core.JanusGraphFactory;

public class JanusExample {
    public static void main(String args[])
    {
        Graph graph = JanusGraphFactory.open("conf/embedded.properties");
        GraphTraversalSource g = graph.traversal();
        // グラフを空にする
        g.V().drop().iterate();
        // 頂点を1個追加する
        g.addV("person").property("name", "bob").property("age", 27).iterate();
        // 変更を確定
        g.tx().commit();
        // 値を取り出すクエリを発行してみる
        List<Map<Object, Object>> result = g.V().valueMap("name", "age").toList();
        // 戻り値は [{name: ["bob"], age: [27]}] が返ってくるはず
        for(Map<Object, Object> vertex : result){
            // vertexは {name:["bob"], age: [27]} のはず
            ArrayList<String> names = (ArrayList<String>)vertex.get("name");  // ["bob"]を得る
            ArrayList<Integer> ages = (ArrayList<Integer>)vertex.get("age");  // [27]を得る
            String name = names.get(0);  //"bob"を得る
            Integer age = ages.get(0);  //27を得る
            System.out.printf("name: %s, age: %s\n", name, age);
        }
        // 頂点数を取り出すクエリを発行してみる
        Long count = g.V().count().next();
        System.out.printf("vertex count is %d\n", count);
        graph.close();
    }
}
```

結果は以下のようになるはず。

```
name: bob, age: 27
vertex count is 1
```

### 注意点:エラー発生時の対応

実行時に例外が発生した場合、プログラムが終了しない状態になっている。このまま再度実行しようとすると、グラフが開けないため、既存のプログラムを終了する必要がある。IDEの右下に×ボタンが表示されているので、押して終了させる。

![netbeans_10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235258/577e8ad7-8111-a0d0-5e72-f80433f543d0.jpeg)


### 使用するクラスの参照

コード中でimportされていないクラスを記述すると、下側に赤線で表示される。この部分にカーソルを合わせて、`Alt+Enter`を押すと、当該クラスをimportすることができる。この機能がないと正直Javaでの開発なんてやっていられない。

![netbeans_11.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235258/acf564e9-1658-59ba-9cc3-a2ea919d23a6.jpeg)


##　おわりに

Javaに慣れていないと辛い所があるが(私自身Javaはド素人なので実際辛かった)、意外と簡単に組み込めてしまうので、使い勝手はかなりいいと思う。

スタンドアロンのアプリにするもよし、クエリの結果をJSONあたりに変換・送信して簡単な自作GraphDBサーバとして運用するもよし。

個人的にはPythonでこんな感じに組み込めたら理想的なのだが……。