2020-10-24に投稿

Next.jsとDgraphで作るクイズ・アプリ (5) Dgraphの基本

Dgraph操作の基本

前回

前回を参照

用語

グラフデータベースなので, ノードで構成されます.

用語 意味
ノード データを構成する基本要素. レコードのようなもの
プレディケート ノードの属性やエッジ(ノード間の関係性)を表す

スキーマ

とりあえず各ノードは以下のようなスキーマ持つとします.

Predicate Data Type
title string
user string
version string
date datetime
question [uid]
answer [uid]
tags [uid]

[uid]というのは別のノードを参照しているという意味で1対多の関係になります.

UID arrays represent a collection of UIDs. This is used to represent one to many relationships.

Data types for predicates

question and answer nodes

クイズの本体とも言える問とその答えです. 同じデータ構造で表現します.

Predicate Data Type
text string
content string

tags

クイズを分類するタグです. ノードで表現することで一意性をもたせることができます.

Predicate Data Type
tag_name string

ミューテーション

データに変更を加えるにはsetとdeleteという2つのキーワードを使います.

CRUD

create

setはノードの作成とプレディケートの更新を行います.

{
    "set": [
        {
            "title": "MonQとは何でしょうか?",
            "user": "brainvader",
            "version": "0.0.1",
            "date": "2020-10-08T18:01:00",
            "question": [
                {
                    "type": "text",
                    "content": "MonQとは何でしょうか?"
                },
                {
                    "type": "text",
                    "content": "またどのようなものを目指しているでしょうか?"
                }
            ],
            "answer": [
                {
                    "type": "text",
                    "content": "クイズベースの学習システムです."
                },
                {
                    "type": "text",
                    "content": "クイズ同士の寒冷性や依存性を定義することでクイズをモジュール化することを目指します."
                }
            ],
            "tags": [
                {
                    "uid": "_:monq",
                    "tag_name": "monq"
                }
            ]
        }
    ]
}

作成されたノードには自動でuidが与えられます.

read

クエリを用いて取得します. has関数を使うと指定したプレディケートを持っているノードを取得できます. func引数にhas関数を渡してやります.

{
  quizzes(func: has(title)) {
    uid
    title
    user
    date
    version
    question {
      type
      content
    }
    answer {
      type
      content
    }
    tags {
      tag_name
    }
  }
}

ここでquizzesは関数名というよりはrootノードです. レスポンスは以下のようになります. quizzesという配列に取得したノードが収められています. 指定したルートノードから辿れるようにな構造になるわけです.

{
  "data": {
    "quizzes": [
      ...
    ]
   }
   ...
}

以下のようなグラフが取得できます. ピンクがquとあるのでquestionで緑がanswer, そして紫がtagsです.中心の青はtitleを含むノードです. つまりクイズそのものを指します.

image

ブランク・ノード

ノードを指定する時にuidは自動で作られます. 作成後のノードには全てuidが付番されているので参照することは簡単です. 問題はノードを定義する時に共通のuidを指定したい場合です.

例えば共通のtagを持つクイズを同時に作りたい場合です. tagはノードとして管理されるのでuidで識別できます. 同じ文字列を指定しても同じノードを参照しているという関係は表せません. この場合_:identifierという表記を使うと同じuidであることを表せます.

以下は英単語の意味を問うよくあるクイズです. これらはどちらもenglishというタグ・ノードを参照するのが妥当でしょう.

{
    "set": [
        {
            "title": "put on holdとは?",
            "user": "brainvader",
            "version": "0.0.1",
            "date": "2020-10-08T18:01:01",
            "answer": [
                {
                    "type": "text",
                    "content": "put on holdとは?"
                }
            ],
            "question": [
                {
                    "type": "text",
                    "content": "保留する"
                }
            ],
            "tags": [
                {
                    "uid": "_:english",
                    "tag_name": "english"
                }
            ]
        },
        {
            "title": "hotshotとは?",
            "user": "brainvader",
            "version": "0.0.1",
            "date": "2020-10-08T18:01:01",
            "answer": [
                {
                    "type": "text",
                    "content": "hotshotとは?"
                }
            ],
            "question": [
                {
                    "type": "text",
                    "content": "有能な人、やり手, 凄腕"
                }
            ],
            "tags": [
                {
                    "uid": "_:english",
                    "tag_name": "english"
                }
            ]
        }
    ]
}

