AWS DynamoDBのqueryで「The provided starting key does not match the range key predicate」エラー
KeyConditionExpression でソートキーを範囲指定 (>= など) してる時に、ExclusiveStartKey で指定した開始位置が、範囲指定のスタート位置より外側である場合に発生する。
↓こういうの。そういえば以前は、DynamoDBのソートキーはレンジキーって呼ばれてたんだっけ。
ValidationException: The provided starting key does not match the range key predicate具体的には、以下のようなパラメータでqueryしたら出てくる。
// ※MyTable はパーティションキーが user_id, ソートキーが created_at
const params = {
TableName: 'MyTable',
KeyConditionExpression: 'user_id = :u and created_at >= :c',
ExpressionAttributeValues: {
':u': 'kiriukun',
':c': '1999-07-31T00:00:10.000Z'
},
ExclusiveStartKey: {
user_id: 'kiriukun',
created_at: '1999-07-31T00:00:09.000Z'
}
};パーティションキーは同値だからここでは触れないものとして、このパラメータの意味は以下の通りになる。
KeyConditionExpressionでは、created_atが1999-07-31T00:00:10.000Z以上のデータを探したい。ExclusiveStartKeyでは、created_atが1999-07-31T00:00:09.000Zの次のデータから開始したい。
名前の通り、ExclusiveStartKey は指定したキーの『次』の値から読み取るためのオプションだ。範囲外なら ExclusiveStartKey は無視してくれればと思うのだが、DynamoDB的にはダメなようだ。
なので KeyConditionExpression で範囲指定しながら ExclusiveStartKey を使うときは、ExclusiveStartKey で指定するソートキーが KeyConditionExpression の範囲内であることを、事前にチェックしておく必要がある。
↓のように、:c と ExclusiveStartKey.created_at を同値に変更した場合はエラーにならない。
const params = {
TableName: 'MyTable',
KeyConditionExpression: 'user_id = :u and created_at >= :c',
ExpressionAttributeValues: {
':u': 'kiriukun',
':c': '1999-07-31T00:00:10.000Z'
},
ExclusiveStartKey: {
user_id: 'kiriukun',
created_at: '1999-07-31T00:00:10.000Z' // ← :c に揃えた
}
};1999-07-31T00:00:10.000Z 以上のデータを探すために 1999-07-31T00:00:10.000Z の『次』から開始するのは、範囲内に収まってるからOK。むしろ、ジャスト 1999-07-31T00:00:10.000Z のデータは引っかからないし。
なお、この範囲の判定は ScanIndexForward の影響を受けるので注意。上記のパラメータに ScanIndexForward: false を追加する↓と、またエラーが出るようになる。
const params = {
TableName: 'MyTable',
KeyConditionExpression: 'user_id = :u and created_at >= :c',
ExpressionAttributeValues: {
':u': 'kiriukun',
':c': '1999-07-31T00:00:10.000Z'
},
ExclusiveStartKey: {
user_id: 'kiriukun',
created_at: '1999-07-31T00:00:10.000Z'
},
ScanIndexForward: false // ← 降順で取得するようにした
};ExclusiveStartKey は指定したキーの次の項目から読み始めるものなので、降順の時は逆転するからだ。降順のとき、1999-07-31T00:00:10.000Z の『次』のデータはそれより昔ということになり、KeyConditionExpression の範囲外になる。
……が、個人的に首を捻ってしまったのは↓の場合。(昇順)
const params = {
TableName: 'MyTable',
KeyConditionExpression: 'user_id = :u and created_at > :c', // ←ここを > にした
ExpressionAttributeValues: {
':u': 'kiriukun',
':c': '1999-07-31T00:00:10.000Z'
},
ExclusiveStartKey: {
user_id: 'kiriukun',
created_at: '1999-07-31T00:00:10.000Z'
}
};これはエラーになる。なるのだが……
KeyConditionExpressionでは、created_atが1999-07-31T00:00:10.000Zより上のデータを探したい。ExclusiveStartKeyでは、created_atが1999-07-31T00:00:10.000Zの次のデータから開始したい。
……確かに自明すぎて ExclusiveStartKey を設定する意味が皆無だけど、ギリギリ範囲外ではないような? 問題にされてるのは、実際の開始位置より ExclusiveStartKey 自体の位置ということだろうか。
DynamoDBの中身とかを知ればしっくりくるのかもしれないけど、とりあえず今は「DynamoDBは明らかにおかしいこと (範囲外) を言われるのが嫌いだし、分かり切ってることを回りくどく (> & ジャストExclusiveStartKey) 言われるのも嫌い」と思うことにした。
以上。
キリウ君が読まないノート