Node.js での DynamoDB Scan(AWS SDK v3)

以下の do/while は防御的なコーディングではありません。フィルター付きの Scan のページは空の Items 配列を返しながら、テーブルにまだ続きが残っていることがあります。つまり最初のレスポンスで止めるのは、一致があるテーブルに対してスキャンが 0 件を報告する方法そのものです。この操作を完全に避けるべき場面は Query と Scan の比較で扱っています。

コード

import {DynamoDBClient, ScanCommand} from '@aws-sdk/client-dynamodb';

const client = new DynamoDBClient({region: 'us-east-1'});

const items = [];
let lastEvaluatedKey;

do {
  const response = await client.send(
    new ScanCommand({
      TableName: 'Music',
      FilterExpression: '#filter0 >= :filterValue0',
      ExpressionAttributeNames: {
        '#filter0': 'Year'
      },
      ExpressionAttributeValues: {
        ':filterValue0': {N: '2010'}
      },
      ExclusiveStartKey: lastEvaluatedKey
    })
  );

  items.push(...(response.Items ?? []));
  lastEvaluatedKey = response.LastEvaluatedKey;
} while (lastEvaluatedKey);

console.log(`Matched ${items.length} items`);

空のページ 2 つ、284.5 読み取りユニット、8 件のアイテム

フィクスチャはそれぞれ約 3.9 KB の 600 曲で、そのうちちょうど 8 件が Year >= 2010 を満たし、末尾にソートされます。上のループが実際に受け取るのはこれです。

ラウンドトリップItems.lengthScannedCount読み取りユニットLastEvaluatedKey
10271128.5あり
20271128.5あり
385827.5なし

連続する 2 ページが何も返さず、それぞれ 128.5 読み取りユニットかかっています。if (!response.Items.length) return とするコードは空のテーブルだと報告します。API リファレンスはその規則を端的に述べています。"a scan result can result in no items meeting the criteria and the Count will result in zero"、そして別途 "a FilterExpression is applied after the items have already been read; the process of filtering does not consume any additional read capacity units" と。

2 つ目の文は請求書の目線で読んでください。フィルターは無料で、それが捨てたものは無料ではありません。8 件のアイテムを届けるのに 284.5 読み取りユニット、フィルターがまったくない場合と同じ請求です。

2026-07-28 に、node v24.18.0 上の @aws-sdk/client-dynamodb 3.1095.0 で、DynamoDB Local(amazon/dynamodb-local)に対して計測しました。件数とキャパシティはエンジン自身のレスポンスフィールドです。

解説

  • ExclusiveStartKey: lastEvaluatedKey は初回は undefined です。v3 のシリアライザーは undefined のメンバーを落とすので、1 つのオブジェクトリテラルで最初のリクエストとその後のすべてを賄えます。代わりに {} を渡すと ValidationException: The provided starting key is invalid で失敗します。
  • response.Items ?? [] は実際に仕事をしています。上の表と併せて読んでください。この null 合体は何も一致しなかったページでもアキュムレーターを正しく保ち、while はそのページを越えてループを生かし続けます。
  • #filter0 は飾りではありませんYear は AWS の予約語リストに載っており、別名を付けずに使うと ValidationException: Invalid FilterExpression: Attribute name is a reserved keyword; reserved keyword: Year が返ります。
  • Limit が数えるのは読んだアイテムであって返すアイテムではありません。このフィルターでは Limit: 10Count: 0ScannedCount: 10 を返します。これはキャパシティのスパイクを抑えるつまみであって、結果を 10 件くださいと頼む方法ではありません。
  • Segment / TotalSegments はテーブル全体のスキャンをワーカーに分割します。分割されるのは実時間であってコストではありません。同じ 284.5 ユニットを、より速く、より並行に使うだけです。

実際のテーブルではいくらになるか

8 件のアイテムに 284.5 読み取りユニットというのが問題の形であり、これは結果ではなくテーブルに比例して増えます。ホットパスにフィルター付きのスキャンを載せる前に、自分のアイテムサイズとトラフィックでテーブル全件読み取りの値段を DynamoDB 料金計算ツールで出し、同じアクセスパターンを Query に変える GSI と比べてください。

テーブルを GUI で、フィルター付き・ページ送り可能な結果グリッドとともに探索するには、スクリプトから手探りでスキャンする代わりに DynoTable をダウンロードしてください。

関連ガイド

参考資料

このリクエストをビジュアルに組み立てる

この操作を無料の DynamoDB クエリビルダーで組み立て — キー条件、フィルタ、インデックス、Limit、ソート順、ページネーションループ — 実行可能な SDK v3・CLI・boto3 のプログラムとしてコピーして戻れます。

DynamoDB クエリビルダーを開く

Console なしで DynamoDB を扱う

DynamoDB では実行できない本物の SQL(JOINs、GROUP BY、集計)を実行する高速な DynamoDB デスクトップクライアント。ビジュアル編集と、あなた自身の Bedrock キーで動く AI エージェントを備えています。

30日間無料トライアル、クレジットカード不要 — その後は期限のない Free プラン。