GraphDB: Dgraph のすゝめ

この記事はAizu Advent Calendar 2017 - Adventar 22日目の記事です。
前の人はywkw1717さんのSmart ContractをデプロイするときにEVMで行われていること - yyyで、
次の人はyopioさんです。

はじめに

紹介欄ではKotlin, Rust, 強化学習のどれかを話そうと思ってたのですが、僕の周りでGraphDBの話が聞きたいという人がいたのでGraphDBの話をします。
インターンでかじった程度なので色々と雑になるかもしれませんので申し訳ありません...

GraphDBとは

GraphDBとはグラフデータベースです。よく使われるRDBでは構造は表のようになっています。
それに比べてGraphDBはグラフ指向型なので、構造がグラフ構造になっています。

DBをグラフ構造にする利点としては

  • グラフ構造なので人間が理解しやすい
  • 関係性を重視する構造に向いている (SNSなど)
  • エッジとノードを辿るので多対多などに対しての探索コストが低い

などが挙げられます。

Dgraphとは

DgraphとはGo言語で実装されたGraphDBです。
github.com
GraphDBのなかではNeo4jが最も有名です。 Dgraphを使うのはNeo4jよりパフォーマンスが高いからです。
詳しくはこちらを見てください : Neo4j vs Dgraph - The numbers speak for themselves - Dgraph Blog

Dgraph入門

はじめに

Dgraphの事始めてきな感じのことを書こうと思います。
Golangで実装されGoのルールなのかわかりませんがA Tour of Dgraphがあります。
これから書くこととしては基本的にそちらにも書いてあるのでそちらを見てもらってもよいかもしれないです。

A Tour of Dgraph

環境構築

docker

% docker pull dgraph/dgraph

でまずdocker imageを取得します。

# Directory to store data in. This would be passed to `-v` flag.
% mkdir -p /tmp/data

# Run Dgraph Zero
% docker run -it -p 8080:8080 -p 9080:9080 -p 8081:8081 -v /tmp/data:/dgraph --name diggy dgraph/dgraph dgraph zero --port_offset -2000

# Run Dgraph Server
% docker exec -it diggy dgraph server --memory_mb 2048 --zero localhost:5080

# Run Dgraph Ratel
% docker exec -it diggy dgraph-ratel

こんな感じで行けます。

on Mac or Linux

% curl https://get.dgraph.io -sSf | bash

これでインストール自体は完了です。

# Start Dgraph zero
% dgraph zero --port_offset -2000

# Strat Dgraph Server
% dgraph server --memory_mb 2048 --zero localhost:5080

# Start Dgraph ratel
% dgraph-ratel

こんな感じで走らせます。

Query

次に軽くQueryを解説します。
今回解説するのは下記の3つです。

  • mutation (set, delete)
  • schema
  • function

mutaion

mutationはDBに直接的な変更があるブロックです。
CRUDでいうところの C, U, D を行うブロックです。

{
    set {
        _:Tokyo <name> "東京" .
        _:Kanagawa <name> "神奈川" .
        _:Saitama <name> "埼玉" .
        _:Chiba <name> "千葉" .
        _:Ibaraki <name> "茨城" .
        _:Gunma <name> "群馬" .
        _:Tochigi <name> "栃木" .
    }
}
{
    delete {
        <0x1> <name> * .
    }
} 

上のがCreateで下のがDeleteです。
上の処理において、_:Tokyo_:Kanagawaなどはそのブロック内で有効な識別子です。これはブロック内でのみ有効なので、ブロックから外れる場合はuidで指定しなければなりません。 それぞれのエンティティから<name>というエッジを使ってそれぞれ文字列の値と接続しています。
下の処理ではすでに識別子の有効期限は切れているのでuidで指定する必要があります。今回は例として適当にこちら側で振りましたが、実際はDgraph側で決定されます。 deleteブロックでは<0x01>のエンティティの<name>エッジで接続しているものを全て消去するようなクエリです。

schema

次にSchemaです。とりあえずサンプルです。

name: string @index(exact, term), @count .
near: uid @count .

こんな感じで書けます。
@index@countはそのエッジとなる<name>, <near>に使える機能を付与しています。 例えば@countであれば1つのエンティティに複数のエンティティが接続している状態であるときにその接続している個数をカウントできたりhas関数などが使えるようになります。 今回は特別にnameに関しても@countを付与します。 逆にこの@countがないとこの行為は行えません。@indexでは文字列周りの操作が細かく行えるようになります。exacttermに関しては細かくなってくるので公式リファレンスを参照していただけると幸いです。

function

恐らく一番使いそうなfunctionについて解説します。

{
    getLocation(func: has(name)) {
        name
        near {
            expand( _all_ )
        }
    }
}

getLocationという関数です。
まずhas(name)<name>エッジでの接続を保持しているノードのみを選択します。そして帰ってくるjsonの欲しい値を記述していきます。 今回は<name><near>を取得するようにしています。expand( _all_ )はそこの値をすべて返す関数です。 例えばschemaで説明したものだとエッジはname, near, uidの3つあります。それらを全て展開するのがexpand( _all_ )です。
この場合<near>は他のuidを保持する、つまりは他のエンティティを所持しているので、そのエンティティの情報を全て返すような形になっています。 今回使用したこの関数は同じブロック内であれば使用することも可能です。

まとめ

インターンでDgraphを触ってそれから触ってなかったのでもしかしたら間違えがあるかもしれません。あったらごめんなさい...
所感としてはガッチリと固いものを作るというよりゆるふわなものを作る印象が強かったです。 GraphDBとしてはまだSNS方面でしか使い道がないらしくいい感じに利用できたらなぁとか思いました。

参照

Dgraph Documentation