今、Neo4jを調査しているのでメモ。
Neo4jはグラフDBとよばれるNoSQL系のデータベースです。Twitterのフォロー関係のような、有向グラフ構造を保存できます。
Neo4jはJavaからは組み込みで利用できますが、その他ネイティブ系のバインディングが全然ありません。そのかわり、RESTによるJSON/HTTPのインターフェイスが用意されています。
ここではRESTサーバーとRubyクライアントのセットでNeo4jを利用する例を紹介します。
サーバーのインストール
Neo4jサーバーをコマンドラインから起動するだけなら、ダウンロードページから落としてきて起動するだけ。
サービスに登録するためのコマンドも用意されています。
sudo ./bin/neo4j install
サーバー起動
$ cd NEO4J_HOME $ bin/neo4j start
以下のURLからWebの管理画面を表示できます。
http://localhost:7474/webadmin
サーバー終了
$ bin/neo4j stop
データの削除
以下のフォルダを削除すると、全てのデータを消去できます。
NEO4J_HOME/data/graph.dbただし、サーバーが停止しているときに消さないといけないみたいです。消したつもりが復活したりしました。
データ削除インターフェイスを追加するアドオンとかも見つけましたが、1.5までの対応のようです。(執筆時点でのNeo4j最新は1.6。無理矢理動くかは未検証)
https://github.com/jexp/neo4j-clean-remote-db-addon
ドキュメント
RESTインターフェイスについては以下にドキュメントがあります。
http://docs.neo4j.org/chunked/milestone/rest-api.html
ただ、そこまで充実しているわけではありません。 Java APIも含め、全体を網羅したPDFもありました。
http://docs.neo4j.org/pdf/neo4j-manual-stable.pdf
Neography
僕はRubyから利用したいので、NeographyというNeo4jのRESTクライアントを使います。
https://github.com/maxdemarzi/neography
インストールはgemで。
gem install neography
Neography以外にも、RailsのActiveModelのように動作するものとして、以下のようなものがあるようです。
- neology: Neographyのラッパー
- architect4r: よりActiveModelに近いが、クエリーのみ実装されていて、index等はサポートしない
traverse
ノードやリレーションの作成などのシンプルな操作はgithubのサンプルを見てもらうとして、グラフをトラバースする方法についてメモしておきます。ちょうどneographyを使ったfacebookの友達サジェストのサンプルがありました。
https://github.com/maxdemarzi/neography/blob/master/examples/facebook.rb
サジェストする部分を見てみます。
def suggestions_for(node) @neo.traverse(node, "nodes", { "order" => "breadth first", "uniqueness" => "node global", "relationships" => { "type" => "friends", "direction" => "in" }, "depth" => 2, "return filter" => { "language" => "javascript", "body" => "position.length() == 2;" } }) end
最初の引数nodeはグラフ走査の起点です。第二引数"nodes"はノードの走査をする、ということを指定しています。 第三引数のオプションとして、それなりの指定があります。
orderでは幅優先"breadth first"を指定しています。他に深さ優先"depth first"が指定できます。
return filterでは、検索条件を指定します。ここではnodeから2ホップのノード(友達の友達)を返すように指定しています。
depthでは、トラバースする際の最大の深度を指定しています。
Cypher
Neo4j特有のクエリー言語として、Cypherがあります。
http://docs.neo4j.org/chunked/snapshot/cypher-query-lang.html
neo.execute_queryというメソッドでCypherクエリーを実行できます。 たとえば先ほどの友達サジェストは、以下のように書けます。
require 'rubygems' require 'neography' @neo = Neography::Rest.new def create_person(name) @neo.create_node("name" => name) end def make_mutual_friends(node1, node2) @neo.create_relationship("friends", node1, node2) @neo.create_relationship("friends", node2, node1) end def suggestions_for(node) n = Neography::Node.new node q = "START n=node(#{n.neo_id}) MATCH n -[:friends]-> f -[:friends]-> t WHERE not(t.name = n.name) RETURN t.name" result = @neo.execute_query(q) result["data"].map { |f| f.first } end johnathan = create_person('Johnathan') mark = create_person('Mark') phill = create_person('Phill') mary = create_person('Mary') luke = create_person('Luke') make_mutual_friends(johnathan, mark) make_mutual_friends(mark, mary) make_mutual_friends(mark, phill) make_mutual_friends(phill, mary) make_mutual_friends(phill, luke) puts "Johnathan should become friends with #{suggestions_for(johnathan).join(', ')}"結果:
Johnathan should become friends with Mary, Phill
ここで実行しているクエリーはこんな感じです。
START n=node(#{n.neo_id}) MATCH n -[:friends]-> f -[:friends]-> t WHERE not(t.name = n.name) RETURN t.name
traverseの例とちょっと論理的に同等とは言えないかもしれませんが(同値チェックのあたり)、この例ではうまく動きます。 Cypherではこのように、STARTで起点、MATCHで検索条件、WHEREでフィルター条件、RETURNで戻り値を記述します。MATCHの記法はなかなか強力で、a -[:link]-> t <-[:link]- bのように、逆向きのリレーションも扱えます。
その他
TinkerPopというところが、グラフDBのためのソフトウェアスタックを用意しています。
グラフのトラバースなどに特化したクエリー言語Gremlinや、RESTインターフェイスのRexsterなどがあります。 本当ならNeo4jを直に使うよりこちらを経由した方が良いと思うのですが、ちょっとまだこなれてない感じがしました。
今日のところは、とりえあえずここまで。