AWS LambdaからDynamoDBをQueryする
photo credit: Violet electric lightning versus turquoise storm - 09072012 via photopin (license)
JavaScript(Node.js)で記述した AWS Lambda から AWS-SDK を使って DynamoDBのテーブルをQueryするサンプルコードをご紹介。
ところでAWSのAPIのリファレンスって検索しにくいですよねえ。 Lambdaがいろんな言語に対応しているため、REST APIと各言語向けの説明が別れていてチュートリアルも検索結果にたくさん出てきます。
※ 本記事ではDynamoDBのQueryするときのパラメータを説明します。 関連する記事として、検索条件を入力してQueryパラメータを生成するWebアプリは以下になります。合わせて参照してみてください。
さらに、 DynamoDBをSQL(的な文)で扱う方法はコチラに書いています。
DynamoDBのAPIは「Welcome Amazon DynamoDB」で説明されていますが、こちらはREST APIの説明で、各言語から呼び出せるメソッドについて直接記述されているわけではありません。だから定型的な読み替えと試行錯誤と推測が必要です。
日本語に翻訳されている文書もありますが、おおかた機械翻訳のようでして、かなり不可解な表現があったりします。 はなから英語で読んだほうが理解しやすいところもあったり。
などと、愚痴ってばかりでは始まらないので、ここはひとつ怒りに任せて(笑)Node.jsのLambda関数からDynamoDBを操作するサンプルコードをご紹介。
※ ここではAmazon DynamoDBと AWS Lambdaについて、ひと通り基本的な知識を持っているって前提で書いてます。ワタシもAWS初心者なんですけどエラそうで申し訳ない。
※ フロントエンドは Amazon API Gatewayを利用している前提です。このため、実行結果はcontext.doneでJSON文字列を出力しています。
KZ-WORKS AWS関連記事
DynamoDB API Query - キー属性で項目を検索
DynamoDBのテーブルから検索条件に合致した項目を問い合わせるためには、Query を使用します。条件無しで全項目を得るには、Queryではなく Scan を使用します。
Queryの検索条件には、少なくともプライマリーキーの条件が指定されなくてはならず、これに加えて、プライマリーソートキーやセカンダリーインデックスの条件も AND / OR で連結して記述できます。
さらに、プライマリーキー以外の項目によるフィルター条件も指定できますが、これは、あくまでも上記のプライマリーキーによる検索結果を得た後に対して絞りこむものですから、クエリ自体のパフォーマンスには、ほぼ無関係でしょう。(レスポンスが少なくなるので、通信速度には影響すると思われます)
サンプルコード(Node.js)
以下のように少しややこしい。初めて見たら軽くめまいを覚えそうです。
var AWS = require('aws-sdk'); var dynamo = new AWS.DynamoDB({ region: 'ap-northeast-1' }); var tableName = "myTable"; exports.handler = function(event, context) { //パラメータ定義 var params = { "TableName": tableName, //キー、インデックスによる検索の定義 "KeyConditionExpression": "id = :Id AND #Ts BETWEEN :T0 AND :T1", //プライマリーキー以外の属性でのフィルタ "FilterExpression": "#Fn > :Fn", //属性名のプレースホルダの定義 "ExpressionAttributeNames": { "#Ts": "timestamp", "#Fn": "value.floor" }, //検索値のプレースホルダの定義 "ExpressionAttributeValues": { ":Id" : { "S" : event.id }, ":T0" : { "S" : event.T0 }, ":T1" : { "S" : event.T1 } ":Fn" : { "S" : event.Fn } } }; //クエリ実行 dynamo.query(params, function(err, data) { if (err) { context.fail( new Error("Fail. err:" + err)); } else { var response = JSON.stringify(data); context.done(null, response); } }); };
ポイントは、params
。
ここではテーブルの指定以外に以下の3つのことをやっています。
以降で、これらパラメータについて、詳細に説明します。
パラメータ
パラメータは第一引数に、objectとして与えられます。 下表に、ここで使用しているパラメータの概要です。
No. | キー | 型 | 説明 |
---|---|---|---|
1 | TableName | string | テーブル名を指定する。 |
2 | KeyConditionExpression | string | キー属性での検索定義 |
3 | ExpressionAttributeValues | object | 検索値のプレースホルダを定義する。 |
4 | ExpressionAttributeNames | object | 属性名のプレースホルダを定義する。 |
5 | FilterExpression | string | 検索後のフィルター条件を記述します。 |
- 1~3は必須のはずです。
APIの説明では
TableName
だけ必須と書いてありますが、QUERYでは少なくともKeyConditionExpression
とExpressionAttributeValues
を指定しないといけません。 - 「4. ExpressionAttributeNames」は、検索する属性名に予約語が使われている場合に、読み替えを行うため必要となるパラメータ。
- 他のパラメータについては、Query - Amazon DynamoDBで御確認ください。
KeyConditionExpression - キー、インデックスによる検索を定義する
KeyConditionExpressionではテーブル作成時に指定したキーによる検索条件を記述します。キー以外の条件は使えません。キー以外で絞り込みたい場合は、フィルターを使用する必要があります(フィルターはキーに関する制約を除けばKeyConditionExpressionと同じように条件を記述します)。
DynamoDBのテーブルのキーはパーティションキーとレンジキーの二種類があり、このうちパーティションキーは必ず検索条件に指定しなければなりません。レンジキーはオプションです。
検索条件の値は直接記述できません。値は検索値のプレースホルダを使って記述しなければなりません。
検索値のプレースホルダは、:
(コロン)で始まる名称です。値に対する仮引数みたいなものですね。
具体的な値をプレースホルダに紐づけるには、次項で説明する ExpressionAttributeValues
を使用します。
以下の :placeholderName
が検索値のプレースホルダ。
{ "KeyConditionExpression": "keyAttrName = :placeholderName", }
KeyConditionExpression についてまとめると以下のようになります。
- キーによる検索条件を記述しなければならない。
- パーティションキーは必ず指定しなければならない。
- 値は検索値のプレースホルダで指定する。
- キーの属性名がDynamoDBの予約語である場合は、属性名のプレースホルダを使用して記述しなくてはならない(予約語でなくても使用可)。
ExpressionAttributeValues - 検索値のプレースホルダを定義する
ExpressionAttributeValuesには、KeyConditionExpressionやFilterExpressionに記述されているプレースホルダの値を型とともに指定します。
{ "KeyConditionExpression": "keyAttrName = :placeholderName", "FilterExpression": "attrName > :placeholderName2", "ExpressionAttributeValues": { ":placeholderName": { "S" : "11" }, ":placeholderName2": { "N" : "12" } } }
※ チュートリアルでは値だけが指定されていますが「structureじゃないとダメですよ」という例外が投入されます。
ExpressionAttributeNames - 属性名のプレースホルダを定義する
DynamoDBのテーブルの属性名に予約語が使われている場合、検索条件を記述できません(「属性名がキーワードだ」との例外投入) ので、ExpressionAttributeNamesで置き換えます。
以下の例では、予約語timestampと同じ属性名を、読み替えています。(#Tm
の部分)
"KeyConditionExpression": "#Tm = :T0", "ExpressionAttributeNames": { "#Tm": "timestamp" }, "ExpressionAttributeValues": { ":T0" : { "S" : "" + event.t0 } }
他のAPI
テーブルの項目を操作するAPIは以下の様なものがあります。
- 項目の問い合わせ - Query
- 全項目取得 - Scan
- 特定項目を取得 - GetItem
- 項目上書き - PutItem
- 項目変更 - UpdateItem
- 項目削除 - DeleteItem
- バッチ項目取得 - BatchGetItem
バッチ項目更新 - BatchWriteItem
PutItemとUpdateItemは紛らわしいけど、上書きしてしまうか、特定の属性の変更が行えるかの違いらしい(詳細不明)
- バッチ系はちょっと使い勝手が違うかもしれませんが、一度にやっちゃう感じでしょう(詳細不明)
- その他、テーブルを操作するAPIは DynamoDBのAPIの説明ページ に書いてあります。
AWS関連記事
- DynamoDB Query APIのパラメータを生成しましょ(プレースホルダをぶっ飛ばせ) - 銀の弾丸
- DynamoDBをSQLで操作するNodeモジュール - 銀の弾丸
- DynamoDB:条件式のプレースホルダを自動生成してみましょう - 銀の弾丸
- AWS Lambdaの関数アップロードをお手軽に - 銀の弾丸
Node.js / npm 関連記事
- package-lock.jsonの潜在的セキュリティ脆弱性を解消しました - 銀の弾丸
- WHATWG Fullscreen API を仕様通りに使えるモジュール「fullscrn」 - 銀の弾丸
- SVGの重なり順序をJavaScriptで制御する「svg-z-order」 - 銀の弾丸
- npm「list-it」― コンソールへ列を揃えてデータを表示 - 銀の弾丸
- npm 「hash-arg」 ― コマンドラインパラメータに名前でアクセス - 銀の弾丸
その他、一般事項
Node.jsから呼び出すAPIのメソッド名
DynamoDBのAPIの説明ページ に掲載されている API の名称をキャメルケースに変換すれば、Node.jsのメソッド名になると思います(多分)。
DynamoDB APIの基本形
Node.jsでのDynamoDB APIは、以下のような形式で呼び出します。
dynamo.apiName(params, function(err, data) { if(err) { //エラー; } else { //dataにAPIの実行結果が入っています。 } });
(↑)apiNameをAPIの名前に書き換え。