RubyでGraphDBごっこ
NoSQLの一つにGraphDBとかいうのがあるんですが、なんかもうJava(というかJVM)に席巻されまくってて他の選択肢が見当たらない勢い。その状況がちょっとさみしかったのと、後はまぁ勉強がてらRubyでそれっぽいものを作ってみました。*1
https://github.com/technohippy/red-grape
なおこれはあくまでもGraphDBのごっこ遊び用なので、それっぽいものを簡単に触ってみたい人だけがターゲットです(そんな需要が本当に存在するかどうかは知りません)。Rubyで本気でGraphDBをやりたい人はNeo4JをJRubyから使うとよろしいのではないでしょうか。
といいつつ以下、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
としても同じグラフが得られます。ただしこの場合はグラフに加えた変更は永続化されません。
ノード(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になるべく合わせています。
その他にも下記のテストでいくつかのパターンを試しているので気が向いたら覗いてみてください。