銀の弾丸

プログラミングに関して、いろいろ書き残していければと思っております。

AWS LambdaからDynamoDBをQueryする

f:id:takamints:20160223124534p:plain
photo credit: Violet electric lightning versus turquoise storm - 09072012 via photopin (license)

JavaScript(Node.js)で記述した AWS Lambda から AWS-SDK を使って DynamoDBのテーブルをQueryするサンプルコードをご紹介。

ところでAWSAPIのリファレンスって検索しにくいですよねえ。 Lambdaがいろんな言語に対応しているため、REST APIと各言語向けの説明が別れていてチュートリアルも検索結果にたくさん出てきます。

※ 本記事ではDynamoDBのQueryするときのパラメータを説明します。 関連する記事として、検索条件を入力してQueryパラメータを生成するWebアプリは以下になります。合わせて参照してみてください。

takamints.hatenablog.jp

さらに、 DynamoDBをSQL(的な文)で扱う方法はコチラに書いています。

takamints.hatenablog.jp

DynamoDBのAPIは「Welcome Amazon DynamoDB」で説明されていますが、こちらはREST APIの説明で、各言語から呼び出せるメソッドについて直接記述されているわけではありません。だから定型的な読み替えと試行錯誤と推測が必要です。

日本語に翻訳されている文書もありますが、おおかた機械翻訳のようでして、かなり不可解な表現があったりします。 はなから英語で読んだほうが理解しやすいところもあったり。

などと、愚痴ってばかりでは始まらないので、ここはひとつ怒りに任せて(笑)Node.jsのLambda関数からDynamoDBを操作するサンプルコードをご紹介。

※ ここではAmazon DynamoDBAWS Lambdaについて、ひと通り基本的な知識を持っているって前提で書いてます。ワタシもAWS初心者なんですけどエラそうで申し訳ない。
※ フロントエンドは Amazon API Gatewayを利用している前提です。このため、実行結果はcontext.doneでJSON文字列を出力しています。


AWSエキスパート養成読本[Amazon Web Servicesに最適化されたアーキテクチャを手に入れる! ] (Software Design plus)
吉田 真吾 今井 智明 大瀧 隆太 松井 基勝 冨永 善視 藤原 吉規 大栗 宗
技術評論社
売り上げランキング: 1,300

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つのことをやっています。

  1. プライマリーキーによる検索を検索値のプレースホルダを使用して定義する
  2. プライマリーキー以外の属性で、キーの検索結果をフィルタする
  3. 属性名が予約語とかち合った場合は属性名のプレースホルダーで解決する

以降で、これらパラメータについて、詳細に説明します。

パラメータ

パラメータは第一引数に、objectとして与えられます。 下表に、ここで使用しているパラメータの概要です。

No. キー 説明
1 TableName string テーブル名を指定する。
2 KeyConditionExpression string キー属性での検索定義
3 ExpressionAttributeValues object 検索値のプレースホルダを定義する。
4 ExpressionAttributeNames object 属性名のプレースホルダを定義する。
5 FilterExpression string 検索後のフィルター条件を記述します。
  • 1~3は必須のはずです。 APIの説明ではTableNameだけ必須と書いてありますが、QUERYでは少なくともKeyConditionExpressionExpressionAttributeValuesを指定しないといけません。
  • 「4. ExpressionAttributeNames」は、検索する属性名に予約語が使われている場合に、読み替えを行うため必要となるパラメータ。
  • 他のパラメータについては、Query - Amazon DynamoDBで御確認ください。

KeyConditionExpression - キー、インデックスによる検索を定義する

KeyConditionExpressionではテーブル作成時に指定したキーによる検索条件を記述します。キー以外の条件は使えません。キー以外で絞り込みたい場合は、フィルターを使用する必要があります(フィルターはキーに関する制約を除けばKeyConditionExpressionと同じように条件を記述します)。

DynamoDBのテーブルのキーはパーティションキーとレンジキーの二種類があり、このうちパーティションキーは必ず検索条件に指定しなければなりません。レンジキーはオプションです。

検索条件の値は直接記述できません。値は検索値のプレースホルダを使って記述しなければなりません。 検索値のプレースホルダは、:(コロン)で始まる名称です。値に対する仮引数みたいなものですね。 具体的な値をプレースホルダに紐づけるには、次項で説明する ExpressionAttributeValues を使用します。

以下の :placeholderName が検索値のプレースホルダ

{
    "KeyConditionExpression":
        "keyAttrName = :placeholderName",
}

KeyConditionExpression についてまとめると以下のようになります。

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は紛らわしいけど、上書きしてしまうか、特定の属性の変更が行えるかの違いらしい(詳細不明)

  • バッチ系はちょっと使い勝手が違うかもしれませんが、一度にやっちゃう感じでしょう(詳細不明)
  • その他、テーブルを操作するAPIDynamoDBのAPIの説明ページ に書いてあります。

AWS関連記事

Node.js / npm 関連記事



その他、一般事項

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の名前に書き換え。


RDB技術者のためのNoSQLガイド
渡部 徹太郎 河村 康爾 北沢 匠 佐伯 嘉康 佐藤 直生 原沢 滋 平山 毅 李昌 桓
秀和システム
売り上げランキング: 40,238