LoginSignup
212
170

More than 5 years have passed since last update.

Node.jsでDynamoDBを操作するためのチートシート[DynamoDB.DocumentClient][AWS]

Last updated at Posted at 2016-09-22

パラメータ書くのが大変なのでモジュール作ってみました

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

index.js

'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形式で日付を入れている

parameter
    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から始まる文字列が入ってる

parameter
    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

index.js
'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でハッシュキーの値を指定します

インデックスを用いた検索

index.js
'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 }

インデックスを用いた検索で項目を指定して絞り込む

index.js
'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

index.js
'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

index.js
'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 } }

index.js
'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 } }

追加

インデックスがハッシュキーとレンジキーを持っている場合

sample
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検索します

インデックスの片方どうしてもわかんないんだけどそれでも検索したい場合

sample
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がサポートされましたが、そんなときに

sample.js
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

参考

212
170
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
212
170