10
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DynamoDB DataMapperをTypeScriptで使ってみた

Last updated at Posted at 2018-11-02

概要

2017年11月 AWSより、DynamoDB DataMapperというライブラリがDeveloper Previewで公開されました。
このDynamoDB DataMapperは、Javaで言うDynamoDB Mapperに近い感じで、クラスとDynamoDBに保存する型をマッピングすることができます。
またJavascriptの新しい記法を押さえているいい感じのライブラリなのですが、あまり日本語で紹介している記事がないので、ここではTypeScriptでの使い方を簡単に紹介していきます。

※本記事で紹介しているソースコードはTypeScript 2.9.2を使用しています。

DynamoDB DataMapper

DynamoDB DataMpperのGitHubには「DataMapperはアプリケーションのドメインクラスとDynamoDBに保存される型の相互運用を容易にします」と記載されています。

現在はv0.7.3まで公開されています。※2018年10月29日現在

クラス宣言

まずはドメインクラスを宣言します。
@aws/dynamodb-data-mapper-annotationsパッケージを利用すると、Decoratorsを用いて、DynamoDBのTableとクラスのマッピングや、HashKeyやRangeKey、Attribute等とクラスプロパティをマッピングすることができます。
以下の例ではid, createdAt, completedの3つのプロパティを持つドメインクラスMyModelを宣言しています。

MyModel.ts
import {
  attribute,
  hashKey,
  rangeKey,
  table,
} from '@aws/dynamodb-data-mapper-annotations'

@table('MyModels')
export default class MyModel {
  @hashKey()
  id: string

  @rangeKey({ defaultProvider: () => new Date() })
  createdAt: Date

  @attribute()
  completed?: boolean
}

※dynamodb-data-mapper-annotationsを使う際は、tsconfig.jsonでexperimentalDecoratorsemitDecoratorMetadataオプションをtrueにする必要があります。

createdAtはDate型で宣言していますが、DynamoDBに格納する際はDataMapperがNumber型に変換して格納します。逆にGetなどで取得する際は、DataMapperがDate型に変換します。
また、defaultProvider: () => new Date()は、DynamoDBにオブジェクトを格納する際、プロパティがundefinedであれば指定したfunctionの結果を代入してくれます。

@aws/dynamodb-data-mapper-annotationsは上記以外にも、autoGeneratedHashKeyversionAttributeなどの機能を提供しています。詳細はGitHubをご参照下さい。

オペレーション

DynamoDBの書き込み、読み込みオペレーションの一部を紹介します。

putやGetなどのオペレーションにはDataMapperインスタンスを利用します。

import { DataMapper } from '@aws/dynamodb-data-mapper'
const DynamoDB = require('aws-sdk/clients/dynamodb')

const mapper = new DataMapper({
  client: new DynamoDB(),
  tableNamePrefix: 'dev_'
})

DataMapperインスタンスの生成時にtableNamePrefixオプションを指定すると、DataMapperは対象のクラス(この記事の例ではMyModel.ts)で宣言しているTable NameにPrefixを付けたものを利用します。

Get

const date = new Date(2018, 9, 1)
const result: MyModel = await mapper.get(Object.assign(new MyModel, { 
  id: 'A',
  createdAt: date,
}))

getの戻り値はPromiseなので、awaitで受け取れます。
第一引数はObject.assignを使っていますが、newしたオブジェクトにプロパティを設定したものを渡しても大丈夫です。
getの第二引数には、読み込み整合性などのオプションが指定できます。

Put

const result: MyModel = await mapper.put(Object.assign(new MyModel,  { 
  id: 'A',
  completed: true,   
}))

getと同様にPromiseが返ってきます。
putの第二引数には、条件式などのオプションが指定できます。

Query

Queryは少し特殊で、ES2018の機能であるAsync Iterationを利用します。
まずはサンプルコードを見てみましょう。

const items = []
const query = mapper.query(MyModel, { id: 'A' })

for await (const item of query) {
  items.push(item)
}

queryの返り値はAsync Iteratorのため、中身を取得する際はfor-await-of構文を利用することで一つずつアイテムを取得することができます。

第二引数にはKeyConditionの指定が可能です。上記の例ではid: Aであるアイテムを取得しています。
さらにソートキーをKeyConditionExpressionで指定できます。
下記の例ではソートキーであるcreatedAtを範囲指定で検索しています。

const items = []
const query = mapper.query(MyModel, {
        id: 'A',
        createdAt: between(1514732400, 1543590000) // 2018-01-01 00:00:00 ~ 2018-12-01 00:00:00を指定
      }
);
for await (const item of query) {
  items.push(item)
}

ここでは@aws/dynamodb-expressionsパッケージに含まれるbetweenという関数を利用しています。

DynamoDB.DocumentClientの場合だと、

const params = {
  TableName: 'MyModels'
  KeyConditionExpression: 'id = :id and createdAt between :startTime and :endTime',
  ExpressionAttributeValues: {
    ':id': 'A',
    ':startTime': 1514732400,
    ':endTime':  1543590000
  }
}

のようにKeyConditonExpressionで式を記述したりExpressionAttributeValuesで値を指定しなければならなかったのですが、between関数を利用するとかなり簡素になりますね。
@aws/dynamodb-expressionsには他にもequals, notEquals, lessThan, greaterThanなど色々な関数が用意されています。

Pagination

Queryにページネーションが発生する可能性がある場合は注意が必要です。
DynamoDBのQueryは1度に取得できるサイズは1MBまでという制限があるため、検索結果がそれを超える場合はレスポンスから取得できるlastEvaluatedKeyを利用して再度クエリをする必要があります。

const items = []
const paginator = mapper.query(MyModel, { id: 'A' }).pages();
for await (const page of paginator) {
  for (const item of page) {
    items.push(item)
  }
}

const newPaginator = mapper.query(MyModel, { id: 'A'},
  { startKey: paginator.lastEvaluatedKey })
.pages()

上記の例では1回目のqueryで取得したlastEvaluatedKeyを、2回目のqueryのstartKeyオプションに渡して、queryを再開しています。

まとめ

今回はDynamoDB DataMapperを利用したDynamoDBの基本的な操作方法の一部をご紹介しました。
これから使おうと思っている方の一助になれば幸いです。

DynamoDB DataMapperは未だDeveloper Previewなので、正式リリースが待ち遠しいです!

10
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?