先程のクエリを実行すると以下のようなグラフが得られます.

image

englishというtagsノードを通じてつながっていることが分かります.

delete

ノードの削除

よく分からん. とにかく消えないです.

プレディケーとの削除

更新処理の前に削除をしてみましょう. 各ノードにはプレディケートというフィールドに該当するものが存在する. ノードをuidで指定してプレディケートをnullに指定するとそのプレディケートは削除されたことになります. 全てのプレディケートがなくなるとそのノードは削除されたとみなされます(とされていますが, コンソールで試すと消えません).

ノードの場合は単純にuidを指定すれば良いようです.

さて先程の英単語のクイズを削除してみましょう. 以下のようなミューテーションを実行します.

{
    "delete": [
        {
            "uid": "0x3a982",
            "title": null,
            "answer": null,
            "question": null,
            "tags": null
        }
    ]
}

この場合tagsは他方のクイズからも消えてしまうでしょうか? このノードからは辿れなくなりますが, ノード自体は残ります.

プレディケートの全削除

以下のような記法を使います.

{
  delete {
    <0x3a985> * * .
  }
}

プレディケートのフィールド名を使うこともできます.

{
  delete {
    <0x3a985> <title> * .
  }
}

こえでtitleプレディケートは消せます.

update

プロパティ・プレディケート

単純に値を上書きすれば良いようです. userの名前を変更してみましょう.

{
    "set": [
        {
            "uid": "0x3a985",
            "user": "no-brainer"
        }
    ]
}

ではuser名を取得してみましょう.

{
  quizzes(func: uid(0x3a985)) {
    user
  }
}

user名がbrainvaderからno-brainerに変わっているのが分かります.

image

エッジ・プレディケート

おなじみのクイズを作ってみましょう.

{
    "set": [
        {
            "title": "MonQとは何でしょうか?",
            "user": "brainvader",
            "version": "0.0.1",
            "date": "2020-10-08T18:01:00",
            "question": [
                {
                    "type": "text",
                    "content": "MonQとは何でしょうか?"
                },
                {
                    "type": "text",
                    "content": "またどのようなものを目指しているでしょうか?"
                }
            ],
            "answer": [
                {
                    "type": "text",
                    "content": "クイズベースの学習システムです."
                },
                {
                    "type": "text",
                    "content": "クイズ同士の寒冷性や依存性を定義することでクイズをモジュール化することを目指します."
                }
            ],
            "tags": [
                {
                    "tag_name": "monq"
                }
            ]
        }
    ]
}

tagを更新して見ます.

{
    "set": [
        {
            "uid": "0x3a992",
            "tags" : {
              "tag_name": "tutorial"
            }
        }
    ]
}

新しくtutorialというtagが追加されたことが分かります.

{
  quizzes(func: has(tag_name)) {
    tag_name
  }
}

Q&A

プレディケートの前に付く~(ティルダ)の意味とは?

エッジの方向を反対に探索することを意味する.

anyofterms(predicate, "query word")

プレディケーとがqueryかwordにマッチする.

まとめ

  • uid(value)
  • has(predicate)
  • gt(predicate, value)
  • @recurse(depth: value)
  • @filter(lt(dislikes, 10))
  • eq(tag_name,"devrel")

クエリ集

タグの取得

{
  all_tags(func: has(tag_name)) {
    tag_name
  }
}

特定のタグの削除

プロパティ・プレディケートがなくなると削除されるので唯一のプレディケートであるtag_nameをnullに設定すれば良い.

```json
{
"delete": [
{
"uid": "0x3a992",
"tags" : [{
"uid" : "0x3a994",
"tag_name": null
}
]
}
]
}

ツイッターでシェア
みんなに共有、忘れないようにメモ

ブレイン

Androidアプリ開発者を目指しています. 興味あることリスト: https://t.co/ew3bb6grdJ Github: https://t.co/9btqysHqWr Qiita: https://t.co/ZVRhjouauX

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント