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

## パラメータ書くのが大変なのでモジュール作ってみました
[nodejs dyqun](https://github.com/yukiGitHubAc/dyqun/blob/master/README.JP.md)
[dynamodbのqueryパラメータを楽に書くツールを作ってみた](http://qiita.com/Yuki_BB3/items/081f1a60b28b92f55b6b)
## 調べた分色々追記しています

# 何がしたかったのか
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

```javascript: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形式で日付を入れている
```javascript: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から始まる文字列が入ってる
```javascript: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

```javascript: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でハッシュキーの値を指定します

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

```javascript: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 }
```

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

```javascript: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

```javascript: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

```javascript: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 } }

```


```javascript: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 } }
```

---

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

```javascript: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検索します

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

```javascript: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歳以上とかにしておけばヒットするし
何かのステータスコードみたいなの持ってたら

```javascript:
':rkey': '1|2|3|4|5'
```
みたいに力技ってのも一応可能

## 既存の項目にフィールドを追加したい
2017/03/01追加
DynamoDBでTTLがサポートされましたが、そんなときに

```javascript: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;
}
```

```javascript:実行
    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: [
                '<値１>','<値２>'
            ]
        },
    }
};
```
ComparisonOperatorで使えるのは下記の通り

```
 ComparisonOperator: EQ | NE | IN | LE | LT | GE | GT | BETWEEN | NOT_NULL | NULL | CONTAINS | NOT_CONTAINS | BEGINS_WITH
```


# 参考
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#query-property