**Describe the bug**
When using `withIndex()` in combination with `limit()`, the package incorrectly generates a DynamoDB `Scan`
operation instead of a `Query` operation.
This results in:
1. Performance Issues: Scanning the entire GSI instead of querying it efficiently
2. Incorrect Results: Returns 0 results when it should return the limited number of items
3. Wrong Operation Type: Uses `FilterExpression` (Scan) instead of `KeyConditionExpression` (Query)
The bug occurs specifically when chaining `withIndex()`, `where()`, and `limit()` together. Without `limit()`,
the query works correctly and uses the proper Query operation.
**Schema**
Table schema:
* Primary key: Composite key `PK` (partition key), `SK` (sort key)
* GSI1: `TasksByStatusIndex`
- Partition key: `GSI1PK` (e.g., "STATUS#OPEN")
- Sort key: `GSI1SK` (e.g., "T")
* GSI2: `TasksByUserIndex`
- Partition key: `GSI2PK` (e.g., "USER#123")
- Sort key: `GSI2SK` (e.g., "OPEN#T")
Model configuration:
```php
public function getDynamoDbIndexKeys() {
return [
'TasksByStatusIndex' => [
'hash' => 'GSI1PK',
'range' => 'GSI1SK',
],
'TasksByUserIndex' => [
'hash' => 'GSI2PK',
'range' => 'GSI2SK',
],
];
}
Debug info
Working query (without limit):
$query = Task::withIndex('TasksByStatusIndex')->where('GSI1PK', 'STATUS#OPEN');
print_r($query->toDynamoDbQuery());
Results in proper Query operation (works correctly, returns 17 results).
Broken query (with limit):
$query = Task::withIndex('TasksByStatusIndex')->where('GSI1PK', 'STATUS#OPEN')->limit(2);
print_r($query->toDynamoDbQuery());
Output:
BaoPham\DynamoDb\RawDynamoDbQuery Object
(
[op] => Scan // ❌ Should be "Query"
[query] => Array
(
[TableName] => local_tasks
[FilterExpression] => #GSI1PK = :a1 // ❌ Should be "KeyConditionExpression"
[IndexName] => TasksByStatusIndex
[ExpressionAttributeNames] => Array
(
[#GSI1PK] => GSI1PK
)
[ExpressionAttributeValues] => Array
(
[:a1] => Array
(
[S] => STATUS#OPEN
)
)
)
)
Expected behavior: Should generate a Query operation with KeyConditionExpression and return 2 results.
Actual behavior: Generates a Scan operation with FilterExpression and returns 0 results.
Reproduction steps:
- Create a model extending
DynamoDbModel with GSI configuration
- Use
Model::withIndex('IndexName')->where('GSI_KEY', 'value')->limit(N)->get()
- Observe that it returns 0 results and uses Scan instead of Query
Workaround:
Use the raw DynamoDB client directly:
$client->query([
'TableName' => $tableName,
'IndexName' => 'TasksByStatusIndex',
'KeyConditionExpression' => 'GSI1PK = :status',
'ExpressionAttributeValues' => [':status' => ['S' => 'STATUS#OPEN']],
'Limit' => 2
]);
Version info
- Laravel: 12.20.0
- PHP: 8.3.0
- baopham/dynamodb: 6.6.0
Edit: better formatting of the report.
Debug info
Working query (without limit):
Results in proper Query operation (works correctly, returns 17 results).
Broken query (with limit):
Output:
Expected behavior: Should generate a Query operation with
KeyConditionExpressionand return 2 results.Actual behavior: Generates a Scan operation with
FilterExpressionand returns 0 results.Reproduction steps:
DynamoDbModelwith GSI configurationModel::withIndex('IndexName')->where('GSI_KEY', 'value')->limit(N)->get()Workaround:
Use the raw DynamoDB client directly:
Version info
Edit: better formatting of the report.