Neo4jで遊びながらここ最近のドキドキ!プリキュアを振り返ってみよう

by Yuji Yamamoto on December 22, 2013

Tagged as: Anime, Precure, Neo4j, GraphDB.

この記事は「プリキュア Advent Calendar 2013」22日目の記事です。 書いている時点ですでに日付を超えてしまいました…。あしからず。

21日目の記事は @speedmod さんの 「プリカラ部♪と部長のこと #cure_advent」でした。

そういやうちの近くにもプリキュアソングがムービー付きで歌えるカラオケありましたねー。

今回やってみたこと

みなさんに感化され、私もエンジニアらしいことをしたいなぁ、と思い、 最近気になっているNeo4jを使って、 ドキドキ!プリキュアの最近のお話を振り返ってみました。

性質上、 ネタバレを含んでいます ので、まだ最近のお話をご覧になっていない方はご注意ください。 また、私自信Neo4jを使い始めたばかりなので、使用方法など間違った点もあるかもしれません。 その時はそっとご指摘してください。

Neo4jって何?

GraphDBという、データベースマネジメントシステムの一種です。 みなさんおなじみのMySQLやPostgreSQLなどと言ったRDBとは異なり、 データを「属性(プロパティ)」を持った「点(ノード)」や「辺(リレーション)」として表現します。

まぁ、詳しい説明は公式サイトか、 日本語の情報であれば『グラフデータベース「Neo4j」の 導入の導入』などをご覧ください。

今回はNeo4jのチュートリアルをお手本に、 最近のドキドキ!プリキュアの話をNeo4jで表現してみました。

自分も試してみたい!という方には前述のチュートリアルがオススメです。 Neo4jをインストールしなくても試せます1。 ですし、それに本筋と離れてしまうので、Neo4jのインストール方法については今回は割愛させていただきます。

とりあえず、今日のお話を振り返ってみましょう。

まずは今日のお話を表すノードを作ります。 みなさんがRDBをSQLで操作するように、Neo4jでは、Cypherという専用の言語を使って操作します。 Cypherで新しいノードを作る際は、以下のようにCREATE節を使用します。 SQLと同じように、末尾にセミコロン(;)をつけるのを忘れないでくださいね。

CREATE (e:Episode { title: '宿命の対決!エースVSレジーナ', air_date: '2013-12-22' });

作成したノードを確認する際は、MATCH節を使用します。

MATCH (episode:Episode { air_date: '2013-12-22' })
RETURN episode;
+--------------------------------------------------------+
| episode                                                |
+--------------------------------------------------------+
| Node[1]{title:"宿命の対決!エースVSレジーナ",air_date:"2013-12-22"} |
+--------------------------------------------------------+
1 row

ちゃんとノードが作られてますね。

もうちょっとGraphDBらしくするために、今回の中心人物と今日のお話を FEATURED というリレーション(関係)でつなげます2

MATCH (episode:Episode)
WHERE episode.title = '宿命の対決!エースVSレジーナ'
CREATE (precure:Precure { precure_name: 'キュアエース' })
CREATE (episode)-[:FEATURED]->(precure);

キュアエースのプロパティが precure_name だけというのもなんだか寂しいので、 sue445さんのrubicureから 情報を ちょっと拝借しましょう。

MATCH (ace:Precure { precure_name: 'キュアエース' })
SET ace.human_name = '円亜久里'
// 複数行の文字列を入れるのにもっと見やすい方法はないかしら・・・
SET ace.transformation_line =
  'プリキュアドレスアップ!\n(キュピラッパー!)\n愛の切り札! キュアエース!\n響け愛の鼓動!ドキドキプリキュア!\n美しさは正義の証し、ウインク一つで、\nあなたのハートを射抜いて差し上げますわ';

そういえば、今回の主役というべき人物は他にもいました。そう、レジーナです。 彼女を本当に悪役(villain)と呼ぶべきかは議論の余地がありそうですが、 ひとまず Villain というラベルを付けましょう。

MATCH (episode:Episode)
WHERE episode.title = '宿命の対決!エースVSレジーナ'
CREATE (villain:Villain { name: 'レジーナ' })
CREATE (episode)-[:FEATURED]->(villain);

おっと、ラベルについて説明しておりませんでした。 「ラベル」とは、どうやらノードの種類を表すものだそうで、Neo4j 2.0から本格的にサポートするようになったんだとか 3。 「ラベル」という名前から察せられるように、 「〇〇というラベルを持つノードは××というプロパティを備えていないといけない」みたいな、 ノードに対して制約を課すものではないみたいです。

さて、Neo4j自体の話は一旦置いといて、作ったグラフをもっと大きくしましょう。 今週のドキドキ!プリキュアでは、キュアエースとレジーナが決闘するのでした。

MATCH  (episode:Episode { title: '宿命の対決!エースVSレジーナ' }),
       (ace:Precure { precure_name: 'キュアエース' }),
       (regina:Villain { name: 'レジーナ' })
