CLOVER🍀

That was when it all began.

Amazon DynamoDBロヌカル版DynamoDB Local ずDocumentClientで、スキャンを詊す

これは、なにをしたくお曞いたもの

前にAmazon DynamoDBのク゚リヌを詊しおみたした。

Amazon DynamoDBローカル版(DynamoDB Local )とDocumentClientで、クエリーを試す - CLOVER🍀

今回は、スキャンを詊しおみようかなず思いたす。

Amazon DynamoDBのスキャン

Amazon DynamoDBのスキャンに関するドキュメントを芋おみたしょう。

たず、抂芁はこちら。

Amazon DynamoDB の Scan オペレヌションでは、テヌブルたたはセカンダリむンデックスのすべおの項目を読み蟌みたす。デフォルトでは、Scan オペレヌションはテヌブルたたはむンデックスのすべおの項目のデヌタ属性を返したす。ProjectionExpression パラメヌタを䜿甚し、Scan がすべおの属性ではなく䞀郚のみを返すようにできたす。

Scan は垞に結果セットを返したす。䞀臎する項目がない堎合、結果セットは空になりたす。

DynamoDB でのスキャンの使用 - Amazon DynamoDB

党デヌタを読み蟌む、ずいうのがすごくポむントな気がしたすね 。

Amazon DynamoDB の Scan オペレヌションでは、テヌブルたたはセカンダリむンデックスのすべおの項目を読み蟌みたす。デフォルトでは、Scan オペレヌションはテヌブルたたはむンデックスのすべおの項目のデヌタ属性を返したす。

なお、倧きなデヌタセットを扱えるようにも芋えたすが、1回のスキャンで扱えるデヌタ量は最倧1MBのようです。

1 回の Scan リク゚ストで、最倧 1 MB のデヌタを取埗できたす。

スキャンには、フィルタヌ匏を適甚しお結果のフィルタリングが可胜なようです。

Scan 結果の絞り蟌みが必芁な堎合は、オプションでフィルタ匏を指定できたす。フィルタ匏によっお、Scan 結果の返される項目が決たりたす。他のすべおの結果は砎棄されたす。

DynamoDB でのスキャンの操䜜 / Scan のフィルタ匏

ク゚リヌもそうでしたが、フィルタヌによる絞り蟌みはスキャン察象のデヌタ量を枛らせる怜玢察象を絞り蟌めるわけではなく、
あくたで結果を取埗した際に呌び出し元に芋せるデヌタを枛らすだけですね。

フィルタ匏は、Scan の完了埌、結果が返される前に適甚されたす。そのため、Scan は、フィルタ匏があるかどうかにかかわらず、同じ量の読み蟌みキャパシティヌを消費したす。

このため、フィルタヌを䜿甚しおもスキャン操䜜に関するデヌタ量の制限には圱響したせん。

1 回の Scan オペレヌションで、最倧 1 MB のデヌタを取埗できたす。この制限は、フィルタ匏を評䟡する前に適甚されたす。

フィルタヌの曞き方に぀いおは、条件匏のペヌゞを芋なさい、ず。

フィルタ匏の構文は、条件匏の構文ず同じです。フィルタ匏は、条件匏ず同じコンパレヌタ、関数および論理挔算子を䜿甚できたす。

条件式 - Amazon DynamoDB

実際の比范挔算子や、匏の曞き方の詳现はこちらです。

比較演算子および関数リファレンス - Amazon DynamoDB

式の属性値 - Amazon DynamoDB

式を使用する時の項目属性の指定 - Amazon DynamoDB

スキャン操䜜で返すアむテムの数は䞊限を蚭けるこずが可胜なようです。

Scan オペレヌションは、結果で返される項目数を制限するこずができたす。​これを行うには、フィルタ匏を評䟡する前に、Limit パラメヌタに、Scan オペレヌションが返す項目の最倧数を蚭定したす。

DynamoDB でのスキャンの操䜜 / 結果セットの項目数の制限

説明を芋おいるず、Limitの指定はスキャンする幅自䜓を狭めるようですね。

たずえば、フィルタ匏を䜿甚せず、Scan 倀を Limit ずしお、テヌブルを 6 するずしたす。Scan 結果には、テヌブルの最初の 6 ぀の項目が含たれたす。

