読者です 読者をやめる 読者になる 読者になる

The Round

合同会社ナイツオの開発ブログ

静岡でGo言語やりたい人!!→こちら

GAE/Go Ancestorクエリについて

golang Google App Engine

どうもお久しぶりになります、マツウラです。
取り上げる内容は、Google App EngineのDatastoreにおけるAncestorクエリです。
今回Ancestorクエリについて調べたのは次の理由からです。

put直後にQueryで問い合わせたところ、putしたハズのEntityが取れなかったのです。

原因は単純なことでした。
データをRoot Entityとして扱っていたためです。
put直後であったため、インデックスの更新が間に合っていなかったようで結果に含まれなかったんですね。

これを解決したのがAncestorクエリでした。
どんなものか、概要をサラッと見てゆきたいと思います。

参考:Go Datastore API - Go — Google Developers

Ancestorパスについて

データストア内のEntityは、ディレクトリ構造と似たような階層構造化空間を形作ります。

Entityを作る際は必要に応じて、親となる別Entityを指定できます。
すると、新しいEntityは親Entityの子となります。(ファイルシステムと異なり、親Entityは実際に存在する必要は無いことに注意してください)

逆に、親を持たないEntityは、Root Entityとなります。
Entityと親の関連付けは永久的で、Entityが作成されると変更することはできません。
また、データストアは同じ親をもつ2つのEntity、または2つのRoot Entityへ同じ数値IDを割り当てることはありません。

Entityの親、そして親の親など、再帰的にはEntityの祖先ということになります。
また、子、そして子の子などはEntityの子孫です。
Entityとその子孫は同じEntityグループに属します。

Entityグループでは、Root Entityから始まり親から子へと進むEntityの順序や、特定のEntityへの繋がり、Entityの祖先パスを構成しています。
Entityを識別する完全なキーは、kindとID / 名前のペアによるシーケンスで構成されています。
対して、Root Entityである場合はAncestorパスは空で、キーは単に自分自身のkindと識別子で構成されます。

Ancestorクエリについて

クエリでは、特定のEntityグループに属するEntityだけを取得するようにAncestorフィルターを追加できます。
これはAncestorクエリとして知られています。
デフォルトでは、Ancestorクエリはデータへの最終変更が最新であることが保証されており、強く一貫した結果を返します。

対照的にAncestorクエリ以外では、単一のEntityグループというよりもデータストア全体を範囲としますが、結果整合性だけなので古い結果を返すかもしれません。
強力な一貫性がアプリにとって重要なら、データはAncestorクエリで取得できるように、関連するEntityは同じEntityグループに置くように、データを構造化する際に考慮する必要があります。

kind無しのAncestorクエリ

Ancestorクエリを含んだkindを指定しないクエリでは、kindに関係なく指定したAncestorとその子孫すべてを取得します。
このタイプのクエリではカスタムインデックスを必要としません。
すべてのkind無しクエリのように、プロパティ値ではフィルタやソート順を含めることはできませんが、エンティティのキーではフィルタリングができます。

注意すること

Ancestorクエリは便利ですが、巨大なAncestorパスツリーを作らないように注意する必要があります。
同じEntityグループに属するため、同時書き込みが増えると並行処理のためにエラーが起きやすくなってゆきます。
複数のユーザーが書き込むケースなら注意が必要ということです。


要するにAncestorクエリは、強力な一貫性を持つ高速なクエリということです。
今回のケースで便利さが実感できました。
特にデータへの最終変更が最新であることが保証されていることが良いですね。
ぜひ使ってみて下さい。