Description
Describe the bug
I have a table with a GSI that has a hash and a range.
The GSI is specified in the model, as described.
If I do a 'where' on the hash key used in the GSI, the index is not used. The raw output shows 'Scan' is picked, not 'Query'.
Schema
In the model I defined the table name etc and added the index:
* Indexes.
* [
* '<simple_index_name>' => [
* 'hash' => '<index_key>'
* ],
* '<composite_index_name>' => [
* 'hash' => '<index_hash_key>',
* 'range' => '<index_range_key>'
* ],
* ]
*
* @var array
*/
protected $dynamoDbIndexKeys = [
'account-created_at-index' => [
'hash' => 'account',
'range' => 'created_at',
],
];
... the query works and it does indeed return the correct item, but when I look into how it got there with the raw query ...
Item::where('account', 'abcde')->toDynamoDbQuery()
... it shows a Scan was done. But 'account' is the hash of an index, so Query should have been used.
To try and debug myself, looking into how it works, I see the decision to use 'Query' appears to be taken in here:
/**
* Return the raw DynamoDb query
*
* @param array $columns
* @param int $limit
* @return RawDynamoDbQuery
*/
public function toDynamoDbQuery(
$columns = [],
$limit = DynamoDbQueryBuilder::MAX_LIMIT
) {
$this->applyScopes();
$this->resetExpressions();
$op = 'Scan';
$queryBuilder = DynamoDb::table($this->model->getTable());
if (!empty($this->wheres)) {
$analyzer = $this->getConditionAnalyzer();
if ($keyConditions = $analyzer->keyConditions()) {
$op = 'Query';
$queryBuilder->setKeyConditionExpression($this->keyConditionExpression->parse($keyConditions));
} ...
... and following back from there, in the Analyzer.php, the issue seems to be this line:
if (count(array_intersect($conditionKeys, $keys)) === count($keys)) {
... since - from what I see - that results in 1 === 2, which is of course not true and so the index is not used. And that explains why the query builder defaults to Scan. I'm not quite sure why it's checking the same number is in both since as I understand it, you can query a GSI using just one of the two (ie using just the hash). In the AWS example, they show doing that - using just the hash: (https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SQLtoNoSQL.Indexes.QueryAndScan.html)
Version info
- Laravel: 5.7
- laravel-dynamodb: latest