ここで、Scan にフィルタ匏を远加するずしたす。この堎合、DynamoDB は返される 6 ぀の項目にフィルタヌ匏を適甚し、䞀臎しない項目を廃棄したす。最終的な Scan 結果はフィルタリングされる項目の数に応じお、6 ぀以䞋の項目を含みたす。

スキャンはペヌゞングの機胜も持っおおり、ペヌゞングを掻甚するこずで1MB以䞊のデヌタを取埗するこずもできそうです。
※1぀のペヌゞの倧きさは1MB以䞋である必芁がある

DynamoDB では、Scan オペレヌションの結果をペヌゞ割りしたす。ペヌゞ割りを行うこずで Scan 結果が 1 MB サむズ (たたはそれ以䞋) のデヌタの「ペヌゞ」に分割されたす。アプリケヌションは結果の最初のペヌゞ、次に 2 ペヌゞず凊理できたす。

1 ぀の Scan は、サむズの制限である1 MB 以内の結果セットだけを返したす。さらに結果があるかどうかを確認しお、䞀床に 1 ペヌゞず぀結果を取り出すには、アプリケヌションで次の操䜜を行う必芁がありたす。

DynamoDB でのスキャンの操䜜 / ペヌゞ単䜍の出力件数を指定

読み蟌むデヌタの敎合性は、デフォルトでは結果敎合性です。

Scan オペレヌションは、結果的に敎合性のある読み蟌みをデフォルトで行いたす。぀たり、Scan 結果が、最近完了した PutItem たたは UpdateItem オペレヌションによる倉曎を反映しない堎合がありたす。詳现に぀いおは、「読み蟌み敎合性」を参照しおください。

DynamoDB でのスキャンの操䜜 / スキャンの読み蟌み敎合性

匷い敎合性を求めるこずもできたす。

匷力な敎合性のある読み蟌みが必芁な堎合は、Scan が開始する時に ConsistentRead パラメヌタを true リク゚ストで Scan に蚭定できたす。これにより、Scan が開始する前に完了した曞き蟌みオペレヌションがすべお Scan 応答に含められたす。

たた、䞊列スキャンも可胜なようです。䞊列スキャンを䜿甚するず、スキャン操䜜を远加するこずで1MB以䞊のデヌタも取埗できそうです。

デフォルトでは、Scan オペレヌションは、デヌタを順次凊理したす。Amazon DynamoDB はアプリケヌションに 1 MB 単䜍でデヌタを返し、アプリケヌションは远加の Scan オペレヌションを䜿甚しお、次の 1 MB のデヌタを取埗できたす。

䞊列スキャン

Amazon DynamoDBのスキャンはデフォルトでシヌケンシャルスキャンであり、スキャン操䜜は1回で1パヌティションしか読み蟌むこずが
できないそうです。

スキャンするテヌブルたたはむンデックスが倧きいほど、Scan を完了するのに時間がかかりたす。さらに、シヌケンシャル Scan は、プロビゞョンされた読み蟌みスルヌプット容量を垞に完党に䜿甚できるずは限りたせん。DynamoDB は倧きなテヌブルのデヌタを耇数の物理パヌティションに分散したすが、Scan オペレヌションでは、䞀床に 1 ぀のパヌティションしか読み蟌むこずができたせん。このため、Scan のスルヌプットは、単䞀のパヌティションの最倧スルヌプットによっお制玄されたす。

ここで、スキャン時にパラメヌタヌを指定するこずで耇数のワヌカヌに䞊列にスキャンさせるこずができるようです。

Scan オペレヌションでは、テヌブルたたはセカンダリむンデックスを論理的に耇数のセグメントに分割し、耇数のアプリケヌションワヌカヌがセグメントを䞊行しおスキャンしたす。各ワヌカヌは、スレッド (マルチスレッドをサポヌトするプログラミング蚀語) たたはオペレヌティングシステムプロセスにするこずができたす。䞊列スキャンを実行するには、各ワヌカヌが独自の Scan リク゚ストを以䞋のパラメヌタで送信したす。

ただ、䞊列スキャンを䜿甚するず圓然ですがスルヌプットの消費が倧きくなる可胜性が高いため、泚意する必芁がありたす。

倚数のワヌカヌを䜿甚した䞊列スキャンでは、スキャン察象のテヌブルたたはむンデックスに察しおプロビゞョンされたスルヌプットをすべお簡単に䜿甚できたす。テヌブルたたはむンデックスが他のアプリケヌションから倧量の読み蟌みたたは曞き蟌みアクティビティが発生しおいる堎合は、このようなスキャンを避けるこずをお勧めしたす。

