DocumentClient を使用して DynamoDB からデータを取得〜便利なオプション〜

AWS.DynamoDB.DocumentClient を使用して DynamoDB からデータを取得する方法を紹介します。

今回は頻繁に使うわけでは無いが、たまに必要になるので知っておいた方が良いオプションについて紹介していきます。

 

動作環境は AWS Lambda Node.js 10.X で確認しています。

 

特定のデータ以降のデータを取得

まずは次のテーブルを全件取得してみます。

テーブル名:Music

var AWS = require('aws-sdk');
var documentClient = new AWS.DynamoDB.DocumentClient({
    apiVersion: '2012-08-10',
    region: "ap-northeast-1"
});

exports.handler = async(event) => {

  var params = {
    TableName: 'Music'
  };

  var result;
  try {
    result = await documentClient.scan(params).promise();
  }
  catch (e) {
    result = e;
  }

  return {
    statusCode: 200,
    body: result,
  };
};

 

結果

{
  "Items": [
    {
      "Artist": "マドンナ",
      "ReleaseDate": "1984/11/12",
      "SongTitle": "ライクアヴァージン"
    },
    {
      "Artist": "アヴリルラヴィーン",
      "ReleaseDate": "2007/02/07",
      "SongTitle": "ガールフレンド"
    },
    {
      "Artist": "マイケルジャクソン",
      "ReleaseDate": "1982/12/01",
      "SongTitle": "スリラー"
    },
    {
      "Artist": "マイケルジャクソン",
      "ReleaseDate": "1983/02/03",
      "SongTitle": "ビートイット"
    }
  ],
  "Count": 4,
  "ScannedCount": 4
}

 

この中から"アヴリルラヴィーン"の次、"マイケルジャクソン"の"スリラー"から取得してみます。

 

コード

var AWS = require('aws-sdk');
var documentClient = new AWS.DynamoDB.DocumentClient({
  apiVersion: '2012-08-10',
  region: "ap-northeast-1"
});

exports.handler = async(event) => {
  var params = {
    TableName: 'Music',
    ExclusiveStartKey: {
      'Artist': 'アヴリルラヴィーン',
      'SongTitle': 'ガールフレンド'
    }
  };
  var result;
  try {
    result = await documentClient.scan(params).promise();
  }
  catch (e) {
    result = e;
  }

  return {
    statusCode: 200,
    body: result,
  };
};

結果

{
  "Items": [
    {
      "Artist": "マイケルジャクソン",
      "ReleaseDate": "1982/12/01",
      "SongTitle": "スリラー"
    },
    {
      "Artist": "マイケルジャクソン",
      "ReleaseDate": "1983/02/03",
      "SongTitle": "ビートイット"
    }
  ],
  "Count": 2,
  "ScannedCount": 2
}

 

"ExclusiveStartKey"にキーを指定すると、そのキーの次の項目から取得できます。

 

DynamoDBから取得するデータは1Mまでしか取得できないという制限があります。

それ以上ある場合ははみ出た部分が切り捨てられ、切り捨てられなかった最後のデータが"LastEvaluatedKey"として返されます。

"LastEvaluatedKey"を"ExclusiveStartKey"に指定して、再度データを取得すると続きから取得できるので、データ量が増えてくるとよく使うオプションです。

 

列に別名をつける

次のテーブルから"Between"が1〜2の曲を取得します。

テーブル名:Music

テーブル名:Music

コード

var AWS = require('aws-sdk');
var documentClient = new AWS.DynamoDB.DocumentClient({
    apiVersion: '2012-08-10',
    region: "ap-northeast-1"
});

exports.handler = async(event) => {
  var params = {
    TableName: 'Music',
    FilterExpression: '#Between Between :Between1 and :Between2',
    ExpressionAttributeValues: {
      ':Between1': 1,
      ':Between2': 2
    },
    ExpressionAttributeNames: {
      '#Between': 'Between'
    },
  };

  var result;
  try {
    result = await documentClient.scan(params).promise();
  }
  catch (e) {
    result = e;
  }

  return {
    statusCode: 200,
    body: result,
  };
};

 

結果

{
  "Items": [
    {
      "Artist": "アヴリルラヴィーン",
      "ReleaseDate": "2007/02/07",
      "Between": 1,
      "SongTitle": "ガールフレンド"
    },
    {
      "Artist": "マイケルジャクソン",
      "ReleaseDate": "1982/12/01",
      "Between": 2,
      "SongTitle": "スリラー"
    }
  ],
  "Count": 2,
  "ScannedCount": 4
}

 

"ExpressionAttributeNames"で"Between"という項目を"#Between"という名前に変えています。

 

もし名前を変えなかった場合"FilterExpression"は"Between Between :Between1 and :Between2"となり、

「Invalid FilterExpression: Syntax error; token: \"Between\", near: \"Between Between\"」という

Betweenの使い方がおかしい旨のエラーなってしまいます。

 

取得順を逆にする

まずは普通に、次のテーブルから"マイケルジャクソン"の曲を取得します。

テーブル名:Music

