CLOVER🍀

That was when it all began.

Amazon DynamoDBのローカル版を試してみる

これは、なにをしたくて書いたもの?

Amazon DynamoDBは使ったことがないのですが、ローカルで動かせるバージョンがあるようなので、試しておきたいなということで。

Amazon DynamoDB とは - Amazon DynamoDB

Amazon DynamoDB

そもそも、Amazon DynamoDBを知らないので、まずは軽くドキュメントを眺めてみます。

Amazon DynamoDB とは - Amazon DynamoDB

特徴は、こちらの配下のページを見るのが良さそうです。

Amazon DynamoDB: 仕組み - Amazon DynamoDB

ざっと見ると、こんなところでしょうか。

  • 基本的なコンポーネントとして、テーブル、アイテム、属性がある
    • アイテムはいわゆるレコード、属性はカラムやフィールドに相当
    • テーブルはリージョン内で一意である必要がある
    • 属性はネストが可能
  • プライマリーキーは、パーティションキー、パーティションキー+ソートキー(複合プライマリーキー)の2種類から選択可能
  • プライマリーキー以外は、スキーマレスである
  • テーブルにセカンダリインデックスを定義できる
  • PartiQLというクエリ言語を使うか、CRUD APIを使ってデータにアクセスできる
  • トランザクションが利用できる
  • データ型にはスカラー(数値、文字列、バイナリ、ブール、null)とドキュメント(List、Map、Sets)がある
  • AZ間でレプリケーションされており、読み込み時の整合性を調整可能
  • 読み書きのスループットを調整可能
  • DynamoDB Streamsという、データ変更イベントを受け取る仕組みがある

Amazon DynamoDBとRDBMSの違いは、こちらのドキュメントを参照。

SQL から NoSQL へ - Amazon DynamoDB

ローカル版のAmazon DynamoDB

Amazon DynamoDBのローカル版は、Java 6以上が動作要件のようです。

DynamoDB ローカル (ダウンロード可能バージョン) のセットアップ - Amazon DynamoDB

ローカル版のAmazon DynamoDBに関する注意事項は、こちら。

DynamoDB Local の使用に関する注意事項 - Amazon DynamoDB

データの保存先はファイル(単一またはAWSアクセスキーIDとリージョンの組み合わせ)かメモリになるようです。
また、実際にAWSで動作するバージョンといくつか違いもあるようです。

ドキュメントを見るのはこんなところにして、とりあえず使ってみましょう。

環境

今回の環境は、こちら。

$ 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)


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

動作確認は、プログラムからのアクセスで行うことにします。Node.jsを使うことにしました。

$ node --version
v16.13.2

ローカル版Amazon DynamoDBをインストールする

ローカル版のAmazon DynamoDBは、こちらのドキュメントに沿って取得できます。

コンピュータ上で DynamoDB をローカルでデプロイする - Amazon DynamoDB

ローカルにファイルをダウンロードする方法、Dockerイメージを使う方法、Mavenアーティファクトとして取得する方法の3つがあるようですが、
今回は素直にダウンロードしてみます。

$ curl -OL https://s3.ap-northeast-1.amazonaws.com/dynamodb-local-tokyo/dynamodb_local_latest.tar.gz

展開。

$ tar xf dynamodb_local_latest.tar.gz

こんな感じに展開されます。

$ ll
合計 48736
drwxrwxr-x 3 xxxxx xxxxx     4096  1月 28 01:46 ./
drwxrwxr-x 7 xxxxx xxxxx     4096  1月 28 01:45 ../
-rw-r--r-- 1 xxxxx xxxxx  5863882  1月 11 03:08 DynamoDBLocal.jar
drwxr-xr-x 2 xxxxx xxxxx     4096  1月 28 01:46 DynamoDBLocal_lib/
-rw-r--r-- 1 xxxxx xxxxx     9450  1月 11 03:06 LICENSE.txt
-rw-r--r-- 1 xxxxx xxxxx     4873  1月 11 03:06 README.txt
-rw-r--r-- 1 xxxxx xxxxx    29753  1月 11 03:06 THIRD-PARTY-LICENSES.txt
-rw-rw-r-- 1 xxxxx xxxxx 43970290  1月 28 01:46 dynamodb_local_latest.tar.gz