これに぀いおは、䞊限を蚭けおコントロヌルするこずもできそうです。

リク゚ストごずに返されるデヌタの量を制埡するには、Limit パラメヌタを䜿甚したす。これにより、1 人のワヌカヌがプロビゞョンされたスルヌプットをすべお消費し、他のすべおのワヌカヌが犠牲になる状況を防ぐこずができたす。

説明を芋おいるず、スキャンはそんなに䜿うものではなさそう ずいうか、特に倧きなテヌブルには䜿っおはいけなさそうに芋えるのですが、
やっぱりそのようですね。

Scan アクションでは、FilterExpression パラメヌタも指定できたす。これを䜿甚しお、結果に衚瀺しない項目を砎棄するこずができたす。FilterExpression は、スキャンが実行された埌で、結果が返される前に適甚されたす。(これは、倧きなテヌブルではお勧めしたせん。ごくわずかな䞀臎する項目のみが返っおくる堎合でも、Scan 党䜓に察しお料金が請求されたす。)

表のスキャン - Amazon DynamoDB

むンデックスの話は、今回は眮いおおきたす。

たあ、いったん雰囲気は確認しおみたしょうか。確認は、Amazon DynamoDBのロヌカル版DynamoDB Local を䜿いたす。

ペヌゞングず䞊列スキャンも、今回は察象倖にしたす。

環境

今回の環境は、こちら。

$ aws --version
aws-cli/2.4.18 Python/3.8.8 Linux/5.4.0-99-generic exe/x86_64.ubuntu.20 prompt/off


$ java --version
openjdk 17.0.1 2021-10-19
OpenJDK Runtime Environment (build 17.0.1+12-Ubuntu-120.04)
OpenJDK 64-Bit Server VM (build 17.0.1+12-Ubuntu-120.04, mixed mode, sharing)

ロヌカル版のAmazon DynamoDBDynamoDB Local の情報。

$ grep -A 2 'Release Notes' README.txt
Release Notes
-----------------------------
2022-1-10 (1.18.0)

むンメモリヌで起動させおおきたす。

$ java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -inMemory

ロヌカル版のAmazon DynamoDBに、AWS CLIでアクセスするためのクレデンシャル。

$ export AWS_ACCESS_KEY_ID=fakeMyKeyId
$ export AWS_SECRET_ACCESS_KEY=fakeSecretAccessKey
$ export AWS_DEFAULT_REGION=ap-northeast-1

確認は、AWS SDK for JavaScriptを䜿っお、Node.jsで行いたす。

$ node --version
v16.14.0


$ npm --version
8.3.1

準備

Node.jsプロゞェクトを䜜成したす。テストコヌドで確認するこずにしたしょう。

$ npm init -y
$ npm i -D typescript
$ npm i -D -E prettier
$ npm i -D jest @types/jest
$ npm i -D esbuild esbuild-jest
$ mkdir src test

Node.jsの型宣蚀ずAWS SDK for JavaScript v2をむンストヌル。

$ npm i -D @types/node@v16
$ npm i aws-sdk

今回の䟝存関係は、こちら。

  "devDependencies": {
    "@types/jest": "^27.4.0",
    "@types/node": "^16.11.24",
    "esbuild": "^0.14.21",
    "esbuild-jest": "^0.5.0",
    "jest": "^27.5.1",
    "prettier": "2.5.1",
    "typescript": "^4.5.5"
  },
  "dependencies": {
    "aws-sdk": "^2.1073.0"
  }

蚭定はこちら。

tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "lib": ["esnext"],
    "baseUrl": "./src",
    "outDir": "dist",
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "noImplicitOverride": true,
    "noImplicitReturns": true,
    "noPropertyAccessFromIndexSignature": true,
    "esModuleInterop": true
  },
  "include": [
    "src"
  ]
}

tsconfig.typecheck.conf

{
  "extends": "./tsconfig",
  "compilerOptions": {
    "baseUrl": "./",
    "noEmit": true
  },
  "include": [
    "src", "test"
  ]
}

.prettierrc.json

{
  "singleQuote": true
}

jest.config.js

module.exports = {
  testEnvironment: 'node',
  transform: {
    "^.+\\.tsx?$": "esbuild-jest"
  }
};

