Javaでグラフデータベース:Neo4jを操作する（セットアップ〜読み書きまで）

会社の勉強会でグラフデータベース（以下グラフDB）を紹介され、興味を持ったので実践してみました。全然知らない領域だったので用語等を整理しつつ、下記の3ポイントを紹介してみます。

* グラフDBの概要
* Neo4jのセットアップ
* Neo4jの初期化〜 簡単な読み書き

グラフDBの真髄とも言える探索機能については書ききれなかったのでまたの機会に書きます。

# グラフDBについて

まずグラフDBとは何か。ググりましたが日本語版Wikipediaのページ無し！

> In computing, a graph database is a database that uses graph structures for semantic queries with nodes, edges and properties to represent and store data. A graph database is any storage system that provides index-free adjacency.
>
[Graph database - Wikipedia, the free encyclopedia -](http://goo.gl/Ua4Nd)

特徴はざっとこんなところでしょうか。

* 数学の一分野であるグラフ理論をベースとしている
* ノード（頂点）とエッジ（関係）を構成要素とするNoSQLデータベースの一つ
* 関係性の表現、ノード間のトラバース（探索）が容易
* また、ノード・エッジはそれぞれプロパティ（属性）を持つことができる。

計算機科学を専攻された方には「グラフ理論」は馴染み深いものかもしれませんが、「グラフ」と聞いて「図表」を想起した僕のような人間は一度Wikipediaのグラフ理論のページを見てみると良いと思います（だいぶ参考になりました）。
（[ダイクストラ法](http://goo.gl/Vttz)アルゴリズムは有名なので聞いたことがあると思います）

> グラフ理論（グラフりろん、英: graph theory）は、数学の一分野。ノード（節点・頂点[1]）の集合とエッジ（枝・辺[2]）の集合で構成されるグラフの性質について研究する学問である。なお「エッジ」をリンク[3]という場合もある。コンピュータのデータ構造、アルゴリズムなどに広く応用されている。  
[グラフ理論 - Wikipedia -](http://goo.gl/PJeB)

こうしたグラフ構造はRDBでも実現できますが、それでもグラフDBが選ばれる理由があります。

1. RDBでエンティティ間のリレーションシップを辿る≒結合の繰り返しになるのでクエリが複雑になったりパフォーマンスの低下が生じがち
1. FacebookやTwitterに代表されるように、リレーションシップをコアとするアプリケーションでのデータ管理や表現への注目の高まり

こうした背景から局所最適化の為にグラフDBの導入が近年進んでいるといわれる次第です。

# グラフDB製品

## **Neo4j**

グラフDBにもいくつかの製品があり、Neo4jはそのうちの一つです。
[neo4j オープンソース nosql グラフデータベース »](http://goo.gl/8UOZbx)

会社の先輩が使っていたのでNeo4jを選んで試してみたわけですが、そのほかにも下記の理由で興味が湧きました。

* [『7つのデータベース 7つの世界』](http://www.amazon.co.jp/gp/product/4274069087?ie=UTF8&camp=1207&creative=8411&creativeASIN=4274069087&linkCode=shr&tag=ohbarye-22&qid=1429705748&sr=8-1&keywords=7%E3%81%A4%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9)で紹介されていた
* 比較的ウェブで情報を多く見かける
* オープンソース

## **TinkerPop**

グラフDBを利用する為のフレームワーク、製品群です。
各プロダクトにキャラクターがいるのですが、全体的にややキモいので必見です。
[TinkerPop](http://goo.gl/zpCCC)

今回はこの中の**BluePrints**のみを使ってグラフDBへのアクセスや読み書きを行います。

## **BluePrints**

各グラフDBに対するインタフェース、APIを提供するソフトウェアです。
複数のグラフDBに対応しています。


# Neo4jを使ってみる

前置きが少し長くなりましたが、Neo4jのセットアップと読み書きを試してみます。

## セットアップ

とりあえず試すだけであれば、JavaからNeo4jにアクセスするのに必要な依存ライブラリの追加だけでOKです。Neo4jのインストールやミドルウェアとしての設定も不要です。

### 依存ライブラリの追加

**blueprints-core**と使用するグラフDB用のライブラリを追加します。
Neo4j用は**blueprints-neo4j-graph**です。

手元でちょうどPlay Frameworkを触ってたのでSBTで追加します。
無論、その他のどんなやり方で追加しても構いません。

```scala:build.sbt
libraryDependencies ++= Seq(
  "com.tinkerpop.blueprints" % "blueprints-core" % "2.6.0",
  "com.tinkerpop.blueprints" % "blueprints-neo4j-graph" % "2.6.0"
)
```

## DB初期化

「面倒なインストールが不要なのは良いけどDBの初期化とかどうする？」と思うかもしれませんが、BruePrintsにはNeo4jのライブラリが同梱されているので、こんな風に書くだけで勝手にやってくれます。

```java:GraphDB.java
package app;

import com.tinkerpop.blueprints.Graph;
import com.tinkerpop.blueprints.impls.neo4j.Neo4jGraph;

public class GraphDB {

	public static void main(String[] args) {
		// initialize / open DB
		Graph g = new Neo4jGraph("data/graphdb");
		// shutdown DB
		g.shutdown();
	}
}
```

これを実行するだけで`data/graphdb`ディレクトリにNeo4jの管理ファイル一式が作成されます。
ちなみに`new Neo4jGraph`時にロックファイルを生成するので、`shutdown`しないとロックファイルが残り続けて次回の実行時に失敗します。

## write / read

RDBで言う`INSERT`と`SELECT`を見てみましょう。
さっきの`GraphDB.java`に`addData`と`showData`を追加してみます。

```java:GraphDB.java
package app;

import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Graph;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.impls.neo4j.Neo4jGraph;

public class GraphDB {

	public static void main(String[] args) {
		Graph g = new Neo4jGraph("data/graphdb");
		
		addData(g);
		showData(g);
		
		g.shutdown();
	}
	
	/**
	 * Add Node and Edge
	 * @param g
	 */
	public static void addData(Graph g) {
		Vertex v1 = g.addVertex(1);
		Vertex v2 = g.addVertex(2);
		
		v1.setProperty("name", "Jonathan Joestar");
		v2.setProperty("name", "Elina Pendleton");
		
		Edge e1 = g.addEdge(1, v1, v2, "similar");
		e1.setProperty("marriage", 1889);
	}
	
	/**
	 * show graph data
	 * @param g
	 */
	public static void showData(Graph g) {
		for (Vertex v : g.getVertices()) {
			System.out.println("Vertex id = " + v.getId().toString()
					+ "  property 'name' = " + v.getProperty("name"));
		}

		for (Edge e : g.getEdges()) {
			System.out.println("Edge id = " + e.getId().toString()
					+ "  property 'name' = " + e.getProperty("since"));
		}
	}
}
```

コードを読むとわかる通り、`addData`では2つの`Vertex:ノード（頂点）`と２点を繋ぐ1つの`Edge:エッジ（辺）`を追加し、それぞれに`property:属性`をセットしています。

`setData`では全ノード/全エッジを取得し、その属性を表示しているだけです。

```text:実行結果
Vertex id = 1 property 'name' = Jonathan Joestar
Vertex id = 2 property 'name' = Elina Pendleton
Edge id = 0 property 'marriage' = 1889
```

ちょっと雑なデータかもしれませんが、これで"`Jonathan Joestar`と`Elina Pendleton`が`1889`年にmarriageしたという関係"をDBに保存できました。

このノードとエッジが集積していくことでソーシャルグラフが書けるようになったり、グラフDBの目的である高速探索ができるようになっていくわけですが、長くなってしまったのでその方法は別の機会に紹介したく思います。

# セットアップから簡単な読み書きを行ってみての所感

## 意外な利点

Neo4jをここまで触って、下記のような利点に気付きました。

* サーバ構築不要による高速な立ち上がり
DBとはいうもののサーバ構築やサービス起動などの一般的なミドルウェア追加時の作業を行わなくても上記のコードだけで扱うことができます。
※ もちろん、サーバとして起動して外部からのアクセスを待ち受けたりもできるとのこと。

* 移植性の高さ
実は`new Neo4jGraph(“data/graphdb”)`で指定したディレクトリを移動するだけで別環境への移植ができます。データファイルもまとめてバージョン管理するのもありだと思います。

ここまで軽量だと気軽に導入できそうで魅力的ですね。
NoSQL選択時の課題であるRDBとの併用の実現も、比較的容易な方ではないでしょうか。

## 情報が少ない

グラフDBは未だメジャーとは言い難い分野のようです。Google検索でヒットする件数も少なく、日本語の情報は特に少なく感じました。（Qiitaにもあまり投稿されていないですね…）

## 他の言語でNeo4jを扱えるか？

Javaの記事で言うのもなんですが他の言語でも扱えるかは気になりますよね。

ちゃんと調べていませんが、とりあえず[PythonにもNeo4jを操作するライブラリがある](http://qiita.com/kenmatsu4/items/f75616461dc5c63ecfe1)ようです。ただNeo4j自体がJavaで書かれていることもあり、とりあえずはJavaで試してみた方が情報も多くて変なハマりどころが少ないのではないか…と個人的には思います。


グラフDB本来のメリットを実感するところまで書けず仕舞いですが、今度は探索についても書いてみたく思います。