グラフ処理に特化したDBにNeo4jというのがあります。
Neo4jはオブジェクト同士の関係性を処理するのに特化したDBで、RDBMSでは困難な表現を処理するのに向いています。
例えば、
「高町なのは」は、「高町ヴィヴィオ」の母親で、「フェイト・T・ハラオウン」は「高町なのは」と「高町ヴィヴィオ」の後見人である。
「高町ヴィヴィオ」からみて「高町なのは」と「フェイト・T・ハラオウン」は「母親」である。
といった関係をCSVから読み込んで構成し、データとして配置する事が可能です。
というわけでさっそくテストデータを作成してみました。
■
魔法少女リリカルなのはキャラクターズ csv
■
Neo4jは既に立ち上がっているものとして、実際に上記の CSV を流し込んでみます。
CSVで想定しているのは、
- 声優 -> キャラクター
- キャラクター -> キャラクター
という関係に絞り込んでいます。
まず、登録前にそれぞれのオブジェクトがユニークである事を保証するために、制約を設定します。
制約・キャラクターの重複を抑制
CREATE CONSTRAINT ON (oc:Character) ASSERT oc.name IS UNIQUE
制約・声優の重複を抑制
CREATE CONSTRAINT ON (ov:VoiceActor) ASSERT ov.name IS UNIQUE
制約の設定が終わったら以下の様な感じでCSVを流し込みます。
USING PERIODIC COMMIT 100 LOAD CSV WITH HEADERS FROM "file:///tmp/nanoha_characters.csv" AS rc MERGE (ocs:Character {name: rc.name}) MERGE (oct:Character {name: rc.target_name}) MERGE (ov:VoiceActor {name: rc.voice_actor}) CREATE UNIQUE (ocs)-[:Rel {type: rc.relation}]->(oct) CREATE UNIQUE (ov)-[:Act]->(ocs)
正常に取り込めていれば、
Added 38 labels, created 38 nodes, set 101 properties, created 83 relationships, returned 0 rows in 414 ms
といった表示が戻されるはずです。
ちなみにこんな構造になります。
StrikerS のキャラクターを含めると、掛け持ちする声優が増えるのでもっと面白いグラフになりそう。
問い合わせには、CipherというNeo4j用の言語を使用します。データベースの問い合わせに使用するSQLに相当するものですが、SQLの基本が表の問い合わせを行うのに対して、Cipherはグラフの構造に対して問い合わせを行います。
基本は構造のマッチングによる問い合わせです。
例)
Aさんが持っているXを指している他の人たち。
Aさんの友達の友達。
基本的な問い合わせは、構造の定義(MATCH)を行ってから、条件(WHERE)の指定を行います。SQLで言うところのMATCHがFROM句でWHEREはそのままWHERE句に相当します。(WHERE句に指定する条件も似ています。)
問い合わせ例(その1)
父親のキャラクターは誰が演じていて、どのキャラクターにとっての父親かを検索。
MATCH (ocs)-[r1:Rel]->(oct)<-[r2:Act]-(va:VoiceActor) WHERE r1.type = "父親" RETURN oct.name, va.name, ocs.name
うまく検索出来たようです。
問い合わせ例(その2)
自分が母親だと思っているのに、相手からは娘だと思われていないキャラクターを検索…って、調べるまでもなくフェイトちゃんですが。
MATCH (ocs)-[r1:Rel]->(oct), (ocs)<-[r2:Rel]-(oct) WHERE r1.type = "母親" and r2.type <> "娘" RETURN ocs.name, r1.type, oct.name
クロノくんと恭也さんまで出てきました。
この二人は母親からは「息子」だと思われているので間違いではない(娘ではないから)ですね。
条件が足りなかったようなので、修正。
MATCH (ocs)-[r1:Rel]->(oct), (ocs)<-[r2:Rel]-(oct) WHERE r1.type = "母親" and ( r2.type <> "息子" and r2.type <> "娘" ) RETURN ocs.name, r1.type, oct.name
ちゃんと表示されました。
このままではあんまりなので、ちゃんと母親と娘という関係であるリンディさんが表示されるように検索してみます。
MATCH (ocs)-[r1:Rel]->(oct), (ocs)<-[r2:Rel]-(oct) WHERE r1.type = "母親" and r2.type = "娘" RETURN ocs.name, r1.type, oct.name
そういえば自分自身も母親でした。(劇中では後見人なのでちょっと違うかもしれないけど、フェイトママって呼んでいるため、ここでは母親にしてみました。)
■
データ件数が少ないので、実データを利用しての能力はわかりませんが機能的にはなかなか良さそうな感じです。