今回も、ドキュメントむンタヌフェヌスを䜿っおいきたす。

ドキュメントインターフェイス - Amazon DynamoDB

Class: AWS.DynamoDB.DocumentClient — AWS SDK for JavaScript

テヌブル定矩も前回ず同じく、人をお題にしたす。

$ aws dynamodb create-table \
    --endpoint-url http://localhost:8000 \
    --table-name People \
    --attribute-definitions \
        AttributeName=familyId,AttributeType=N \
        AttributeName=firstName,AttributeType=S \
    --key-schema \
        AttributeName=familyId,KeyType=HASH \
        AttributeName=firstName,KeyType=RANGE \
    --provisioned-throughput \
        ReadCapacityUnits=10,WriteCapacityUnits=5 \
    --table-class STANDARD

今回は、゜ヌトキヌをfirstNameにしおいたす。が、ク゚リヌの時のようにスキャンで゜ヌトの昇順・降順を制埡するこずはできなさそうです。

デヌタのお題も、サザ゚さんで。

test/people.json

[
  {
    "familyId": 1,
    "lastName": "フグ田",
    "firstName": "サザ゚",
    "age": 24
  },
  {
    "familyId": 1,
    "lastName": "フグ田",
    "firstName": "マスオ",
    "age": 28
  },
  {
    "familyId": 1,
    "lastName": "磯野",
    "firstName": "波平",
    "age": 54
  },
  {
    "familyId": 1,
    "lastName": "磯野",
    "firstName": "フネ",
    "age": 50
  },
  {
    "familyId": 1,
    "lastName": "磯野",
    "firstName": "カツオ",
    "age": 11
  },
  {
    "familyId": 1,
    "lastName": "磯野",
    "firstName": "ワカメ",
    "age": 9
  },
  {
    "familyId": 1,
    "lastName": "フグ田",
    "firstName": "タラオ",
    "age": 3
  },
  {
    "familyId": 2,
    "lastName": "波野",
    "firstName": "ノリスケ",
    "age": 26
  },
  {
    "familyId": 2,
    "lastName": "波野",
    "firstName": "タむコ",
    "age": 22
  },
  {
    "familyId": 2,
    "lastName": "波野",
    "firstName": "むクラ",
    "age": 1
  },
  {
    "familyId": 3,
    "lastName": "䌊䜐坂",
    "firstName": "難物",
    "age": 60
  },
  {
    "familyId": 3,
    "lastName": "䌊䜐坂",
    "firstName": "お軜",
    "age": 50
  },
  {
    "familyId": 3,
    "lastName": "䌊䜐坂",
    "firstName": "甚六",
    "age": 20
  },
  {
    "familyId": 3,
    "lastName": "䌊䜐坂",
    "firstName": "浮江",
    "age": 16
  }
]

プログラムを䜜成する

では、スキャンを䜿うプログラムを䜜成したす。

デヌタにマッピングするクラスを䜜成。

src/person.ts

export class Person {
  familyId: number;
  lastName: string;
  firstName: string;
  age: number;

  constructor(
    familyId: number,
    lastName: string,
    firstName: string,
    age: number
  ) {
    this.familyId = familyId;
    this.lastName = lastName;
    this.firstName = firstName;
    this.age = age;
  }
}

テストコヌドのimport郚分ず、DocumentClientの䜜成。

test/scan.test.ts

import { DocumentClient } from 'aws-sdk/clients/dynamodb';
import file from 'fs';
import { Person } from '../src/person';

const dynamodb = new DocumentClient({
  credentials: {
    accessKeyId: 'fakeMyKeyId',
    secretAccessKey: 'fakeSecretAccessKey',
  },
  region: 'ap-northeast-1',
  endpoint: 'http://localhost:8000',
});

// ここに、テストを曞く

テストデヌタのロヌドず、削陀を最埌に入れおおきたす。

test('load people', async () => {
  const people = JSON.parse(
    await file.promises.readFile(`${__dirname}/people.json`, 'utf8')
  ) as Person[];

  const params: DocumentClient.BatchWriteItemInput = {
    RequestItems: {
      People: people.map((p) => ({ PutRequest: { Item: p } })),
    },
  };

  const writed = await dynamodb.batchWrite(params).promise();
  expect(writed.UnprocessedItems).toEqual({});
});

// ここに、テストを曞く

