概要
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を宣言しています。
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でexperimentalDecorators
とemitDecoratorMetadata
オプションをtrueにする必要があります。
createdAt
はDate型で宣言していますが、DynamoDBに格納する際はDataMapperがNumber型に変換して格納します。逆にGetなどで取得する際は、DataMapperがDate型に変換します。
また、defaultProvider: () => new Date()
は、DynamoDBにオブジェクトを格納する際、プロパティがundefined
であれば指定したfunctionの結果を代入してくれます。
@aws/dynamodb-data-mapper-annotationsは上記以外にも、autoGeneratedHashKey
やversionAttribute
などの機能を提供しています。詳細は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なので、正式リリースが待ち遠しいです!