DynamoDBLocal_libというディレクトリには、ローカル版のAmazon DynamoDBが依存すると思われるJARファイル、.soファイル、
.dllファイルが含まれています。

起動は、以下のコマンドで行います。

$ java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar [オプション]

ヘルプ。

$ java -jar DynamoDBLocal.jar -help
usage: java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar
            [-port <port-no.>] [-inMemory] [-delayTransientStatuses]
            [-dbPath <path>][-sharedDb] [-cors <allow-list>]
 -cors <arg>                Enable CORS support for javascript against a
                            specific allow-list list the domains separated
                            by , use '*' for public access (default is
                            '*')
 -dbPath <path>             Specify the location of your database file.
                            Default is the current directory.
 -delayTransientStatuses    When specified, DynamoDB Local will introduce
                            delays to hold various transient table and
                            index statuses so that it simulates actual
                            service more closely. Currently works only for
                            CREATING and DELETING online index statuses.
 -help                      Display DynamoDB Local usage and options.
 -inMemory                  When specified, DynamoDB Local will run in
                            memory.
 -optimizeDbBeforeStartup   Optimize the underlying backing store database
                            tables before starting up the server
 -port <port-no.>           Specify a port number. Default is 8000
 -sharedDb                  When specified, DynamoDB Local will use a
                            single database instead of separate databases
                            for each credential and region. As a result,
                            all clients will interact with the same set of
                            tables, regardless of their region and
                            credential configuration. (Useful for
                            interacting with Local through the JS Shell in
                            addition to other SDKs)

バージョンがわからないのですが、同梱されているREADME.mdに書かれているものを確認すればよいのでしょうか。

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

データの保存先は、以下の3パターンがあります。

  • -sharedDbオプションを付与して起動する … shared-local-instance.dbという単一のファイルに全クライアントのデータを保存する
  • -sharedDbオプションを付与しない … [AWSアクセスキーID]_[リージョン名].dbというファイルに、AWSアクセスキーIDおよびリージョン単位にデータが保存される
  • -inMemoryオプションを付与する … データは、インメモリーに保存される

データをインメモリーに保持した場合はローカル版のAmazon DynamoDBを再起動するとデータがなくなりますし、ファイルに保存している場合は
当然ですがファイルを削除するとデータがなくなります。

今回はインメモリーで起動してみます。

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

ポート8000でリッスンするようですね。

Port:   8000
InMemory:       true
DbPath: null
SharedDb:       false
shouldDelayTransientStatuses:   false
CorsParams:     *

AWS CLIでアクセスする

ローカル版のAmazon DynamoDBが起動したので、AWS CLIでアクセスしてみましょう。

ドキュメントを見ると、クレデンシャルは設定する必要がありそうなので

コンピュータ上で DynamoDB をローカルでデプロイする - Amazon DynamoDB

環境変数で設定。

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

--endpoint-urlをローカル版のAmazon DynamoDBに指定すると、アクセスできます。

$ aws dynamodb list-tables --endpoint-url http://localhost:8000
{
    "TableNames": []
}

続いて、テーブルを作成してみましょう。

ステップ 1: テーブルを作成する - Amazon DynamoDB

ドキュメントに沿って、--endpoint-urlを指定しつつテーブルを作成。

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

各パラメーターの意味は、APIリファレンスから確認できます。

CreateTable - Amazon DynamoDB

テーブルの作成を確認。

$ aws dynamodb describe-table --endpoint-url http://localhost:8000 --table-name Music | grep TableStatus
        "TableStatus": "ACTIVE",

で、ここの定義の意味がわからないまま進んでも意味がない気がするので、確認しておきましょう。

    --table-name Music \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --provisioned-throughput \
        ReadCapacityUnits=10,WriteCapacityUnits=5 \
    --table-class STANDARD

参照するのはこちらです。

CreateTable - Amazon DynamoDB

AttributeDefinition - Amazon DynamoDB

読み込み/書き込みキャパシティーモード - Amazon DynamoDB

テーブルクラス - Amazon DynamoDB

