パラメータ書くのが大変なのでモジュール作ってみました
nodejs dyqun
dynamodbのqueryパラメータを楽に書くツールを作ってみた
調べた分色々追記しています
何がしたかったのか
DynamoDBものすごい使ってるんですが、生のSDK操作すると
{S: 'hogehoge'}だったり数値なのに{N:'10'}だったり書かなきゃいけなくてつらすぎるので色々調べました。
色々試したんですが、SDKで提供されてるDocumentClientに戻って来てこいつが一番使いやすかったのでnodeでDynamoいじるなら絶対使ったほうがイイよってお話です。
Dynamo使うならここに書いて有ることだけでかなりのことがイケるはず
動作はLambdaでいじってるのでLambda動かしたときのソースをそのままサンプルとしています。
前提
説明で使用するDyanamoDBのテーブル定義について書きます。
-
tesi_1
- プライマリパーティションキー:key (文字列)
- age-index(GSI) age(数値)
- サンプルデータ
{ Item: { age: 14, key: 'Ranko_Kanzaki' } }
実行サンプル
GET
'use strict';
var aws = require('aws-sdk');
var docClient = new aws.DynamoDB.DocumentClient({region: 'ap-northeast-1'});
exports.handler = function (event, context) {
var params = {
TableName : 'test_1',
Key: {
'key': 'Ranko_Kanzaki'
}
};
docClient.get(params, function(err, data) {
if (err){
console.log(err);
context.succeed();
} else {
console.log(data);
context.succeed();
}
});
};
{ Item: { age: 14, key: 'Ranko_Kanzaki' } }
GETはハッシュキーの値を指定して、そのキーを取得します。
2016/11 追加
Hashキー + レンジキーの場合
レンジキーがわかんないんだけど、ハッシュキーに含まれるもの全部出したいとき用
レンジキーに全部の場合をカバーし得るものを指定すればいい。
レンジキーにyyyymmdd形式で日付を入れている
var name = 'P'
dyparam = {
TableName : 'test_1',
KeyConditionExpression: "#key1= :key1_name AND #key2 < :key2_name",
ExpressionAttributeNames:{
"#key1": "name",
"#key2": "yyyymmdd"
},
ExpressionAttributeValues: {
":key1_name": name,
":key2_name": '99991231'
}
};
レンジキーにyyyyから始まる文字列が入ってる
var name = 'P'
dyparam = {
TableName : 'test_1',
KeyConditionExpression: "#key1 = : key1_name AND begins_with(#key2, :key2_name)",
ExpressionAttributeNames:{
"#key1": "name",
"#key2": "yyyymmdd"
},
ExpressionAttributeValues: {
":key1_name": name,
":key2_name": '19|20'
}
};
1900年代〜2000年台カバーしておけばばあ・・・ね。
QUERY
'use strict';
var aws = require('aws-sdk');
var docClient = new aws.DynamoDB.DocumentClient({region: 'ap-northeast-1'});
exports.handler = function (event, context) {
var params = {
TableName : 'test_1',
KeyConditionExpression: "#hash = :str",
ExpressionAttributeNames:{
"#hash": "key"
},
ExpressionAttributeValues: {
":str": "Takagaki_Kaede"
}
};
docClient.query(params, function(err, data) {
if (err) {
console.log(err, err.stack);
} else {
console.log(data);
}
});
};
{ Items: [ { comment: '今日はワインだなって', age: 25, key: 'Takagaki_Kaede' } ],
Count: 1,
ScannedCount: 1 }
- KeyConditionExpressionに検索値の式を書きます
- ExpressionAttributeNamesでハッシュキーの名前を指定します
- ExpressionAttributeValuesでハッシュキーの値を指定します
インデックスを用いた検索
'use strict';
var aws = require('aws-sdk');
var docClient = new aws.DynamoDB.DocumentClient({region: 'ap-northeast-1'});
exports.handler = function (event, context) {
var params = {
TableName: 'test_1',
IndexName: 'age-index',
KeyConditionExpression: '#rangeKey = :rkey',
ExpressionAttributeNames : {
"#rangeKey" : 'age'
},
ExpressionAttributeValues: {
':rkey': 14
}
};
docClient.query(params, function(err, data) {
if (err) {
console.log(err, err.stack);
}else {
console.log(data);
}
});
};
{ Items:
[ { age: 14, key: 'Sachiko_Koshimizu' },
{ comment: '自称 天使', age: 14, key: 'Ranko_Kanzaki' } ],
Count: 2,
ScannedCount: 2 }
インデックスを用いた検索で項目を指定して絞り込む
'use strict';
var aws = require('aws-sdk');
var docClient = new aws.DynamoDB.DocumentClient({region: 'ap-northeast-1'});
exports.handler = function (event, context) {
var params = {
TableName: 'test_1',
Key:{
"comment": '自称 天使'
},
IndexName: 'age-index',
KeyConditionExpression: '#rangeKey = :rkey',
ExpressionAttributeNames : {
"#rangeKey" : 'age'
},
ExpressionAttributeValues: {
':rkey': 14
}
};
docClient.query(params, function(err, data) {
if (err) {
console.log(err, err.stack);
}else {
console.log(data);
}
});
};
{ Items:
[ { age: 14, key: 'Sachiko_Koshimizu' },
{ comment: '自称 天使', age: 14, key: 'Ranko_Kanzaki' } ],
Count: 2,
ScannedCount: 2 }
幸子だけが釣れました。
PUT
'use strict';
var aws = require('aws-sdk');
var docClient = new aws.DynamoDB.DocumentClient({region: 'ap-northeast-1'});
exports.handler = function (event, context) {
var item = {
key: 'Mayu_Sakuma',
age: 16,
comment: 'ずっと…ずっと待ってたの…'
};
var params = {
TableName: 'test_1',
Item: item
};
docClient.put(params, function (err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
};
{}
ままゆに会えました
UPDATE
'use strict';
var aws = require('aws-sdk');
var docClient = new aws.DynamoDB.DocumentClient({region: 'ap-northeast-1'});
exports.handler = function (event, context) {
var params = {
TableName: 'test_1',
Key:{
"key": 'Nana_Abe'
},
UpdateExpression: "set age = age + :val",
ExpressionAttributeValues:{
":val":1
},
ReturnValues:"UPDATED_NEW"
};
docClient.update(params, function (err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
};
ナナは永遠のじゅうななさいです・・・
更新できなかったので。。。
更新用データは楓さんが飲んだビールの数にしよう。
GETします。
{ Item:
{ comment: '今日はワインだなって',
beer_count: 9,
wine_count: 3,
key: 'Kaede_Takagaki',
age: 25 } }
'use strict';
var aws = require('aws-sdk');
var docClient = new aws.DynamoDB.DocumentClient({region: 'ap-northeast-1'});
exports.handler = function (event, context) {
var params = {
TableName: 'test_1',
Key:{
"key": 'Kaede_Takagaki'
},
UpdateExpression: "set beer_count = beer_count + :val",
ExpressionAttributeValues:{
":val":1
},
ReturnValues:"UPDATED_NEW"
};
docClient.update(params, function (err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
};
{ Attributes: { beer_count: 10 } }
追加
インデックスがハッシュキーとレンジキーを持っている場合
var params = {
TableName: 'test_1',
IndexName: 'age-index',
KeyConditionExpression: '#hashKey = :hkey AND #rangeKey = :rKey',
ExpressionAttributeNames : {
"#hashKey" : 'name',
"#rangeKey" : 'age'
},
ExpressionAttributeValues: {
':hKey': 'Momoka-Sakurai',
':rkey': 14
}
};
こんな感じでAND検索します
インデックスの片方どうしてもわかんないんだけどそれでも検索したい場合
var params = {
TableName: 'test_1',
IndexName: 'age-index',
KeyConditionExpression: '#hashKey = :hkey AND #rangeKey > :rKey',
ExpressionAttributeNames : {
"#hashKey" : 'name',
"#rangeKey" : 'age'
},
ExpressionAttributeValues: {
':hKey': 'Momoka-Sakurai',
':rkey': 0
}
};
こんな具合に例えば年齢なら0歳以上とかにしておけばヒットするし
何かのステータスコードみたいなの持ってたら
':rkey': '1|2|3|4|5'
みたいに力技ってのも一応可能
既存の項目にフィールドを追加したい
2017/03/01追加
DynamoDBでTTLがサポートされましたが、そんなときに
function updateDB(hashKey, unixTime) {
return new Promise((resolve, reject) => {
let params = {
TableName: 'test',
Key: {
"hash": hashKey
},
AttributeUpdates: {
ttl: {
Action: 'ADD',
Value: unixTime
}
},
Expected: {
hash: {
Value: hashKey,
Exists: true
}
},
ReturnValues: "UPDATED_NEW"
};
docClient.update(params, (err, data) => {
if (err) {
console.log(err);
reject(err);
} else {
console.log(data);
resolve();
}
});
});
}
// 24時間後
function unixTime() {
let date = new Date();
return Math.floor(date.getTime() / 1000) + 86400;
}
updateDB('hogehoge', unixTime()).then(result => {
callback();
}).catch(err => {
console.log('[ERROR] update error: ', err);
callback(err);
});
こんな感じで AttributeUpdates を使って制御します。
Expected で対象が存在した場合のみ処理を実行し、対象が存在しなければエラーを返す様指定します。
レンジキーに指定していない項目を使用して対象を絞り込みたい
KeyConditionExpressionで式を定義し、Queryを行うと前述しましたが、
レンジキーに指定していないキーを使って対象を絞り込みたいときもある?かもしれません。そんな時用
var params = {
TableName: 'test',
KeyConditions: {
'<パーティションキー名>': {
ComparisonOperator: 'EQ',
AttributeValueList: ['<値>']
}
},
QueryFilter: {
'<レンジキーでもなんでもない項目名>': {
ComparisonOperator: 'IN',
AttributeValueList: [
'<値1>','<値2>'
]
},
}
};
ComparisonOperatorで使えるのは下記の通り
ComparisonOperator: EQ | NE | IN | LE | LT | GE | GT | BETWEEN | NOT_NULL | NULL | CONTAINS | NOT_CONTAINS | BEGINS_WITH
参考