0
0

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 1 year has passed since last update.

【Node.js】LambdaからDynamoDBテーブルをScanするコードの書き方【前編】

Last updated at Posted at 2023-03-20

LambdaからDynamoDBに格納されたテーブル情報を取得する方法について、関数の作成からコードの記述方法までを説明します。
なお、Lambda関数はNode.jsで記述します。

後編ではテーブル情報の取得条件を設定する「FilterExpression」について深掘りしていますので、こちらもご確認ください。

Lambda関数を作成する

以下の手順でLambda関数を作成します。

Lambdaのホーム画面で、「関数の作成」を選択

image.png

各種設定を行う

image.png

関数の作成方法の選択

関数の作成方法には、3つの作成方法があります。今回は、「一から作成」を選択します。

基本的な設定

  • 関数名:関数の名前を設定します。自由に設定してください。
  • ランタイム:関数に記述する言語を選択していきます。今回はNode.jsを選択します。
  • アーキテクチャ:関数の実行に使用するコンピュータプロセッサのタイプを選択します。
    デフォルトのx86_64を選択します。
  • アクセス権限:特に設定しません。
  • 詳細設定:特に設定しません。

DynamoDBテーブルを作成する

今回は、料理検索を行うサイトを想定して料理テーブルを作成します。 条件は以下の通りです。
  • テーブル名:dish
  • 列:料理ID(dish_id)、料理名(dish_name)、値段(dish_price)

料理テーブル(dish)

dish_id dish_name dish_price
1 カレー 100
2 親子丼 300
3 ペペロンチーノ 200
4 麻婆豆腐 300
5 牛丼 150

関数のソースコードを記述する

本章では、lambdaに記述するソースコードについて説明します。

(補足)scanとqueryの違いについて

lambda関数からDynamoDBテーブルを検索する方法は2つ存在します。

  • scan:指定したテーブルのレコードを全件取得し、検索条件に合致したレコードのみを抽出
  • query:主キーを選択して1件のレコードを抽出

ネット上にはqueryに関する記事が多いのですが、複雑な条件で複数件のレコードを取得できるscanの方が便利なので今回はscanに絞ってご紹介します。

データ全件取得

まずは全件取得、つまり、dishテーブルの全レコードを取得します。

const aws = require('aws-sdk'); //①
const dynamo = new aws.DynamoDB.DocumentClient(); //②

exports.handler = async (event) => {
    let res = await dynamoscan(event);
    return res;
}; //③

function dynamoscan(event) {
    return new Promise((resolve, reject) => { //⑤

        let params = {
            "TableName": "dish",
        };

        dynamo.scan(params, function(err, data) {
            if (err) {
                reject(err);
            }
            else {
                resolve(data.Items);
            }

        });
    });
}

それぞれの行について役割を解説します。

const aws = require('aws-sdk');

AWSサービスをプログラムから操作するために必要です。

const dynamo = new aws.DynamoDB.DocumentClient();

DynamoDBにアクセスするためのオブジェクトを作成しています。

exports.handler = async (event) => {
    let res = await dynamoscan(event);
    return res;
};

lambda関数を実行した際に一番最初に実行するようにするメソッドです。
今回の場合は、dynamoscanという関数を実行し、戻り値を返します。

dynamoscanの処理は以下のようになります。

    return new Promise((resolve, reject) => {
        let params = {
            "TableName":"food",
            "ReturnConsumedCapacity": "TOTAL"
        };
        
        dynamo.scan(params,function(err, data) {
            if (err) {
                reject(err);
            } else {
                resolve(data.Items);
            }
        });
    });

