The Round

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

[PR] 5分から相談できるGCP™ 開発コンサル!→こちら

GAE/Go Datastore クエリカーソル

注:古い記事の為、内容が最新ではない可能性がありますm(_ _)m

どうもこんにちわ!マツウラです。
今回は前回の基本的なクエリに続けてGo言語でのクエリカーソルの使い方について見てゆきます。

参考:Go — Google Developers Datastore Queries

クエリカーソル

クエリカーソルはoffsetのオーバーヘッドを発生させること無く、結果を取得出来ます。

アプリケーションは結果の最後のインデックスを示すカーソル(文字列でbase64エンコード)を取得します。
アプリケーションはこのカーソルを保存し、次のバッチの開始点として使用することが出来ます。
また、エンドカーソルを指定することで結果セットの範囲を制限することも可能です。

次はカーソルを使用した基本的な例です。

c := appengine.NewContext(r)
q := datastore.NewQuery("Person")

item, err := memcache.Get(c, "person_cursor")
if err == nil {
    cursor, err := datastore.DecodeCursor(string(cursor.Value))
    if err == nil {
        q = q.Start(cursor)
    }
}

t := q.Run(c)
for {
    var p Person
    _, err := t.Next(&p)
    if err == datastore.Done {
        break
    }
    if err != nil {
        c.Errorf("fetching next Person: %v", err)
        break
    }
    fmt.Fprintf(w, "%v\n", p)
}

cursor, err := t.Cursor()
if err == nil {
    memcache.Set(c, &memcache.Item{
        Key:   "person_cursor",
        Value: []byte(cursor.String()),
    })
}
func (t *Iterator) Cursor() (Cursor, error)

イテレータの現在位置を表すカーソルを返します。

func (t *Iterator) Next(dst interface{}) (*Key, error)

取得する結果のkeyを返します。(エンティティはdstに格納されます)
これ以上結果がない場合はDoneがerrorとして返されます。

func (q *Query) Start(c Cursor) *Query

Startは開始点を指定したクエリを返します。

func (q *Query) End(c Cursor) *Query

Endは終了点を指定したクエリを返します。

カーソルとデータ更新

もしカーソルを使用している間にクエリの結果が変化することがあれば(エンティティの追加や削除、更新など)、その変更は後のクエリで結果に反映されるようになります。

カーソル位置の先に新しく結果に返るであろうエンティティが追加で現れても、その結果には返されません。
もし結果セットから最後に返された結果を削除しても、カーソルは次の結果を知ることができます。

q := datastore.NewQuery("Person")
iter := q.Run(c)

new_e := &Person{....}
key := datastore.NewIncompleteKey(c, "Person", nil)
_, err := datastore.Put(c, key, new_e)
// この新しく追加したエンティティは、上記のクエリカーソルでは取得できない。

2016/11/17:更新ここから(一部誤りがあったため修正)
カーソルの使用中にクエリの結果に変更があった場合、それがカーソル位置の後であれば結果として返されます。
ただし、カーソル位置の前であれば結果には返されません。
2016/11/17:ここまで

クエリの結果を取得する場合、結果の継続的なグループを返すため開始カーソルと終了カーソルの両方を使用することができます。

結果を取得するために開始と終了のカーソルを使う場合、カーソルを生成した時と同じ結果サイズは保証しません。
エンティティはカーソルが生成され、クエリで使用されるまでの間にデータストアに追加または削除しても構いません。

Goでは、イテレータのCursorメソッドを呼び出すことで、クエリの結果を取得した後にカーソルを取得します。
カーソルから追加の結果を取得するには同一のクエリを作成し、Startメソッドにカーソルを渡して実行します。

カーソルの制限

カーソルの使用範囲について。

  • カーソルはクエリを実行し、さらに同一のクエリを継続する同一のアプリケーションによってのみ使用することが出来ます。
    再びカーソルを使用するには、種類、フィルタ、ソートを含め正確に元のクエリを再構成する必要があります。

同じ結果を返す可能性について。

  • 複数の値を持つプロパティでは、不等式フィルタやソートを用いるクエリは常に期待通りに動作するとは限りません。
    カーソルを使用するクエリ間ではプロパティ値の重複が解消されず、また同じ結果を返す可能性があります。

カーソルが無効化されるケース。

  • 新しくAppEngineがリリースでは内部実装が変更される場合があり、その場合依存するカーソルは無効化されます。
    もし有効でなくなったカーソルを使用するとデータストアはエラーを返します。

今回はクエリカーソルについて取り上げました。
とはいってもあまりGo言語特有の事例はなかったかもしれません^^;
App EngineでGo言語を始めるにあたっての参考にして頂ければ幸いです。

次回はプロジェクションクエリについてGo言語での基本的な使い方など見てゆきます。