LambdaからDynamoDBに格納されたテーブル情報を取得する方法について、関数の作成からコードの記述方法までを説明します。
なお、Lambda関数はNode.jsで記述します。
後編ではテーブル情報の取得条件を設定する「FilterExpression」について深掘りしていますので、こちらもご確認ください。
Lambda関数を作成する
以下の手順でLambda関数を作成します。
Lambdaのホーム画面で、「関数の作成」を選択
各種設定を行う
関数の作成方法の選択
関数の作成方法には、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について理解を深める必要があります。
これからは少し発展的な内容になりますので、ご興味のある方は後編をご覧下さい。