Pythonでグラフデータベース Neo4j入門 for ビギナー (Mac OSX向け)

「Pythonでグラフデータベース Neo4j入門 for ビギナー」とか銘打っておきながら私自身もビギナーなので、すみません。
Pythonで[Neo4j](http://neo4j.com)を操作する環境構築と、データをいじってみるサンプルをご紹介します。

<!--
Youtubeでとてもよい入門となる動画があったので、それをベースにPythonでNeo4jが使える、という触りのところまでをこの記事で書いていきたいと思います。

#0.イントロ#
下記の動画とスライドがわかりやすかったので、Holger Spillさんに許諾を得て、その内容の一部をこの記事で解説しています。なので、もし読んでためになりましたら下記の動画とスライドに「いいね」を押してください！

"Holger Spill: An introduction to Python and graph databases with Neo4j"
https://youtu.be/ps5RtmWKwLY

"Holger Spill: An introduction to Python and graph databases with Neo4j"
https://speakerdeck.com/nzpug/holger-spill-an-introduction-to-python-and-graph-databases-with-neo4j
-->
#1. Neo4jの導入#
まず、Mac OSXにNeo4Jを導入する方法から始めます。私の環境がYosemite 10.10.2なのでもし環境違いで何かエラー等あればコメント等で教えていただけると嬉しいです。

まず、JDKが必要ですが、Macに最初から入っているJavaではうまく動かないそうなので Oracle JDK 7を http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html から導入します。

次に、
http://neo4j.com/download/
にアクセスして、Neo4jのCommunity Edition（無料版）をダウンロードしてください。

neo4j-community-2.2.0-unix-2.tarがダウンロードされるので解凍してフォルダに入ります。

```bash
tar zvf neo4j-community-2.2.0-unix-2.tar
cd neo4j-community-2.2.0
```

そして起動します。

```bash
./bin/neo4j start
```

インストールは非常に簡単ですね！
ブラウザからアクセスできるユーザーインターフェースがあるので、それを使ってみていきます。
アクセス先は、
http://localhost:7474/browser/
です。
最初の起動時にIDとPasswordを設定することを求められるので、適宜設定します。


#2. PythonでNeo4jに接続する#
いくつかNeo4jに接続できるPythonライブラリがあるようですが、ここでは[Neo4jRestClient](http://neo4j-rest-client.readthedocs.org/en/latest/)をつかってPythonからNeo4jに接続することにします。

```bash
pip install neo4jrestclient
```

pipって素晴らしいですね、インストールが本当に簡単！ :satisfied: 

[neo4jRestClientのチュートリアル](http://neo4j-rest-client.readthedocs.org/en/latest/info.html#getting-started)に従って試していきます。

```py
from neo4jrestclient.client import GraphDatabase

url = "http://＜ユーザーID＞:＜パスワード＞@localhost:7474/db/data/"
gdb = GraphDatabase(url)
```
＜ユーザーID＞と＜パスワード＞の部分には、先ほど設定したものを書いてください。
これでPythonからNeo4jに接続されます。

次にNodeを2つ足してみます。aliceとbobの同い年の2人です。

```py
alice = gdb.nodes.create(name="Alice", age=30)
bob = gdb.nodes.create(name="Bob", age=30)
```

実はbobはaliceの事を1980年から知っていたのだけど、aliceがbobを知ったのは3年後だった・・・
というノードを追加すると下記になります。

```py
bob.relationships.create("Knows", alice, since=1980)
alice.relationships.create("Knows", bob, since=1983)
```

ではこれを表示させて見ましょう。（iPython notebookでpython動かしている事を前提として進めます）

ノードとノードの関係を含めて全部表示させるクエリを叩きます。また、data_contents=Trueを設定することがキモで、これをつけないとうまく動きません。（これがわからずにちょっと時間を食いました・・・）

```py
gdb.query("MATCH (n)-[r]-(m) RETURN n, r, m", data_contents=True)
```

![neo4j_001-compressor.png](https://qiita-image-store.s3.amazonaws.com/0/50670/461ec2bf-5db4-627c-993f-0a01d6276a21.png)
グラフが表示された！

これをブラウザ上で見てみるには、 http://localhost:7474/browser/ にアクセスして

```
MATCH (n)-[r]-(m) RETURN n, r, m
```

![neo4j_002-compressor.png](https://qiita-image-store.s3.amazonaws.com/0/50670/68bafa7e-b41d-5153-a0d3-d71287f584cb.png)

を入力してEnterを押すと、
![neo4j_003.png](https://qiita-image-store.s3.amazonaws.com/0/50670/78d1cba2-abee-9fd1-8bfe-6b1253e81cba.png)
グラフが表示されます :grinning: 



#2. データをいじってみる#

先ほどまでのデータを一度削除します。

```py
# All Delete
gdb.query("MATCH (n) OPTIONAL MATCH (n)-[r]-() DELETE n,r", data_contents=True)
```

その上で、3つのNode, Relationshipを追加します。

```py
# Person Nodeの追加
alice = gdb.nodes.create(name="Alice", age=30)
bob   = gdb.nodes.create(name="Bob", age=30)
ken   = gdb.nodes.create(name="Ken", age=35)

alice.labels.add("Person")
bob.labels.add("Person")
ken.labels.add("Person")

# Relationshipの設定
bob.relationships.create("Knows", alice, since=1980)
alice.relationships.create("Knows", bob, since=1983)
alice.relationships.create("Knows", ken, since=2015)
```

Nodeというのはここでいうそれぞれの人のことを指し、RelationshipはAliceはBobを知っているといった関係性のことを指します。

![neo4j_004-compressor-2.png](https://qiita-image-store.s3.amazonaws.com/0/50670/afffbc10-b38b-d108-b857-6f281b89efc6.png)

iPython上で表示のクエリを叩くと、

```py
gdb.query("MATCH (n)-[r]-(m) RETURN n, r, m", data_contents=True)
```

![neo4j_005-compressor.png](https://qiita-image-store.s3.amazonaws.com/0/50670/ba2fd2bc-814a-8ce2-906e-3848a9f5b9b4.png)

同じようなグラフが表示されます :D

次に書くPersonがBlogを持っていて、そのBlogとの関係性をブログの所持者:"Owner",購読者: "Subscribe"として定義してグラフを見てみます。

```py
# Blog Nodeの追加
cam_blog  = gdb.nodes.create(name="Camera Blog")
comp_blog = gdb.nodes.create(name="Computer Blog")
trav_blog = gdb.nodes.create(name="Travel Blog")
gour_blog = gdb.nodes.create(name="Gourmet Blog")

cam_blog.labels.add("Blog")
comp_blog.labels.add("Blog")
trav_blog.labels.add("Blog")
gour_blog.labels.add("Blog")

# Relationの追加
alice.relationships.create("Own", cam_blog)
bob.relationships.create("Own", comp_blog)
ken.relationships.create("Own", trav_blog)

alice.relationships.create("Subscribe", trav_blog)
alice.relationships.create("Subscribe", gour_blog)
bob.relationships.create("Subscribe", cam_blog)
ken.relationships.create("Subscribe", comp_blog)
```

また、ブラウザで http://localhost:7474/browser/ にアクセスして下記のクエリを投げると、

```
MATCH (n)-[r]-(m) RETURN n, r, m
```

こんなグラフが表示されます。なかなか分かりやすいと思います。「グラフデータベースは関係性がリレーショナルデータベースよりもわかりやすい」、と言われるゆえんですねw
![neo4j_006.png](https://qiita-image-store.s3.amazonaws.com/0/50670/c5c818f0-254a-df37-d7dd-e8ee762216d3.png)


#3. Cypherファーストステップ#

Cypherの基本的な書き方について例を幾つか挙げてみようと思います。
<br>
赤丸で示した下記のBob, Alice, Kenが選択されます。

```
match (n:Person) RETURN n
```
![neo4j_007-compressor.png](https://qiita-image-store.s3.amazonaws.com/0/50670/0c7c65a5-122b-a787-6e0d-29a06d6038d8.png)
（※ 赤枠は分かりやすさのために追加したものでNeo4Jのブラウザ画面でこの表示になるわけではありません）

今度はBlogのノードが選択されます。

```
match (n:Blog) RETURN n
```

![neo4j_008-compressor.png](https://qiita-image-store.s3.amazonaws.com/0/50670/356a1825-e117-b370-a8ea-aa4afda91437.png)


このCypherクエリはBobだけを抽出します。

```
MATCH (b:Person {name:'Bob'}) RETURN b
```

![neo4j_009-compressor.png](https://qiita-image-store.s3.amazonaws.com/0/50670/67159302-367f-a911-304f-45184e7c380b.png)

関係も一緒に選択します。下記の場合はOwnの関係があるもののみが対象です。

```
MATCH (p:Person)-[r:Own]->(b:Blog) RETURN p,r,b;
```
![neo4j_011-compressor.png](https://qiita-image-store.s3.amazonaws.com/0/50670/ac1e14bc-d538-ab78-6fe4-3e1deb89fdbb.png)


最後にランダムにPersonノードとBlogノードを100個ずつ生成して、そこに500のSubscribe関係を付与したグラフを生成して表示してみます。

```py
import numpy.random as rd

l = 100

person_list = []
blog_list = []
for i in range(l):
    
    p = gdb.nodes.create(name="person_%d"%i)
    p.labels.add("Person")
    
    b  = gdb.nodes.create(name="Blog_%d"%i)
    b.labels.add("Blog")
    
    person_list.append(p)
    blog_list.append(b)

r1 = range(len(person_list))
rd.shuffle(r1)

for i in range(len(blog_list)):
    blog_list[i].relationships.create("Own", person_list[r1[i]])


r2 = range(l) * 5
rd.shuffle(r2)
r3 = range(l) * 5
rd.shuffle(r3)
                
for i,j in zip(r2, r3):
    person_list[i].relationships.create("Subscribe", blog_list[j])
```
![neo4j_012-compressor.png](https://qiita-image-store.s3.amazonaws.com/0/50670/ea55b344-827f-6d9e-f749-3a277ad1c4be.png)

カオスですね！ :grin: 

#3.Networkxで描画#
iCypherをインストールします。

```bash
pip install ipython-cypher
```

下記のコードでnetworkxでグラフ化できます。

```py:neo4j-networkx.py
%load_ext cypher
%matplotlib inline
import networkx as nx
import matplotlib.pyplot as plt

result = %%cypher http://＜ユーザーID＞:＜パスワード＞@localhost:7474/db/data/ MATCH (n)-[r]-(m) RETURN n,r,m;
node_map ={'Person':'#22FF99', 'Blog': '#6622FF' }
node_label={'Person':'Person', 'Blog': 'Blog' }

g = result.get_graph()
pos=nx.get_node_attributes(g,'pos')
plt.figure(figsize=(15,15))
nx.draw(g, node_color=[node_map[g.node[n]['labels'][0]] for n in g],node_size=80, width=0.5, edge_color='#999999')

```

![neo4j_014-compressor.png](https://qiita-image-store.s3.amazonaws.com/0/50670/ae4317b0-53e4-5a14-e083-67c476c6397a.png)

<!--
#4.Gephiとの連携#
-->