Dynamoを用いた検索での厄介なルール
ハッシュ値やソートキーについては以下の記事に説明があります
https://aws.typepad.com/sajp/2017/02/choosing-the-right-dynamodb-partition-key.html
普通にquery()使ってデータを取得するとき、パーティションキーを必ず指定しなくてはならない。
じゃあ、ハッシュ値のデータを他のキーを条件にして取り出したいとき、無理やんって思った。
もちろんすべてのデータを取得するscanを使ってしまえば解決できるが、全てのデータを取得してそこからフィルターをかけるのは、この先データの量が多くなったときに大変な負荷になるので普通に考えたら使わない。
いろいろググったら、グローバルセカンダリインデックスがハッシュ値の代わりになるってのを知っていろいろ試行錯誤してなんとか成功した。
よくある通常のテーブル定義
以下のようにテーブル名やパーティションキーを設定する。
以下のようなテーブル定義なら、普通にhogeIDを指定したら、普通にデータを取得できる。
{
"TableName": "hoge",
"AttributeDefinitions": [
{
"AttributeName": "hogeID",
"AttributeType": "S"
}
],
"KeySchema": [
{
"AttributeName": "hogeID",
"KeyType": "HASH"
}
],
"ProvisionedThroughput": {
"WriteCapacityUnits": 1,
"ReadCapacityUnits": 1
}
}
が、例えば上記に書かれていないが他の時間のデータを使った条件に該当するhogeID一覧を取り出したかったら、以下のようなテーブル定義だと不可能。検索しようとしたら、パーティションキーを指定してくれよぉ~ってエラーが出る。
パーティションキーを指定しなくてもいいようにするテーブル定義
グローバルセカンダリーインデックスを利用する。
アトリビュートするもの一覧を定義
mysql的には、使うカラム一覧を定義する感じ。
今回重要になるのが、仮に「gsiId」という名前でカラム(属性)を追加した。この属性に全てのレコードに1を入れてしまえば、SQLの部分でハッシュとして仮に作った「gsiId」を使って「gsiId=1 and 〜」みたいにしたら、必ずハッシュ値を指定しなきゃいけないルールはなんとか解決できる。
"AttributeDefinitions": [
{
"AttributeName": "あらかじめ自分が用意している名前",
"AttributeType": "S"
},
{
"AttributeName": "gsiId",
"AttributeType": "S"
},
]
グローバルセカンダリーインデックスの部分を追加
自分の場合はカラムとなる部分が複数あって、その分インデックスを追加する感じ。
今回は省略して2つ。endDateは日付のデータが文字列で入っていて、
この日付のデータを使って検索しようとしたとき、通常のテーブル定義では不可能。
なので、startDateとendDateにそれぞれインデックスをくっつけるイメージ
"GlobalSecondaryIndexes": [
{
"IndexName": "hoge-index",
"Projection": {
"ProjectionType": "ALL"
},
"ProvisionedThroughput": {
"WriteCapacityUnits": 5,
"ReadCapacityUnits": 10
},
"KeySchema": [
{
"KeyType": "HASH",
"AttributeName": "gsiId"
},
{
"KeyType": "RANGE",
"AttributeName": "startDate"
}
]
},
{
"IndexName": "hoge2-index",
"Projection": {
"ProjectionType": "ALL"
},
"ProvisionedThroughput": {
"WriteCapacityUnits": 5,
"ReadCapacityUnits": 10
},
"KeySchema": [
{
"KeyType": "HASH",
"AttributeName": "gsiId"
},
{
"KeyType": "RANGE",
"AttributeName": "endDate"
}
]
}
],
実際にデータを取得
const params = {
TableName: 'テーブル名',
IndexName: 'インデックス名',
ExpressionAttributeNames: {
'#sc': 'mysql的にカラム名',
'#gi': 'mysql的にカラム名',
},
ExpressionAttributeValues: {
':start_date': '2020-10-06T11:30:15Z',
':end_date': '2020-10-06T11:40:17Z',
':gsi_value': '1',
},
KeyConditionExpression:
'#gi = :gsi_value and #sc BETWEEN :start_date AND :end_date',
}
const hoge = await documentClient.query(params).promise()
まとめ
くっそ苦戦した。mysqlとかポスグレなら普通に検索できるのにdynamoだと普通に検索するだけで大変だった。
でも一度覚えたらいけそう