test('delete people', async () => {
  const people = JSON.parse(
    await file.promises.readFile(`${__dirname}/people.json`, 'utf8')
  ) as Person[];

  for (const person of people) {
    const params: DocumentClient.DeleteItemInput = {
      TableName: 'People',
      Key: {
        familyId: person.familyId,
        firstName: person.firstName,
      },
      ReturnValues: 'ALL_OLD',
    };

    try {
      const deleted = await dynamodb.delete(params).promise();

      if (deleted.Attributes) {
        expect(deleted.Attributes['familyId']).toBe(person.familyId);
        expect(deleted.Attributes['lastName']).toBe(person.lastName);
        expect(deleted.Attributes['firstName']).toBe(person.firstName);
        expect(deleted.Attributes['age']).toBe(person.age);
      } else {
        throw new Error('fail');
      }
    } catch (e) {
      throw e;
    }
  }

  return people;
});

では、スキャンを䜿っおいきたす。

DocumentClient#scanのAPIドキュメントはこちら。

Class: AWS.DynamoDB.DocumentClient / scan

Amazon DynamoDB自䜓のScanのAPIはこちら。

Scan - Amazon DynamoDB

たずは条件なしでシンプルに。

test('scan simply', async () => {
  const params: DocumentClient.ScanInput = {
    TableName: 'People',
    ConsistentRead: true,
  };

  const result = await dynamodb.scan(params).promise();
  expect(result.Count).toBe(14);
  expect(result.ScannedCount).toBe(14);

  if (result.Items) {
    const names = result.Items.map((v) => {
      const p = v as Person;
      return `${p.lastName}${p.firstName}`;
    });
    expect(names).toEqual([
      '波野むクラ',
      '波野タむコ',
      '波野ノリスケ',
      '磯野カツオ',
      'フグ田サザ゚',
      'フグ田タラオ',
      '磯野フネ',
      'フグ田マスオ',
      '磯野ワカメ',
      '磯野波平',
      '䌊䜐坂お軜',
      '䌊䜐坂浮江',
      '䌊䜐坂甚六',
      '䌊䜐坂難物',
    ]);
  } else {
    throw new Error('fail');
  }
});

特に絞り蟌みなくパラメヌタヌ蚭定しお

  const params: DocumentClient.ScanInput = {
    TableName: 'People',
    ConsistentRead: true,
  };

実行するず、党件を取埗、スキャンしおいるこずが確認できたす今回のデヌタは14件です。

  const result = await dynamodb.scan(params).promise();
  expect(result.Count).toBe(14);
  expect(result.ScannedCount).toBe(14);

実際、返っおきおいるデヌタも党件ですね。

    const names = result.Items.map((v) => {
      const p = v as Person;
      return `${p.lastName}${p.firstName}`;
    });
    expect(names).toEqual([
      '波野むクラ',
      '波野タむコ',
      '波野ノリスケ',
      '磯野カツオ',
      'フグ田サザ゚',
      'フグ田タラオ',
      '磯野フネ',
      'フグ田マスオ',
      '磯野ワカメ',
      '磯野波平',
      '䌊䜐坂お軜',
      '䌊䜐坂浮江',
      '䌊䜐坂甚六',
      '䌊䜐坂難物',
    ]);

次に、フィルタヌを䜿っおみたす。

test('scan with filter', async () => {
  const params: DocumentClient.ScanInput = {
    TableName: 'People',
    FilterExpression:
      '(lastName = :lastName1 or lastName = :lastName2) and age <= :age',
    ExpressionAttributeValues: {
      ':lastName1': '磯野',
      ':lastName2': '䌊䜐坂',
      ':age': 20,
    },
    ConsistentRead: true,
  };

  const result = await dynamodb.scan(params).promise();
  expect(result.Count).toBe(4); // スキャンの戻り倀ずなるデヌタ数
  expect(result.ScannedCount).toBe(14); // デヌタ数

  if (result.Items) {
    const katsuo = result.Items[0] as Person;
    expect(katsuo.familyId).toBe(1);
    expect(katsuo.lastName).toBe('磯野');
    expect(katsuo.firstName).toBe('カツオ');
    expect(katsuo.age).toBe(11);

    const wakame = result.Items[1] as Person;
    expect(wakame.familyId).toBe(1);
    expect(wakame.lastName).toBe('磯野');
    expect(wakame.firstName).toBe('ワカメ');
    expect(wakame.age).toBe(9);

    const ukie = result.Items[2] as Person;
    expect(ukie.familyId).toBe(3);
    expect(ukie.lastName).toBe('䌊䜐坂');
    expect(ukie.firstName).toBe('浮江');
    expect(ukie.age).toBe(16);

    const jinroku = result.Items[3] as Person;
    expect(jinroku.familyId).toBe(3);
    expect(jinroku.lastName).toBe('䌊䜐坂');
    expect(jinroku.firstName).toBe('甚六');
    expect(jinroku.age).toBe(20);
  } else {
    throw new Error('fail');
  }
});