指定している各オプションの意味は、以下の意味のようです。

  • --table-name … テーブル名
  • --key-schema … テーブル(またはインデックス)の主キーの定義
  • --attribute-definitions … 属性の定義
  • --provisioned-throughput … テーブル(またはインデックス)のプロビジョニングされたスループットを指定
    • --billing-modeで選択可能なPROVISIONED(デフォルト)とPAY_PER_REQUESTのうち、PROVISIONEDを選択した場合は指定が必須
  • --table-class … テーブルクラス。STANDARDとSTANDARD_INFREQUENT_ACCESSから選択

ということは、テーブルMusicを以下の定義で作っていることになります。

  • Artistをパーティションキー、SongTitleをソートキーとする複合主キー
  • Artist、SongTitleはともに文字列型
  • 秒間あたりのスループットは、それぞれ(強い一貫性を持つ)読み取りの最大数が10、書き込みの最大数が5
  • テーブルクラスは標準

Amazon DynamoDBをJavaScript(Node.js)から操作してみる

テーブルを作成したので、プログラムからアクセスしてみましょう。今回は、JavaScript(Node.js)+TypeScriptで扱うことにします。

テーブル作成以降のこちらのステップを、CRUD程度まで行っていましょう。
※AWS SDK for JavaScriptを使ったドキュメントもありますが、そちらは後で

DynamoDB の使用開始 - Amazon DynamoDB

AWS SDK for JavaScriptは、v2を使うことにします。

AWS SDK for JavaScript とは - AWS SDK for JavaScript

File: README — AWS SDK for JavaScript

Class: AWS.DynamoDB — AWS SDK for JavaScript

環境は、こちら。

$ node --version
v16.13.2


$ npm --version
8.1.2

Node.jsプロジェクトのセットアップを行います。確認はテストコードで行うので、Jestまでインストール。

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

AWS SDK for JavaScript v2のインストール。

$ npm i aws-sdk

今回の依存関係は、このようになりました。

  "devDependencies": {
    "@types/jest": "^27.4.0",
    "@types/node": "^16.11.21",
    "esbuild": "^0.14.14",
    "esbuild-jest": "^0.5.0",
    "jest": "^27.4.7",
    "prettier": "2.5.1",
    "typescript": "^4.5.5"
  },
  "dependencies": {
    "aws-sdk": "^2.1066.0"
  }

TypeScriptの設定。

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"
  ]
}

Prettierの設定。

.prettierrc.json

{
  "singleQuote": true
}

Jestの設定。

jest.config.js

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

AWS SDK for JavaScript v2はTypeScriptをサポートしているようなのですが、型宣言の情報に困ったらこちらを見ましょう。

https://github.com/aws/aws-sdk-js/blob/v2.1066.0/clients/dynamodb.d.ts

それで、今回なぞっていくのですが、AWS SDKでDynamoDBにアクセスするAPIは3種類あり、AWS SDK for JavaScriptは
低レベルインターフェースとドキュメントインターフェースの2種類をサポートしています。

低レベルインターフェイス - Amazon DynamoDB

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

オブジェクト永続性インターフェイス - Amazon DynamoDB

まずは、低レベルインターフェースの方を使って、AWS CLIのドキュメントを模倣していきます。

ローカル版のAmazon DynamoDBに接続する準備

まずは、必要なモジュールのインポートとローカル版のAmazon DynamoDBに接続する準備をしましょう。こんな感じになりました。

test/dynamodb.test.ts

import AWS from 'aws-sdk';
import {
  DeleteItemInput,
  GetItemInput,
  PutItemInput,
  QueryInput,
  UpdateItemInput,
} from 'aws-sdk/clients/dynamodb';

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

// ここに、テストを書く!

エンドポイントの指定が必要になりますね。

では、進めていきます。

データを書き込む

最初は、書き込みから。

ステップ 2: コンソールまたは AWS CLI を使用して、テーブルにデータを書き込む - Amazon DynamoDB

ドキュメントどおり、2件書き込んでみます。これはDynamoDB#putItemで行います。

