DynamoDB のページネーション
DynamoDB は1回の呼び出しで「すべての」結果を返すことはありません。Query や Scan
は最大 1 MB のデータを返し、続きから再開するための LastEvaluatedKey を渡します。
ページネーションを正しく行うとは、カウンターではなく、そのキーでループすることです。
ループ
let key;
do {
const out = await client.send(new QueryCommand({...params, ExclusiveStartKey: key}));
process(out.Items);
key = out.LastEvaluatedKey;
} while (key);LastEvaluatedKey が undefined のとき、末尾に到達しています。それを
ExclusiveStartKey として渡し戻すと、次のスライスを取得できます。
制御フローは、キーが存在しないときだけ抜ける単一のループです。
どのパスも、返ってきたキーから再開するか停止するかのどちらかです — カウンターはありません。
Limit はページサイズではない
Limit は DynamoDB が 評価する アイテム数の上限であって、FilterExpression の
後に返すアイテム数ではありません。フィルタの背後にある Limit: 25 のクエリは、
3件だけを返しつつ LastEvaluatedKey を渡すことがあります — ページが短く見えても、
キーが空になるまでページングし続けなければなりません。空でない
LastEvaluatedKey は、さらに マッチする アイテムがあることも保証しません。
末尾に到達したことを証明するのは、キーが 存在しない ことだけです。
SDK にページングさせる
どちらの SDK も上記のループをラップしているので、ページを直接反復できます。
// AWS SDK for JavaScript v3
import {paginateQuery} from '@aws-sdk/lib-dynamodb';
for await (const page of paginateQuery({client}, params)) {
process(page.Items);
}# boto3
paginator = client.get_paginator('query')
for page in paginator.paginate(**params):
process(page['Items'])ページ番号はない
DynamoDB には 総件数もランダムなページアクセスもありません — カーソルを
たどり直さずに「7ページ目」へ飛んだり前のページへ戻ったりはできません。番号付き
ページではなく、無限スクロールや「もっと読み込む」を中心に UI を設計してください。
(Select: 'COUNT' のクエリでも、件数を数えるためにマッチしたすべてのアイテムを
読み取り、課金されます。)
API 向けのステートレスなカーソル
LastEvaluatedKey は最後のアイテムのキー属性にすぎません。base64 エンコードして、
不透明な nextToken としてクライアントに渡し、次のリクエストで ExclusiveStartKey
にデコードし戻してください。サーバー側のカーソル状態は不要です。
そのトークンは DynamoDB-JSON です — DynamoDB-JSON コンバーター で目視したり手作りした りできます。そして Scan を回避するためにページングしている なら、それはたいていインデックスを追加すべきサインです。
DynoTable を試す と、カーソルを代わりに追跡しながらクエリ結果を ビジュアルにページングできます。