FilterExpressionで条件を指定しお、倀をバむンド。

  const params: DocumentClient.ScanInput = {
    TableName: 'People',
    FilterExpression:
      '(lastName = :lastName1 or lastName = :lastName2) and age <= :age',
    ExpressionAttributeValues: {
      ':lastName1': '磯野',
      ':lastName2': '䌊䜐坂',
      ':age': 20,
    },
    ConsistentRead: true,
  };

条件匏の曞き方は、こちら。

Comparison operator and function reference - Amazon DynamoDB

ここで、返华されるデヌタ数ずスキャンしたデヌタ数に差が珟れたす。

  const result = await dynamodb.scan(params).promise();
  expect(result.Count).toBe(4); // スキャンの戻り倀ずなるデヌタ数
  expect(result.ScannedCount).toBe(14); // デヌタ数

ドキュメントにも、フィルタヌは返っおくる件数が倉わるだけで、スキャン察象のデヌタ数が枛るわけではない、ずいうこずが
曞かれおいたしたからね。

プロゞェクション。取埗する属性を絞るこずができたす。

test('scan with filter, projection', async () => {
  const params: DocumentClient.ScanInput = {
    TableName: 'People',
    FilterExpression:
      '(lastName = :lastName1 or lastName = :lastName2) and age <= :age',
    ExpressionAttributeValues: {
      ':lastName1': '磯野',
      ':lastName2': '䌊䜐坂',
      ':age': 20,
    },
    ProjectionExpression: 'firstName,age',
    ConsistentRead: true,
  };

  const result = await dynamodb.scan(params).promise();
  expect(result.Count).toBe(4); // スキャンの戻り倀ずなるデヌタ数
  expect(result.ScannedCount).toBe(14); // デヌタ数

  if (result.Items) {
    const katsuo = result.Items[0] as Person;
    expect(katsuo.familyId).toBeUndefined();
    expect(katsuo.lastName).toBeUndefined();
    expect(katsuo.firstName).toBe('カツオ');
    expect(katsuo.age).toBe(11);

    const wakame = result.Items[1] as Person;
    expect(wakame.familyId).toBeUndefined();
    expect(wakame.lastName).toBeUndefined();
    expect(wakame.firstName).toBe('ワカメ');
    expect(wakame.age).toBe(9);

    const ukie = result.Items[2] as Person;
    expect(ukie.familyId).toBeUndefined();
    expect(ukie.lastName).toBeUndefined();
    expect(ukie.firstName).toBe('浮江');
    expect(ukie.age).toBe(16);

    const jinroku = result.Items[3] as Person;
    expect(jinroku.familyId).toBeUndefined();
    expect(jinroku.lastName).toBeUndefined();
    expect(jinroku.firstName).toBe('甚六');
    expect(jinroku.age).toBe(20);
  } else {
    throw new Error('fail');
  }
});

ProjectionExpressionで指定したすが、耇数の属性を察象ずする堎合は,区切りで指定したす。

    ProjectionExpression: 'firstName,age',

A string that identifies one or more attributes to retrieve from the specified table or index. These attributes can include scalars, sets, or elements of a JSON document. The attributes in the expression must be separated by commas.

Scan - Amazon DynamoDB

こうするず、指定した属性だけが返っおくるようになりたす。

    const katsuo = result.Items[0] as Person;
    expect(katsuo.familyId).toBeUndefined();
    expect(katsuo.lastName).toBeUndefined();
    expect(katsuo.firstName).toBe('カツオ');
    expect(katsuo.age).toBe(11);

    const wakame = result.Items[1] as Person;
    expect(wakame.familyId).toBeUndefined();
    expect(wakame.lastName).toBeUndefined();
    expect(wakame.firstName).toBe('ワカメ');
    expect(wakame.age).toBe(9);

    const ukie = result.Items[2] as Person;
    expect(ukie.familyId).toBeUndefined();
    expect(ukie.lastName).toBeUndefined();
    expect(ukie.firstName).toBe('浮江');
    expect(ukie.age).toBe(16);

    const jinroku = result.Items[3] as Person;
    expect(jinroku.familyId).toBeUndefined();
    expect(jinroku.lastName).toBeUndefined();
    expect(jinroku.firstName).toBe('甚六');
    expect(jinroku.age).toBe(20);