test('write items', async () => {
  const params1: PutItemInput = {
    TableName: 'Music',
    Item: {
      Artist: {
        S: 'No One You Know',
      },
      SongTitle: {
        S: 'Call Me Today',
      },
      AlbumTitle: {
        S: 'Somewhat Famous',
      },
      Awards: {
        N: '1',
      },
    },
  };

  await dynamodb.putItem(params1).promise();

  const params2: PutItemInput = {
    TableName: 'Music',
    Item: {
      Artist: {
        S: 'Acme Band',
      },
      SongTitle: {
        S: 'Happy Day',
      },
      AlbumTitle: {
        S: 'Songs About Life',
      },
      Awards: {
        N: '10',
      },
    },
  };

  await dynamodb.putItem(params2).promise();
});

SとかNは、各属性の値が文字列型や数値型であることを表しています。

データを読み込む

データの読み込み。

ステップ 3: テーブルからデータを読み込む - Amazon DynamoDB

DynamoDB#readItemで、キーを指定して読み込む形になるようです。

test('read item', async () => {
  const params: GetItemInput = {
    TableName: 'Music',
    Key: {
      Artist: {
        S: 'Acme Band',
      },
      SongTitle: {
        S: 'Happy Day',
      },
    },
    ConsistentRead: true,
  };

  const item = await dynamodb.getItem(params).promise();
  if (item.Item) {
    expect(item.Item['AlbumTitle']).toEqual({
      S: 'Songs About Life',
    });
    expect(item.Item['Awards']).toEqual({ N: '10' });
  } else {
    throw new Error('fail');
  }
});

ちなみに今回は複合主キーなので、その指定が中途半端だとエラーになります。以下はパーティションキーのみを指定して、ソートキーを
していませんが、これは失敗します。

test('read item, fail', async () => {
  const params: GetItemInput = {
    TableName: 'Music',
    Key: {
      Artist: {
        S: 'Acme Band',
      },
    },
    ConsistentRead: true,
  };

  try {
    const item = await dynamodb.getItem(params).promise();

    throw new Error('fail');
  } catch (e) {
    const error = e as Error;
    expect(error.message).toBe(
      'The number of conditions on the keys is invalid'
    );
  }
});

データを更新する

データの更新。

ステップ 4: テーブルのデータを更新する - Amazon DynamoDB

DynamoDB#updateItemで行います。SQLのupdate文みたいな感じですね。

test('update item', async () => {
  const params: UpdateItemInput = {
    TableName: 'Music',
    Key: {
      Artist: {
        S: 'Acme Band',
      },
      SongTitle: {
        S: 'Happy Day',
      },
    },
    ExpressionAttributeNames: {
      '#AT': 'AlbumTitle',
    },
    ExpressionAttributeValues: {
      ':NEWVAL': {
        S: 'Updated Album Title',
      },
    },
    UpdateExpression: 'SET #AT = :NEWVAL',
    ReturnValues: 'ALL_NEW',
  };

  const returnValue = await dynamodb.updateItem(params).promise();
  expect(returnValue.Attributes).toEqual({
    Artist: {
      S: 'Acme Band',
    },
    SongTitle: {
      S: 'Happy Day',
    },
    AlbumTitle: {
      S: 'Updated Album Title',
    },
    Awards: {
      N: '10',
    },
  });
});

なお、あるアイテムを単純に置き換えるだけでよいなら、DynamoDB#putItemでOKなようです。

Creates a new item, or replaces an old item with a new item. If an item that has the same primary key as the new item already exists in the specified table, the new item completely replaces the existing item.

Class: AWS.DynamoDB / putItem

データをクエリする

データに対してクエリを実行してみます。

ステップ 5: テーブルのデータをクエリする - Amazon DynamoDB

これは、DynamoDB#queryで行います。

test('query', async () => {
  const params: QueryInput = {
    TableName: 'Music',
    KeyConditionExpression: 'Artist = :name',
    ExpressionAttributeValues: {
      ':name': {
        S: 'Acme Band',
      },
    },
  };

  const result = await dynamodb.query(params).promise();
  expect(result.Count).toBe(1);
  expect(result.ScannedCount).toBe(1);
  expect(result.ConsumedCapacity).toBeUndefined();
  expect(result.Items).toEqual([
    {
      Artist: {
        S: 'Acme Band',
      },
      SongTitle: {
        S: 'Happy Day',
      },

      AlbumTitle: {
        S: 'Updated Album Title',
      },
      Awards: {
        N: '10',
      },
    },
  ]);
});
データを削除する

