RedisGraphでアイマスのGraphを作ってみる

はじめに

RedisGraphどころかRedisすら触ったことなかったけど、QuickStartのクエリを見様見真似でやったのでそこまで詳しくないです。
でも、RedisGraphの日本語記事が無かったんで試しに使ってみた。みたいな記事です。

RedisGraphとは

oss.redislabs.com

公式サイトにあるPrimary featuresを見ていきましょう。

  • Property Graph Modelに基づいてる。
    たぶんこんな感じの、ノードとノードの間に意味を持つエッジ(ブランチ)があるグラフのことでしょう。
    f:id:crssnky:20200809172004p:plain
  • ノードとエッジは属性を持つこともできる。
    f:id:crssnky:20200809172556p:plain im@sparqlのように、あらゆる事象をエッジとノードで表すのではなく、
    f:id:crssnky:20200809172646p:plain このように、ノードにある程度の情報を持たせても良いらしい。
    im@sparql(RDF)では名前の文字列を与えるにしてもエッジを用意していたので、これは結構助かるかも?でも、属性にするか、別ノードにするかはルールが要るかもですね。
  • ノードに名前が付けられる。
    付けられます。
  • エッジにはtypeを付けられる。
    f:id:crssnky:20200809173456p:plain エッジにはいろいろ付けられます。
  • グラフはスパース隣接行列で表現される。
    スパース行列は、疎行列。隣接行列はグラフのエッジを行列で表すアレです。
    使ってみたレベルの今は内部構造は気にしません。
  • クエリ言語はCypherです。
    Graph query language(GQL) standardへの取っ掛かりとなる直感的な言語らしいです。
    まぁ確かに、
CREATE (:Idol {name:'如月千早'})-[:BelongTo]->(:Production {name:'765プロダクション'})

となり、上の図を形まで同じように記号で書き起こした感じ。
書くのは煩雑かもだけど、初学者でも読み取りやすいですね。

  • Cypherクエリは線形代数式に変換される。
    使ってみたレベルの今は内部構造は気にしません。

グラフを書いてみた

github.com

im@sparqlにアイドルとユニットの所属情報を問い合わせて、結果をredisgraphに書き込むやつです。Rustで書いてます。
これ何も難しいことしてないです...結果

{
  "units": { "type": "literal" , "value": "ARCANA,ルナ,ハニーサウンド,Engage!,TORICO,ミッシングムーン,you-i,765ProAllstars,エターナルハーモニー,SLEEPING BEAUTY,765MillionStars,花鳥風月" } ,
  "idol": { "type": "literal" , "value": "如月千早" }
}

を分解して

"CREATE (:Idol {{name:'[アイドル名]'}})-[:memberOf]->(:Unit {{name: '[ユニット名]'}})"

上のやつにこんな感じで入れてるだけです。↓

  for element in json {
    let units = element.units.value;
    let idol = element.idol.value;
    for unit in units.split(separator) {
      let create_query = format!(
        "CREATE (:Idol {{name:'{}'}})-[:memberOf]->(:Unit {{name: '{}'}})",
        idol,
        percent_decode(unit.as_bytes()) // ユニット名はエンコードされてるかも
          .decode_utf8()
          .unwrap()
          .to_string()
          .replace("'", "\\'")
      );
      result = graph.mutate(&create_query);
      if !result.is_ok() {
        return result;
      }
    }
    println!("{}", idol);
  }

グラフに問い合わせてみた

redisgraphのQuickStart通り、redis-cliコマンドを使って問い合わせます。
ここで注意なのが、--rawオプションを付けなければ日本語が文字化けするということです。
というわけでクエリはこちら

GRAPH.QUERY imasparql "MATCH (i:Idol)-[:memberOf]->(u:Unit) WHERE i.name = '如月千早' RETURN i.name, u.name"

クエリは説明しなくてもなんとなく分かるよね?(逆に言えばなんとなく分かる程度にしか自分も説明できない...) 結果はこちら

i.name
u.name
如月千早
ARCANA
如月千早
ルナ
如月千早
ハニーサウンド
如月千早
Engage!
如月千早
TORICO
如月千早
ミッシングムーン
如月千早
you-i
如月千早
765ProAllstars
如月千早
エターナルハーモニー
如月千早
SLEEPING BEAUTY
如月千早
765MillionStars
如月千早
花鳥風月
Query internal execution time: 1.649300 milliseconds

わぁ、羅列!
--rawさえ付けなければ

127.0.0.1:6379> GRAPH.QUERY imasparql "MATCH (i:Idol)-[:memberOf]->(u:Unit) WHERE i.name = '黛冬優子' RETURN i.name, u.name"
1) 1) "i.name"
   2) "u.name"
2) 1) 1) "\xe9\xbb\x9b\xe5\x86\xac\xe5\x84\xaa\xe5\xad\x90"
      2) "Straylight"
3) 1) "Query internal execution time: 0.962100 milliseconds"

こんな感じで、塊ごとに分けてもらえるのですが....
まぁ、コマンドラインでやる人なんていないでしょうし、ソースコード上で使うクライアント次第じゃないでしょうか。

おわりに

使ってみた程度としてはこんな感じです。
今回はCREATEMATCHをメインに使いましたが、他にも命令はあるみたいなのでもっと便利になるとは思います。

おまけ

GitHubのリポジトリを見て気付いたかもしれませんが、redisgraphにim@sparqlのアイドルとユニットの所属情報を書き込んでくれるDocker Imageを配布しています。

hub.docker.com

コンテナ生成時にim@sparqlへ問い合わせるので、生成しなおせば新鮮なグラフになります。
ユニット情報だけでなく、いろいろ追加していこうかと思っているので、im@sparqlでは速度に不満がある方はぜひ使ってみてください。