Python
データベース
neo4j
グラフデータベース

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

More than 3 years have passed since last update.

「Pythonでグラフデータベース Neo4j入門 for ビギナー」とか銘打っておきながら私自身もビギナーなので、すみません。
Pythonで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がダウンロードされるので解凍してフォルダに入ります。

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

そして起動します。

./bin/neo4j start

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

2. PythonでNeo4jに接続する

いくつかNeo4jに接続できるPythonライブラリがあるようですが、ここではNeo4jRestClientをつかってPythonからNeo4jに接続することにします。

pip install neo4jrestclient

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

neo4jRestClientのチュートリアルに従って試していきます。

from neo4jrestclient.client import GraphDatabase

url = "http://<ユーザーID>:<パスワード>@localhost:7474/db/data/"
gdb = GraphDatabase(url)

<ユーザーID>と<パスワード>の部分には、先ほど設定したものを書いてください。
これでPythonからNeo4jに接続されます。

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

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

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

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

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

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

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

neo4j_001-compressor.png
グラフが表示された!

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

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

neo4j_002-compressor.png

を入力してEnterを押すと、
neo4j_003.png
グラフが表示されます :grinning:

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

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

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

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

# 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

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

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

neo4j_005-compressor.png

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

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

# 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

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

Cypherの基本的な書き方について例を幾つか挙げてみようと思います。


赤丸で示した下記のBob, Alice, Kenが選択されます。

match (n:Person) RETURN n

neo4j_007-compressor.png
(※ 赤枠は分かりやすさのために追加したものでNeo4Jのブラウザ画面でこの表示になるわけではありません)

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

match (n:Blog) RETURN n

neo4j_008-compressor.png

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

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

neo4j_009-compressor.png

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

MATCH (p:Person)-[r:Own]->(b:Blog) RETURN p,r,b;

neo4j_011-compressor.png

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

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

カオスですね! :grin:

3.Networkxで描画

iCypherをインストールします。

pip install ipython-cypher

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

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