グラフデータベースNeo4jを触ってみた

database

社内勉強会でオープンソースの グラフデータベースNeo4jが紹介されていたので触ってみた。

What is a Graph Database?

つながりも含めたグラフとしてデータを扱うデータベース。 データセットのサイズによらず、複雑なつながりや、クエリをうまく扱える。 無数のデータの中から、関係ないデータを見ることなく多数のノードとつながりからなる必要なデータだけを取れる。

インストール

ここからCommunity Editionを選んで OSごとに用意されている実行ファイルをダウンロードしてくる。 ファイルを実行し、Startを押すとブラウザで開けるようになる。

グラフ

グラフは以下の要素から構成される。

  • Node: データそのもので、まとめるためのラベルを複数付けられる
  • Relationships: typeを持つ、Nodeのつながり
  • Properties: NodeやRelationshipsが持てるkey-valueの値

Cypher

Neo4jで使うクエリ言語。

まずはCREATE文でNodeを作る。Personはラベルだ。

CREATE (ee:Person { name: "Emil", from: "Sweden", klout: 99 })

CREATE文では使われていなかった謎のeeだが、MATCH文を見るとデータが格納される変数だということがわかる。 このeeは次のCREATE文でも参照できて、 (ee)-[:KNOWS {since: 2001}]->(js) のように作ったNodeとのRelationshipsを張るのに使っている。

MATCH (ee:Person) WHERE ee.name = "Emil" RETURN ee;

CREATE (js:Person { name: "Johan", from: "Sweden", learn: "surfing" }),
(ir:Person { name: "Ian", from: "England", title: "author" }),
(rvb:Person { name: "Rik", from: "Belgium", pet: "Orval" }),
(ally:Person { name: "Allison", from: "California", hobby: "surfing" }),
(ee)-[:KNOWS {since: 2001}]->(js),(ee)-[:KNOWS {rating: 5}]->(ir),
(js)-[:KNOWS]->(ir),(js)-[:KNOWS]->(rvb),
(ir)-[:KNOWS]->(js),(ir)-[:KNOWS]->(ally),
(rvb)-[:KNOWS]->(ally)

以下のようにパターンマッチもできる。この例だとEmilの友達が取得できる。

MATCH (ee:Person)-[:KNOWS]-(friends)
WHERE ee.name = "Emil" RETURN ee, friends

必ずしも変数に入れなくても良く、() で無視することができる。

MATCH (js:Person)-[:KNOWS]-()-[:KNOWS]-(surfer)
WHERE js.name = "Johan" AND surfer.hobby = "surfing"
RETURN DISTINCT surfer

最短経路を取得するのも簡単。[*1..4] でホップ数に制限をかけたり、範囲を指定したりすることもできる。

MATCH p=shortestPath(
  (bacon:Person {name:"Kevin Bacon"})-[*]-(meg:Person {name:"Meg Ryan"})
)
RETURN p

もちろんSQLのLIMITやORDER BY、GROUP BY相当のこともできる。 文法が直感的に分かりやすいものになっていて良いと思った。

欠点

とにかくSQLのJOIN地獄から脱出できそうなのが大きくて、それだけで採用したい気になってしまうが、 データの総量とは関係なく一つのNodeに大量にRelationshipsが付くと非常に遅くなるという問題があるらしい。

【CyberAgent】技術情報/TechReport - テックレポート/MongoDB de GraphDB | 株式会社サイバーエージェント

なぜかというと隣接Node/RelationshipsのポインタをLinkedListで持っているからみたいだ。

igreque : Info -> Neo4jについてちょちょいと調べたまとめ

電車の路線のように一つのNodeに対して高々いくつかのRelationshipsしかつながらないことが分かっている場合には問題ないので、 そういうモデルを作るか、あるいはデータの数を絞るか、使うにあたって工夫が必要そうだ。