RubyでGraphDBごっこ

NoSQLの一つにGraphDBとかいうのがあるんですが、なんかもうJava(というかJVM)に席巻されまくってて他の選択肢が見当たらない勢い。その状況がちょっとさみしかったのと、後はまぁ勉強がてらRubyでそれっぽいものを作ってみました。*1

https://github.com/technohippy/red-grape

なおこれはあくまでもGraphDBのごっこ遊び用なので、それっぽいものを簡単に触ってみたい人だけがターゲットです(そんな需要が本当に存在するかどうかは知りません)。Rubyで本気でGraphDBをやりたい人はNeo4JJRubyから使うとよろしいのではないでしょうか。

といいつつ以下、Neo4JでもJRubyでもない方の使い方。

インストール

$ gem install red_grape

サーバー起動

$ trevis
  +=================+
  |  +     T     +  |
  | oOo  oOOOo  oOo |
  |  8    oOo    8  |
  |        O        |
Start server: druby://localhost:28282
[Ctrl+C to stop]

コンソール起動

$ redgrape 
         T
       oOOOo
        oOo
-------- O --------
irb(main):001:0> 

コンソールで色々試す

デフォルトグラフを取得する
irb(main):001:0> $store
 => #<RedGrape::GraphStore:0x007fb615137a90> 
irb(main):002:0> $store.graphs
 => [:tinker] 
irb(main):003:0> g = $store.graph :tinker
 => redgrape[vertices:6 edges:6] 

サーバーを起動していればコンソール内では $store でGraphStoreインスタンスにアクセスできます。$store.graphs で保存されているグラフのキー一覧を確認してから $store.graph :tinker でグラフを取得します。tinkerグラフはデフォルトで登録されているテスト用のグラフです。

サーバーを起動するのが面倒ならコンソールだけを立ち上げて

irb(main):001:0> g = RedGrape.create_tinker_graph

としても同じグラフが得られます。ただしこの場合はグラフに加えた変更は永続化されません。

http://markorodriguez.files.wordpress.com/2011/08/pipes-mario-2.png

ノード(Vertex)を確認する
irb(main):004:0> g.V
 => [v[1], v[2], v[3], v[4], v[5], v[6]] 
irb(main):005:0> v1 = g.vertex 1
 => v[1]

g.Vでグラフ内のノード一覧を表示できます。v[id]という形式で表示されるので、idを確認してvertexメソッドに渡すとノードを取得できます。

irb(main):006:0> v1.property_keys
 => ["name", "age"]
irb(main):007:0> v1.name
 => "marko"

ノードのプロパティはproperty_keysで確認でき、その値はメソッド呼び出しの形で取得できます。

エッジ(Edge)を確認する
irb(main):008:0> g.E
 => [e[7][1-knows->2], e[8][1-knows->4], e[9][1-created->3], e[10][4-created->5], e[11][4-created->3], e[12][6-created->3]] 

g.Eでグラフ内のエッジをe[(エッジID)][(ノードID)-(ラベル)->(ノードID)]という形式で表示できます。
今回の例ではノード1から3つのエッジが出ていることが分かります。

irb(main):009:0> v1.out
 => [v[2], v[4], v[3]]
irb(main):010:0> v1.out 'created'
 => [v[3]]

outメソッドでノードから繋がっているノード一覧を取得できます。また、outメソッドにラベルを渡すとそのラベルを持つエッジの先にあるノードだけを取得することもできます。

ノードを作成する
irb(main):011:0> g.add_vertex 10, name:'gachapin', age:5
 => v[10]
irb(main):012:0> g.v(10).name
 => gachapin

ノードIDとプロパティを指定してノードを作成できます。

ノードを繋ぐ
irb(main):013:0> g.add_edge 13, 1, 10, 'unknown'
 => e[13][1-unknown->10] 
irb(main):014:0> v1.out
 => [v[2], v[4], v[3], v[10]] 

エッジIDとエッジに繋がるノード、ラベルを指定してエッジを作成できます。
ノードやエッジの作成方法がIDを自分で指定したりしててダサいですが、とりあえずtinkerpopのblueprintsに合わせてあるつもりです。

グラフを保存
irb(main):015:0> $store.put_graph :tinker, g
 => redgrape[vertices:7 edges:7] 

put_graphすると変更がサーバーに保持されます。

より複雑な探索を試す

一応GraphDBごっこを目的としているので、もう少し複雑なこともできます。

例えば、次のようにすると全ノードの中から30歳以上(age > 30)のノードを知っている(knows)ノードを取得できます。

irb(main):016:0> g.V.out('knows').has('age', :gt, 30).back(2)
 => [v[1]]

例えば、ノード1から2ステップで辿れるノードのうち、1ステップで辿れるものを除外するには次のようにします。

irb(main):017:0> g.v(1).out.aggregate(:x).out.except(:x)
 => [v[5]]

トラバーサル用のメソッドはGremlinになるべく合わせています。

その他にも下記のテストでいくつかのパターンを試しているので気が向いたら覗いてみてください。

*1:なにげにRubyのブランクが長くてリハビリも兼ねてるのでいろいろと粗いのはご容赦いただきたく