paramsの中身を除き、お約束だと考えてかまいません。
それぞれ説明を説明すると以下のようになります。

  • return new Promise((resolve, reject) => {
    プロミスという記述方法を使用しています。詳しいことはさておき、resolveまたはrejectの引数が戻り値になります。
  • let params = {
    paramsという変数にscan時の検索条件を設定します。dynamo.scanを呼び出す際の引数として設定します。
  • dynamo.scan(params,function(err, data) {
    paramsに格納された値に基づいてDynamoDBテーブルのscanを行います。
    正常終了した場合はdataという変数のItemsを返します。
    異常終了した場合はエラーの内容を返します。
    ※どちらもjson形式

一致検索

続いては一致検索について説明します。dishテーブルの指定したdish_idを持つレコードを取得します。

const aws = require('aws-sdk'); //①
const dynamo = new aws.DynamoDB.DocumentClient(); //②

exports.handler = async (event) => {
    let res = await dynamoscan(event);
    return res;
}; //③

function dynamoscan(event) {
    return new Promise((resolve, reject) => { //⑤

        const params = {
            TableName: 'dish',
            KeyConditionExpression: '#id = :value', //①
            ExpressionAttributeNames: { //②
                "#id": "dish_id"
            },
            ExpressionAttributeValues: { //③
                ":value": Number(event.dishId)
            }
        };

        dynamo.scan(params, function(err, data) {
            if (err) {
                reject(err);
            }
            else {
                resolve(data.Items);
            }

        });
    });
}

paramsの設定内容を除けばデータ全件取得と変わりませんので、paramsの中身のみご説明します。

KeyConditionExpression: '#id = :value',

scan時の抽出条件です。"#id"と":value"が一致した場合のみ値を抽出します。
"#id"と":value"はバインドされており、以下のように定義します。

ExpressionAttributeNames: {"#id": "dish_id"}

"ExpressionAttributeNames"は'#'から始まるバインド値を定義します。
今回の場合、検索対象の列名をバインド値に格納しています。

ExpressionAttributeValues: {":value": Number(event.dishId)}

"ExpressionAttributeValues"は':'から始まるバインド値を定義します。
今回の場合、検索する値をバインド値に格納しています。

範囲検索

料理テーブルから、指定した金額の範囲内の料理を抽出します。

const aws = require('aws-sdk'); //①
const dynamo = new aws.DynamoDB.DocumentClient(); //②

exports.handler = async (event) => {
    let res = await dynamoscan(event);
    return res;
}; //③

function dynamoscan(event) {
    return new Promise((resolve, reject) => { //⑤

        const params = {
            TableName: "dish", //テーブル名を指定
            FilterExpression: "#dish_price between :minPrice and :maxPrice", //①
            ExpressionAttributeNames: {
                "#dish_price": "dish_price",
            },

            ExpressionAttributeValues: {
                ":minPrice": Number(event.minPrice),
                ":maxPrice": Number(event.maxPrice)
            }
        };

        dynamo.scan(params, function(err, data) {
            if (err) {
                reject(err);
            }
            else {
                resolve(data.Items);
            }

        });
    });
}

今回もparams以外は特に変わりませんので、paramsの値のみを解説します。

 FilterExpression: "#dish_price between :minPrice and :maxPrice",
 ExpressionAttributeNames: {
     "#dish_price": "dish_price",
 },
 ExpressionAttributeValues: {
     ":minPrice": Number(event.minPrice),
     ":maxPrice": Number(event.maxPrice)
 }

between_functionを使用することにより、"#dish_price"に格納した列の値が、
":minPrice"から":maxPrice"の範囲にあるレコードを検索します。
今回は、dishテーブルの"dish_price"列の値が、"event.minPrice"以上かつ、"event.maxPrice"以下のレコードを検索しています。
"ExpressionAttributeValues"では、2つのバインドを解決しています。

部分一致検索

「~を含む場合」で検索します。
今回は例として、料理名に「丼」を含むレコードをdishテーブルから検索します。

var aws = require('aws-sdk');
var dynamo = new aws.DynamoDB();

exports.handler = async (event) => {
    let res = await dynamoscan(event);
    return res;
};

function dynamoscan(event) {
    return new Promise((resolve, reject) => {
        let params = {
            TableName: "dish",
            FilterExpression: "contains(#dish_name, :dishName)",
            ExpressionAttributeNames: {
                "#dish_name": "dish_name",
            },
            ExpressionAttributeValues: {
                ":dishName": { "S": event.dishName }
            },
        };

        dynamo.scan(params, function(err, data) {
            if (err) {
                reject(err, err);
            }
            else {
                resolve(data.Items);
            }
        });
    });
}

以降、コードを説明していきます。

var aws = require('aws-sdk');
var dynamo = new aws.DynamoDB();

exports.handler = async (event) => {
    let res = await dynamoscan(event);
    return res;
};

全件検索と同じくおまじないのなので脳死でコピーしてください...と言いたいところですが、
1ヶ所だけ異なります。
2行目の末尾が"DynamoDB.DocumentClient()"ではなく、ただの"DynamoDB()"です。
このように記載した場合、文字の前に属性をつけなければいけません。
例えば、以下のように記載することになります。

"N": event.minPrice
"N": event.maxPrice
"S": event.dishName

もし仮に"DynamoDB.DocumentClient()"と記載した場合、

event.minPrice
event.maxPrice
event.dishName

これだけで良いので、基本的には"DynamoDB.DocumentClient()"を使うと良いです。
今回は紹介のために"DynamoDB()"を使用しました。

let params = {
    TableName: "dish",
    FilterExpression: "contains(#dish_name, :dishName)",
    ExpressionAttributeNames: {
        "#dish_name": "dish_name",
    },
    ExpressionAttributeValues: {
        ":dishName": { "S": event.dishName }
    },
};

こちらはscanの条件を指定しています。
paramsをdynamo.scanの引数として渡します。
それぞれの値は以下の通り。

  • TableName:検索対象のテーブル名。dishテーブルをscanの対象とする
  • FilterExpression:#dish_nameという列に:dishNameという値が含まれているレコードを検索
    なお、#dish_nameと:dishNameの部分はバインド必須
  • ExpressionAttributeNames:('#'から始まる)バインド値の設定
  • ExpressionAttributeValues:(':'から始まる)バインド値の設定
    先ほど説明した通り、今回の設定では値の前に属性をつける必要がある("S": のこと)

その他の箇所は上記と違いありません。

後編へ続く

lambda関数からDynamoDBテーブルのレコードを取得する方法についての説明は以上です。

しかし、より自由に、複雑な検索条件を指定したい場合は、FilterExpressionについて理解を深める必要があります。

これからは少し発展的な内容になりますので、ご興味のある方は後編をご覧下さい。

参考文献

AWS Lambda の概要

Amazon DynamoDB とは
AWS Cloud9 とは?

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?