Python(boto3)での DynamoDB Query
このページが短い理由は boto3 の query ページネーターにあります。LastEvaluatedKey を完全に隠してくれるからです。ただし、おそらく欲しかったはずの数字も 1 つ隠します。信用する前に知っておく価値があるのはそこです。そもそも query に手を伸ばすべき場面については Query と Scan の比較を参照してください。
コード
import boto3
client = boto3.client("dynamodb")
paginator = client.get_paginator("query")
items = []
for page in paginator.paginate(
TableName="Music",
KeyConditionExpression="#hashKey = :hashKeyValue AND begins_with(#rangeKey, :rangeKeyValue)",
ExpressionAttributeNames={"#hashKey": "Artist", "#rangeKey": "SongTitle"},
ExpressionAttributeValues={":hashKeyValue": {"S": "Arturo Sandoval"}, ":rangeKeyValue": {"S": "C"}},
):
items.extend(page["Items"])
print(f"Found {len(items)} items")ページネーターは請求額を合計してくれません
1 曲あたり約 3.9 KB、すべてが Artist = "Arturo Sandoval" の下にある 600 曲のフィクスチャに対して、このループは 3 ページを返します。271 件、271 件、58 件で、それぞれ 128.5、128.5、27.5 読み取りユニットです。同じページネーターに 1 つにまとめた結果を求めると、こうなります。
build_full_result() -> Items 600 Count 600 ScannedCount 600
ConsumedCapacity.CapacityUnits 128.5Count と ScannedCount は合計されました。ConsumedCapacity は合計されていません。これは最初のページの値であり、実際の合計は 284.5 でした。botocore の DynamoDB ページネーター設定はその理由を明示しています。Count と ScannedCount は結果キーとして、ConsumedCapacity は非集計キーとして列挙されています。build_full_result() からキャパシティをログに取っているなら、パーティション全体の読み取りを半分以上少なく報告していることになります。
上の for page in paginator.paginate(...) ループの中にあるページごとの dict は生のレスポンスなので、page["ConsumedCapacity"]["CapacityUnits"] を自分で合計すれば正直な 284.5 が得られます。
58 回の余分なラウンドトリップを生む Limit
Limit は query の有効なパラメーターなので paginate() は受け付けますが、Python ユーザーが期待するパラメーターではありません。
paginate(..., Limit=10) -> 61 pages, 10 items each
paginate(...) -> 3 pagesこれは全体ではなく リクエストごと のアイテム数を制限するので、ページネーターは同じ 600 件を取得するために律儀に 61 回の HTTP 呼び出しを行います。総数を制限するには PaginationConfig={"MaxItems": 10} を使ってください。Limit に対応するつまみは PaginationConfig["PageSize"] です。
2026-07-28 に、CPython 3.14.6 上の boto3 1.43.58 で、DynamoDB Local(amazon/dynamodb-local)に対して計測しました。
解説
- クライアントは双方向で DynamoDB JSON を話します。値は
{"S": "Arturo Sandoval"}として入り、Yearは{"N": "1994"}として返ってきます。一方 リソース API(boto3.resource("dynamodb").Table(...).query)は両方向に変換し、Decimal('1994')を渡してきます。これは金額には正しく、floatに足せないと拒否された初回は驚かされます。 Key("Artist").eq(...)はリソース API 専用です。クライアントに渡すとリクエストが出る前に例外になります。ParamValidationError: Invalid type for parameter KeyConditionExpression ... valid types: <class 'str'>です。クライアントが欲しいのは、このページが組み立てている式の文字列です。- キー条件は 1 つの等価条件に加えて、最大 1 つのソートキー比較です(
=、<、<=、>、>=、BETWEEN、begins_with)。それ以外はFilterExpressionに入れてください。boto3 はそのまま渡し、DynamoDB は読み取りの後に適用します。ScanIndexForward=Falseで順序が逆になり、IndexName="..."でインデックスに向けられます。
ビジュアルに行う
DynamoDB Expression Builder は、キー条件と型付きの ExpressionAttributeValues マップを boto3 にそのまま渡せる Python として書き出します。ここは {"N": "2010"} の代わりに {"N": 2010} と打ってしまいがちな箇所です。
同じクエリをキー条件のフォームから自分のテーブルに向け、結果をページ送りできるグリッドで読むには、DynoTable をダウンロードしてください。
関連ガイド
- Query と Scan の比較 —
queryが既定として正しい理由。 - キー条件式 — 使えるパーティション/ソートキー演算子のすべて。
- 「Query condition missed key schema element」 — キー条件が誤った属性を指しているか、パーティションキーを飛ばしている。
- 「Query key condition not supported」 — contains や 2 つ目のソートキー条件のように、キー条件では使えない演算子。