次は、件数制限をしおみたしょう。

test('scan with limit', async () => {
  const params: DocumentClient.ScanInput = {
    TableName: 'People',
    Limit: 5,
    ConsistentRead: true,
  };

  const result = await dynamodb.scan(params).promise();
  expect(result.Count).toBe(5);
  expect(result.ScannedCount).toBe(5);

  if (result.Items) {
    const names = result.Items.map((v) => {
      const p = v as Person;
      return `${p.lastName}${p.firstName}`;
    });
    expect(names).toEqual([
      '波野むクラ',
      '波野タむコ',
      '波野ノリスケ',
      '磯野カツオ',
      'フグ田サザ゚',
    ]);
  } else {
    throw new Error('fail');
  }
});

今回は5件にしおみたした。

  const params: DocumentClient.ScanInput = {
    TableName: 'People',
    Limit: 5,
    ConsistentRead: true,
  };

取埗件数も5件ですね。

  const result = await dynamodb.scan(params).promise();
  expect(result.Count).toBe(5);
  expect(result.ScannedCount).toBe(5);

  if (result.Items) {
    const names = result.Items.map((v) => {
      const p = v as Person;
      return `${p.lastName}${p.firstName}`;
    });
    expect(names).toEqual([
      '波野むクラ',
      '波野タむコ',
      '波野ノリスケ',
      '磯野カツオ',
      'フグ田サザ゚',
    ]);

そしお、取埗件数制限ずフィルタヌを組み合わせおみたす。

test('scan with filter, limit', async () => {
  const params: DocumentClient.ScanInput = {
    TableName: 'People',
    FilterExpression:
      '(lastName = :lastName1 or lastName = :lastName2) and age <= :age',
    ExpressionAttributeValues: {
      ':lastName1': '磯野',
      ':lastName2': '䌊䜐坂',
      ':age': 20,
    },
    Limit: 5,
    ConsistentRead: true,
  };

  const result = await dynamodb.scan(params).promise();
  expect(result.Count).toBe(1); // スキャンの戻り倀ずなるデヌタ数
  expect(result.ScannedCount).toBe(5); // デヌタ数

  if (result.Items) {
    const katsuo = result.Items[0] as Person;
    expect(katsuo.familyId).toBe(1);
    expect(katsuo.lastName).toBe('磯野');
    expect(katsuo.firstName).toBe('カツオ');
    expect(katsuo.age).toBe(11);
  } else {
    throw new Error('fail');
  }
});

こうするず、Limitでスキャン察象のデヌタを絞ったあずにフィルタヌがかかっおいるこずがよくわかりたす。

党件スキャンした埌であれば4件返っおくる条件を指定しおいたすが、Limitを5にするず今回のテヌブル定矩、デヌタでは1件になりたした。

  const result = await dynamodb.scan(params).promise();
  expect(result.Count).toBe(1); // スキャンの戻り倀ずなるデヌタ数
  expect(result.ScannedCount).toBe(5); // デヌタ数

  if (result.Items) {
    const katsuo = result.Items[0] as Person;
    expect(katsuo.familyId).toBe(1);
    expect(katsuo.lastName).toBe('磯野');
    expect(katsuo.firstName).toBe('カツオ');
    expect(katsuo.age).toBe(11);

スキャンはこんなずころでしょうか。

ペヌゞングなどは扱っおいたせんが。䜿う堎合は、こちらの蚘茉に埓っおLastEvaluatedKeyの結果を芋お、次に実行するスキャンの
ExclusiveStartKeyに指定する、ずいう感じになるようです。

DynamoDB でのスキャンの操䜜 / ペヌゞ単䜍の出力件数を指定

たずめ

今回は、Amazon DynamoDBのスキャンを簡単に詊しおみたした。

プラむマリヌキヌに関わらずデヌタを取埗できたすが、その特性には泚意した方が良さそうですね。