neo4jで友人関係を表現する

こんにちは。 2回目の登場になります、auスマートパス開発部の松本です。

多様化、複雑化するデータを使ったサービス開発の需要も高まっており、今回は使用感の調査もかねてグラフデータベースを触ってみました。
使用するデータベースは最もポピュラーであろうneo4jです。 書かれている記事も多く、とりあえず触ってみるのにはちょうどいい感じでした。

neo4jにはCypher (サイファー)というめちゃくちゃカッコイイ名前のクエリ言語があり、WEBインターフェースを使ってダイレクトにデータを処理することができます。

今回、何度も初期化を繰り返しながら試行することになるので、データの作成はRubyとneographyを用いることにしました。 初回ということもあり、データをグラフ化することに焦点を置いています。

LINK
* neo4j
* neography

データを作成する

◼接続の確立

まず、接続を確立します。

require 'rubygems'
require 'neography'
@suma = Neography::Rest.new("http://ユーザ名:パスワード@localhost:7474")

◼ノードの作成

neographyのgithubのWikiを見ながら、ノードを追加していきます。 今回は特に使いませんが、プロパティには趣味(hobby)を持たせています。

node1 = @suma.create_node(name: "M君", hobby: "Video game")
node2 = @suma.create_node(name: "A君", hobby: "surfing" )
・
・
・

プロパティはあとから追加、変更できます。

@suma.set_node_properties(node1, {"hobby" => "FootBall"})
@suma.set_node_properties(node2, {"division" => "DevG"})

ノードを削除するのも簡単です。

@suma.delete_node(node1)

◼ラベルの追加

ここで、nodeのラベルに「Engineer」を追加します。 エンジニアをラベルにするかプロパティにするかちょっと悩みました。

  • ラベルにした場合

  「Engineer」テーブルに各ノードが格納されるイメージ

  • プロパティにした場合

  ノードに「職業」という「属性」がつくイメージ

この辺りは用途というか設計次第となるのでしょうが、今回は全員がエンジニアであることと後の抽出作業を考えて、ラベルにしました。

@suma.add_label(node1, "Engineer") 
@suma.add_label(node2, ["Engineer","1G"])  
・
・
・

ノードの登録が終わったところで、Cypherを用いてグラフ化してみます。

合計8名がノードとして登録されています。

◼relationships(関係性)の追加

次に、グラフDBのキモである、relationships(関係性)の記述を行います。 関係性には方向があります。friendsな関係とはいえ、一方通行でないとは限りません。

node2(A君)はnode1(M君)を友達だと思っているので以下のように記述します。

@suma.create_relationship("friends", node2, node1)

この時点では、node2(A君)からnode1(M君)への一方向な関係線が引かれるだけです。
このままでは、友達とは言えません。
node1(M君)も、まあnode2(A君)を友達だと思っているので、引数を逆にして同様の記述を行います。

@suma.create_relationship("friends", node1, node2)

グラフで見てみるとこんな感じです。 node1(M君)とnode2(A君)の間に「friends」が双方向で存在しています。

全てのノードに対して、関係性を記述していきます。
最終的にこんな形になりました。

これでデータの準備ができましたので、いろいろ抽出してみます。

データを抽出してみる

ここまで作成したデータを用いて、いろいろ抽出してみます。

◼A君を取り巻く人間関係は?

A君の周りの全ての関係性を抽出します。

なかなか複雑ですね。

◼A君「が」友人だと思っている人は?

Aくんから出ている「friends」relationshipsのうち、外向きのものを抽出します。(双方向のrelationshipsを含む)

◼A君「を」友人だと思ってくれている人は?

Aくんから出ている「friends」relationshipsのうち、内向きのものを抽出します。(双方向のrelationshipsを含む)

同じ「friends」という関係性でも、方向が違えば見え方も変わってきます。

この他に、「友達だと思っている人の友達」や「サッカーが好きな友達」なども抽出することができます。
「関係性」「方向」が加わることによって、「friends」という単語だけでは表しきれない関係が簡単に抽出できているのがわかると思います。

まとめ

ほんの少し触っただけですが、「方向を表現できる関係性」を持たせられることがすごく便利でした。

「A君の友達の友達で趣味がサッカーの人を検索せよ」。これを例えばMySQLで実装しようとするとちょっと面倒な事になると思います。 (テンポラリ的な「友達テーブル」を作るとか)

◼︎関係性の表現について

今回はfriendsという単一のrelationshipsで方向のみを用いて関係性を表現しました。
ですが、関係性自体にプロパティを記述できるため、単に「知り合い」というrelationshipsを持たせ、そのプロパティに付き合いの深さを記述するやり方も考えられます。
また、方向性ごとに関係性の名前を変えるというやり方もあると思います。

  • 一方通行:知り合い
  • 双方向:friends!

など。

どれが最適なのかは、まだまだ検討をしていく必要がありそうです。
今後は、RDBとの比較などもやってみたいと思います。