最後はGetting Startedのドキュメントには記載がありますが、データを削除してみます。

これは、DynamoDB#deleteItemで行います。

test('delete item', async () => {
  const params: DeleteItemInput = {
    TableName: 'Music',
    Key: {
      Artist: {
        S: 'Acme Band',
      },
      SongTitle: {
        S: 'Happy Day',
      },
    },
  };

  const value = await dynamodb.deleteItem(params).promise();
  expect(value).not.toBeNull();

  const readParms: GetItemInput = {
    TableName: 'Music',
    Key: {
      Artist: {
        S: 'Acme Band',
      },
      SongTitle: {
        S: 'Happy Day',
      },
    },
    ConsistentRead: true,
  };

  const item = await dynamodb.getItem(params).promise();
  expect(item.Item).toBeUndefined();
});

アイテムを削除後、読み込みを行ってみて該当のアイテムが取得できなくなっていることを確認。

ドキュメントインターフェースを使ってみる

先ほどはAWS SDK for JavaScriptで低レベルインターフェースの使ってみましたが、最後にドキュメントインターフェースを使ってみたいと思います。

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

ドキュメントインターフェースを使うと、SやNといったデータ型の指定を書く必要がなくなり、記述が簡単になります。

AWS SDK for JavaScriptではドキュメントインターフェースが利用でき、DocumentClientというクラスがその機能を持ちます。

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

また、Amazon DynamoDBのJavaScript用のGetting Startedでは、ドキュメントインターフェースを使ったサンプルコードになっています。

ステップ 1: JavaScript を使用して DynamoDB テーブルを作成する - Amazon DynamoDB

型宣言はこちらです。

https://github.com/aws/aws-sdk-js/blob/v2.1066.0/lib/dynamodb/document_client.d.ts

Amazon DynamoDBのJavaScriptのGetting Startedのうち、CRUDあたりをやってみましょう。

テーブルは、AWS CLIで作成しておくことにします。

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

こちらも複合主キーですね。

ドキュメントインターフェースを使うための準備は、こちら。AWS.DynamoDB.DocumentClientという配置になっているのですが、最初から
DocumentClientで一気にimportすることにしました。

test/documentclient.test.ts

import { DocumentClient } from 'aws-sdk/clients/dynamodb';

import { Movie, Info } from '../src/movie';

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

// ここに、テストを書く!

AWSのクレデンシャルやエンドポイントは、DynamoDBと同じようにコンストラクタで渡すことができます。

せっかくなので、データに関するクラス定義を行うことにしました。

src/movie.ts

export class Movie {
  year: number;
  title: string;
  info: Info;

  constructor(year: number, title: string, info: Info) {
    this.year = year;
    this.title = title;
    this.info = info;
  }
}

export class Info {
  plot: string;
  rating: number;
  actors?: string[] = undefined;

  constructor(plot: string, rating: number, actors?: string[]) {
    this.plot = plot;
    this.rating = rating;
    this.actors = actors;
  }
}

こちらは、簡単に書いていきます。

データ(アイテム)の書き込み。

test('write items', async () => {
  const movie = new Movie(
    2015,
    'The Big New Movie',
    new Info('Nothing happens at all.', 0)
  );

  const params: DocumentClient.PutItemInput = {
    TableName: 'Movies',
    Item: movie,
  };

  return await dynamodb.put(params).promise();
});

データの読み込み。

test('read item', async () => {
  const params: DocumentClient.GetItemInput = {
    TableName: 'Movies',
    Key: {
      year: 2015,
      title: 'The Big New Movie',
    },
    ConsistentRead: true,
  };

  const value = await dynamodb.get(params).promise();
  const movie = value.Item as Movie;
  expect(movie.year).toBe(2015);
  expect(movie.title).toBe('The Big New Movie');
  expect(movie.info.plot).toBe('Nothing happens at all.');
  expect(movie.info.rating).toBe(0);
});

