0
0

More than 3 years have passed since last update.

DynamoDBで"The provided starting key does not match the range key predicate"とエラーが出た場合の対策(node.js)

Last updated at Posted at 2020-04-13

Lambda(node.js)で、DynamoDBから超膨大なレコードを取得するプログラムを開発していました。
1分単位でクエリを実行して、レコードを小分け取得するようにしたのですが、
その際にプロパティを初期化できていなかったため、標題のエラーが発生したようです。

っていうことが、以下の記事を見てわかりました

具体的な対策は delete params["ExclusiveStartKey"]; みたいな感じで
プロパティを消せば良いです。

すっごく長いですが、備忘録を兼ねてソースコードを掲載します


try {
  var ENV = require('dotenv').config();

  var AWS = require('aws-sdk');
  AWS.config.update({ region: process.env['AWS_DYNAMO_DB_REGION'] });
  var docClient = new AWS.DynamoDB.DocumentClient({ apiVersion : '2012-08-10' });
}
catch(ex){
  console.error(ex);
}

var globalSecondaryIndexes = [{
  key   : 'dummy',
  index : 'dummy-created_at-index',
}];

var primaryKeys = {
  partitionKey : 'log_id',
  sortKey      : 'created_at'
};

async function getBulk(runTime = null)
{
  var myRunTime = runTime ? runTime : new Date();

  var myQuery = "#partitionKey = :partitionVal AND #rangeKey BETWEEN :rangeStart and :rangeEnd";

  var params = {
    TableName: process.env['AWS_DYNAMO_DB_ACCESS_LOG_TABLE_NAME'],
    IndexName : globalSecondaryIndexes[0].index,
    ExpressionAttributeNames : {
      "#partitionKey" : globalSecondaryIndexes[0].key,
      "#rangeKey" : primaryKeys.sortKey
    },
    ExpressionAttributeValues : {
      ":partitionVal" : 1,
      ":rangeStart"   : 0,
      ":rangeEnd"     : 0
    },
    KeyConditionExpression : myQuery
  };

  var results = new Object();
  var dateRanges = [
    {
      "start" : 1586762880,
      "end"   : 1586762939,
    },
    {
      "start" : 1586762940,
      "end"   : 1586762999,
    }
  ];

  try {
    for(var i in dateRanges){
      var dateRange = dateRanges[i];

      params["ExpressionAttributeValues"][":rangeStart"] = dateRange["start"];
      params["ExpressionAttributeValues"][":rangeEnd"] = dateRange["end"];

      delete params["ExclusiveStartKey"]; // <-- ★★★★★★ これ!! ★★★★★★

      var result = await get(params); // Array.object
      results[dateRange["start"]] = result;
    }

    return results; // Object.Array.object
  }
  catch(ex){
    console.error("Whoops, looks like something went wrong.");
    console.error(ex);
    throw new Error(ex);
  }
};

async function get(params)
{
  try {
    // 初回のクエリ発行
    var response = await queryGet(params);
    var results = response.data; // Array.object

    // 二回目以降のクエリ発行
    while(response.LastEvaluatedKey){
      params.ExclusiveStartKey = response.LastEvaluatedKey;
      response = await queryGet(params);
      // オブジェクトをマージ
      Array.prototype.push.apply(results, response.data);
    }

    // オブジェクトを返す
    return results; // Array.object
  }
  catch(ex){
    myFunc.logError("Whoops, looks like something went wrong.");
    myFunc.logError(ex);
    throw new Error(ex);
  }
};

function queryGet(parameter)
{
  return new Promise((resolve, reject) => {
    docClient.query(parameter, (err, data) => {
      if(err){
        console.error(err);
        reject(err);
      }
      else {
        if(data.LastEvaluatedKey){ // 全件取得できていない場合、最後の取得位置を通知して再帰処理を行う
          resolve({ data: data.Items, count: data.Count, LastEvaluatedKey: data.LastEvaluatedKey });
        }
        else { // 初回または最後の取得処理時
          resolve({ data: data.Items, count: data.Count });
        }
      }
    });
  });
};
0
0
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
0
0