CREATE (battle:Battle)
CREATE (ace)-[:FOUGHT_IN]->(battle)<-[:FOUGHT_IN]-(regina)
CREATE (battle)-[:OCCURRED_IN]->(episode);

「今週のお話で起きた」ということを表現するため、 今週の episode と、 battle の間に OCCURRED_IN というリレーションを加えております。 これにより、二人があと何話戦ってもそれぞれの内容をきちんと区別することができます 4

レジーナちゃんといえば先週のアレですよね!

そうそう、レジーナちゃんと言えば、先週こんなことがありました。

MATCH  (regina:Villain  { name: 'レジーナ' })
CREATE (episode:Episode { title: 'ジコチューの罠!マナのいないクリスマス!', air_date: '2013-12-15' })
CREATE (heart:Precure   { precure_name: 'キュアハート',       human_name: '相田マナ' })
CREATE (diamond:Precure { precure_name: 'キュアダイアモンド', human_name: '菱川六花' })

CREATE (kokuhaku:Kokuhaku)
CREATE (kokuhaku)-[:FOR]->(heart)
CREATE (diamond)-[:FORCE_TO { line: 'すきなのよ!いいかげん 正直にみとめなさい!'}]->(kokuhaku)
CREATE (regina)-[:TOLD { line: 'うるさいわね!そうよ すきよ!\nあたしだってマナがすき!悪い!?'}]->(kokuhaku)
CREATE (kokuhaku)-[:OCCURRED_IN]->(episode);

キャー、恥ずかしい///

と、とにかく、新しく追加したお話と他のノードとの関係を明確にしておきましょう。

MATCH  (e45:Episode   { air_date: '2013-12-22' }),
       (e46:Episode   { air_date: '2013-12-15' })
CREATE (e46)-[:NEXT_OF]->(e45);

MATCH  (e45:Episode   { air_date:   '2013-12-15' }),
       (mana:Precure  { human_name: '相田マナ' }),
       (rikka:Precure { human_name: '菱川六花' })
CREATE (e45)-[:FEATURED]->(mana)
CREATE (e45)-[:FEATURED]->(rikka);

放送日(air_date)が2013年12月22日のお話を2013年12月15日の次の(NEXT_OF)お話、 とすることで、検索の際、前後のお話をたどりやすくしました。 こうすることで、 例えば「あれ、レジーナちゃんがマナちゃんと出会ってから何話後に告白したんだっけ?」 みたいな質問に簡単に答えられるようになるかもしれません。 するとしたらこんな感じのクエリでしょうか?

MATCH (regina:Villain { name: 'レジーナ' }),
      (mana:Precure  { human_name: '相田マナ' }),
      // 「-->」と書くことで、リレーションの部分を省略できるそうです!
      (regina)-->(kokuhaku:Kokuhaku)-->(mana),
      (kokuhaku)-[:OCCURRED_IN]->(kokuhaku_episode:Episode),

      // kokuhaku_episode からいくつか間に「NEXT_OF」を挟んだノードを選ぶ
      episode_interval =
        (kokuhaku_episode) <-
          [:NEXT_OF*1..] -
        (deai_episode:Episode), // もう、英語を考えるのが疲れました...

      (deai:Deai)-[:OCCURRED_IN]->(deai_episode),
      (regina)-->(deai)-->(mana)
RETURN LENGTH(episode_interval);

君も、自分だけのプリキュアワールドを表現してみよう!

さて、ここまで取り留めもなく、およそ気まぐれにグラフを作ってみました。 「ここ最近のドキドキ!プリキュアを振り返る」には明らかに不十分な情報量ですが、 このように、Neo4jでは気ままに考えながら、 それこそホワイトボードに図を書くようなノリで、 様々なデータや、それらの関係を簡単に表現することができます。 RDBのように、予めがっちりスキーマを考える必要もなく、 まさに「作りながら考える」のに向いていると言えるでしょう。

この先今回作ったグラフをもとに、 「俺と〇〇たんとの愛の遍歴(〇〇にはあなたの嫁の名前が入ります!)」 なんてアレなテーマで記録したり、 「新しい武器の登場タイミングと各おもちゃの売上との関係」 みたいなお固いテーマを調べたりもできます(大分飛躍がありますが…)。

みなさんも、Neo4jを使って素敵なプリキュアDBを作ってみてはいかがでしょうか?


  1. ただし、残念ながら日本語を入力すると文字化けしてしまうので、この記事に書いたクエリをそのまま入力しても残念なことになってしまいます…orz

  2. 誰が中心人物なのか、は私の主観による。

  3. Ian Robinson, Jim Webber, and Emil Eifrem 「Graph Databases」 p.43, O’Reilly Media, Inc. より。

  4. もちろん、マナちゃんにすればそんなことは起きてほしくないのでしょうけど。


I'm a Haskeller Supported By Haskell-jp.