更新。

test('update item', async () => {
  const params: DocumentClient.UpdateItemInput = {
    TableName: 'Movies',
    Key: {
      year: 2015,
      title: 'The Big New Movie',
    },
    ExpressionAttributeValues: {
      ':r': 5.5,
      ':p': 'Everything happens all at once.',
      ':a': ['Larry', 'Moe', 'Curly'],
    },
    UpdateExpression: 'set info.rating = :r, info.plot = :p, info.actors = :a',
    ReturnValues: 'UPDATED_NEW',
  };

  const value = await dynamodb.update(params).promise();
  if (value.Attributes) {
    const info = value.Attributes['info'] as Info;
    expect(info.plot).toBe('Everything happens all at once.');
    expect(info.rating).toBe(5.5);
    expect(info.actors).toEqual(['Larry', 'Moe', 'Curly']);
  } else {
    throw new Error('fail');
  }
});

バッチ処理でのデータ登録と、読み込みでの確認。

test('batch write items', async () => {
  const movies = [
    new Movie(
      2013,
      'Rush',
      new Info(
        'A re-creation of the merciless 1970s rivalry between Formula One rivals James Hunt and Niki Lauda.',
        8.3
      )
    ),
    new Movie(
      2012,
      'The Dark Knight Rises',
      new Info(
        'Eight years on, a new evil rises from where the Batman and Commissioner Gordon tried to bury it, causing the Batman to resurface and fight to protect Gotham City... the very city which brands him an enemy.',
        8.6
      )
    ),
  ];

  const params: DocumentClient.BatchWriteItemInput = {
    RequestItems: {
      Movies: movies.map((m) => {
        return { PutRequest: { Item: m } };
      }),
    },
  };

  const writed = await dynamodb.batchWrite(params).promise();

  const readParams1: DocumentClient.GetItemInput = {
    TableName: 'Movies',
    Key: {
      year: 2013,
      title: 'Rush',
    },
    ConsistentRead: true,
  };

  const value1 = await dynamodb.get(readParams1).promise();
  const movie1 = value1.Item as Movie;
  expect(movie1.year).toBe(2013);
  expect(movie1.title).toBe('Rush');
  expect(movie1.info.plot).toBe(
    'A re-creation of the merciless 1970s rivalry between Formula One rivals James Hunt and Niki Lauda.'
  );
  expect(movie1.info.rating).toBe(8.3);

  const readParams2: DocumentClient.GetItemInput = {
    TableName: 'Movies',
    Key: {
      year: 2012,
      title: 'The Dark Knight Rises',
    },
    ConsistentRead: true,
  };

  const value2 = await dynamodb.get(readParams2).promise();
  const movie2 = value2.Item as Movie;
  expect(movie2.year).toBe(2012);
  expect(movie2.title).toBe('The Dark Knight Rises');
  expect(movie2.info.plot).toBe(
    'Eight years on, a new evil rises from where the Batman and Commissioner Gordon tried to bury it, causing the Batman to resurface and fight to protect Gotham City... the very city which brands him an enemy.'
  );
  expect(movie2.info.rating).toBe(8.6);
});

データの削除と確認。

test('delete item', async () => {
  const params: DocumentClient.DeleteItemInput = {
    TableName: 'Movies',
    Key: {
      year: 2015,
      title: 'The Big New Movie',
    },
  };

  const value = await dynamodb.delete(params).promise();
  expect(value).not.toBeNull();

  const readParams: DocumentClient.GetItemInput = {
    TableName: 'Movies',
    Key: {
      year: 2015,
      title: 'The Big New Movie',
    },
    ConsistentRead: true,
  };

  const readValue = await dynamodb.get(readParams).promise();
  expect(readValue.Item).toBeUndefined();
});

長くなってきたので、今回はこのあたりで。

まとめ

ローカル版を使って、簡単にAmazon DynamoDBを試してみました。

多少雰囲気はわかってきましたが、クエリやスキャンはまた今度試してみようかなと思います。
また、使用するインターフェースはドキュメントインターフェースで良いかなと思います。

ちょっとずつ、慣れていってみましょう。