プライマリパーティションキー:Artist プライマリソートキー:SongTitle

DocumentClient

 

コード

var AWS = require('aws-sdk');
var documentClient = new AWS.DynamoDB.DocumentClient({
    apiVersion: '2012-08-10',
    region: "ap-northeast-1"
});

exports.handler = async(event) => {
  var params = {
    TableName: 'Music',
    KeyConditionExpression: 'Artist = :Artist',
    ExpressionAttributeValues: {
      ':Artist': 'マイケルジャクソン'
    }
  };

  var result;
  try {
    result = await documentClient.query(params).promise();
  }
  catch (e) {
    result = e;
  }

  return {
    statusCode: 200,
    body: result,
  };
};

 

結果

{
  "Items": [
    {
      "Artist": "マイケルジャクソン",
      "ReleaseDate": "1982/12/01",
      "SongTitle": "スリラー"
    },
    {
      "Artist": "マイケルジャクソン",
      "ReleaseDate": "1983/02/03",
      "SongTitle": "ビートイット"
    }
  ],
  "Count": 2,
  "ScannedCount": 2
}

 

"スリラー">"ビートイット"の順で取得できました。

それではコードにオプションを付けてみます。

コード

var AWS = require('aws-sdk');
var documentClient = new AWS.DynamoDB.DocumentClient({
    apiVersion: '2012-08-10',
    region: "ap-northeast-1"
});

exports.handler = async(event) => {
  var params = {
    TableName: 'Music',
    KeyConditionExpression: 'Artist = :Artist',
    ExpressionAttributeValues: {
      ':Artist': 'マイケルジャクソン'
    },
    ScanIndexForward: false
  };

  var result;
  try {
    result = await documentClient.query(params).promise();
  }
  catch (e) {
    result = e;
  }

  return {
    statusCode: 200,
    body: result,
  };
};

 

結果

{
  "Items": [
    {
      "Artist": "マイケルジャクソン",
      "ReleaseDate": "1983/02/03",
      "SongTitle": "ビートイット"
    },
    {
      "Artist": "マイケルジャクソン",
      "ReleaseDate": "1982/12/01",
      "SongTitle": "スリラー"
    }
  ],
  "Count": 2,
  "ScannedCount": 2
}

 

最初とは逆の"ビートイット">"スリラー"の順に取得できました。

 

"ScanIndexForward: false"を指定することで、プライマリソートキーの値を逆順にすることができます。

ただし、プライマリパーティションキーしか無いテーブルや、"scan"メソッドで使用した場合、

エラーにはなりませんが、逆順になることも無いので注意してください。

あくまでもプライマリソートキーが逆順になるだけです。

 

並列処理で取得する

次のテーブルを2つに分けてデータを取得します。

テーブル名:Music

プライマリパーティションキー:Artist プライマリソートキー:SongTitle

DocumentClient 並列処理で取得

 

コード

var AWS = require('aws-sdk');
var documentClient = new AWS.DynamoDB.DocumentClient({
    apiVersion: '2012-08-10',
    region: "ap-northeast-1"
});

exports.handler = async(event) => {
  var results = [];
  await Promise.all([
    (async () => {
      // 1/2を取得
      var params = {
        TableName: 'Music',
        Segment: 0,
        TotalSegments: 2
      };

      try {
        var result = await documentClient.scan(params).promise();
        results.push(result);
      }
      catch (e) {
        results.push(e);
      }
      return;
    })(),
    (async () => {
      // 2/2を取得
      var params = {
        TableName: 'Music',
        Segment: 1,
        TotalSegments: 2
      };

      try {
        var result = await documentClient.scan(params).promise();
        results.push(result);
      }
      catch (e) {
        results.push(e);
      }
      return;
    })(),
  ]);

  return {
    statusCode: 200,
    body: results,
  };
};

 

結果

[
  {
    "Items": [
      {
        "Artist": "マドンナ",
        "ReleaseDate": "1984/11/12",
        "SongTitle": "ライクアヴァージン"
      },
      {
        "Artist": "アヴリルラヴィーン",
        "ReleaseDate": "2007/02/07",
        "SongTitle": "ガールフレンド"
      }
    ],
    "Count": 2,
    "ScannedCount": 2
  },
  {
    "Items": [
      {
        "Artist": "マイケルジャクソン",
        "ReleaseDate": "1982/12/01",
        "SongTitle": "スリラー"
      },
      {
        "Artist": "マイケルジャクソン",
        "ReleaseDate": "1983/02/03",
        "SongTitle": "ビートイット"
      }
    ],
    "Count": 2,
    "ScannedCount": 2
  }
]

 

"TotalSegments"を指定すると取得処理を分割して実行できます。

今回はテーブルを2個に分けて、"Segment"が0(1/2番目の部分)のデータと1(2/2番目の部分)のデータを取得しています。

4行程度ならわざわざ分ける意味は無いですが、体感ですが1万行を超えてくると差がでてくるので、バッチ処理等で活躍すると思います。

 

 

 

アプリケーション開発バナー

 

AWS無料相談