クエリを実行してデータを取得するときに offset を使わない方法がある

ビデオチャットで、Google の人とディスカッションしたときに謎が解けました。
というより、気にせずスルーしてただけなんですが。

ちなみに体験記です。詳しいやり方は、別記事にします。
新料金体系に対する質疑応答で話は進み、クエリーを実行する fetch メソッドで offset を指定した場合、そのオフセット分も課金されると言われ、ドキュメントに書いてあることが理解できました。

クエリの性能特性は、offset 数 + limit と直線的に比例します。

私が開発している Web サービスの仕様では、一度に取得するデータの上限は 1000 に届きません。そして、レスポンスをよくする為に 20件ずつ取得するようにしていました。( JavaScript の方で20件取得を再起処理させている。 )
動作確認程度のテストしかしていませんが、気にするほどパフォーマンスが悪いわけではありません。気になる一文でしたが、他のやり方がわからないし。調べる余裕もありませんでした。

しかし、これは問題です。いくらパフォーマンスが悪く無くても、小刻みに offset しているぶん課金されるわけです。offset して20件取得しているのに、全取得してサーバ側で処理して20件だけ返すのと同じです。結果が500件合った場合ですと、25回も毎回全取得しているのです。

ちょっとまて、もったいないではないか。

どうすれば良いのですか?と、質問したら、「query のメソッドで cursor があるから、それを使えば良い。」とアドバイスを貰いました。「うーん、ドキュメントにそんなのあったか?」と思いましたが、なんとかなるだろうと、この話はここで終わりに。

しかし、これが甘かった! cursor の使い方を検索しても、見つからない。 Java でのやり方なら数件ヒットしましたが。公式ドキュメントで探しても、cursor という単語自体が見つからない。(今気がついたのですが、Google Group で探せば良かった…。)

それでも諦めずに探す事、数時間。やっと StackOverflow から発掘できました。Google AppEngine: How to fetch more than 1000?
最初の方の回答では、取得した最後の ID を保存。次の取得の query に、そのID以上を指定する方法でした。モデルには、エンティティが作成された日時を入れるプロパティがあり、日付の昇降順で取得していたので、 cursor が解らなかったら、その方法で行こうと思いました。
そして続きを見ると、 cursor メソッドを使った回答がありました。しかし、あまり理解が出来なかったので、また探す旅に出ました。

振り出しに戻った…。と思ったのですが、検索結果をよく見ると、公式ドキュメントのリンクがあるのです。英語版ですが。あれ?と思いつつ内容をみる。そして、なんとなく仕組みが解ったのですが、すっきりしません。そうです、なんで日本語の公式ドキュメントでは見つからな…、はっ!まさかっ!

query オブジェクトのドキュメントを左右並べて上から確認すると…。後半部分がごっそり無いではありませんか! なんと、100%日本語に翻訳されているわけではなかったのです。

ここまで相当時間を費やしました。いつもの自分なら感情的になっているのですが、開発の遅れが気になったのでしょうか、冷静に次の課題を調べることに。

再起処理をしている為に、 cursor の位置をどこかに保存しなくてはいけないからです。幸い、cursor を調べているときに、memcache を使ったやり方があったので、 memcahe を軽く調べて実装に入りました。

おもいのほか、あっさり実装できました。念のため、キャッシュが無い場合には、offset で取得するようにしました。ゆえに JavaScript の方はまったく手を入れていません。

教訓「英語ドキュメントだけを見て、日本語ドキュメントは見るな!」と言いたいところですが、それは厳しいですよね…。