Skip to content

Commit

Permalink
Merge pull request #34 from kitar/batch-write-item
Browse files Browse the repository at this point in the history
  • Loading branch information
kitar authored Dec 23, 2022
2 parents edb21cb + e29ad04 commit 83dfe30
Show file tree
Hide file tree
Showing 7 changed files with 492 additions and 7 deletions.
91 changes: 85 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ You can find an example implementation in [kitar/simplechat](https://github.com/
* [Using Global Secondary Indexes](#using-global-secondary-indexes)
+ [index()](#index)
* [Atomic Counter](#atomic-counter)
* [Batch Operations](#batch-operations)
+ [batchGetItem()](#batchgetitem)
+ [batchPutItem()](#batchputitem)
+ [batchDeleteItem()](#batchdeleteitem)
+ [batchWriteItem()](#batchwriteitem)
* [DynamoDB-specific operators for condition() and filter()](#dynamodb-specific-operators-for-condition-and-filter)
+ [Comparators](#comparators)
+ [functions](#functions)
Expand Down Expand Up @@ -197,7 +202,6 @@ class User extends Model implements AuthenticatableContract
}
```

> **Note**
> Note that this model is implementing `Illuminate\Contracts\Auth\Authenticatable` and using `Illuminate\Auth\Authenticatable`. This is **optional**, but if we use them, we can use this model with authentication as well. For authentication, please refer to [Authentication section](#authentication-with-model)) for more details.
### Basic Usage
Expand Down Expand Up @@ -234,7 +238,6 @@ public static function scan($exclusiveStartKey = null, $sort = 'asc', $limit = 5
}
```

> **Note**
> DynamoDB can only handle result set up to 1MB per call, so we have to paginate if there are more results. see [Paginating the Results](#paginating-the-results) for more details.
#### Retrieving a model
Expand Down Expand Up @@ -474,7 +477,6 @@ $response = DB::table('ProductCatalog')
->getItem(['Id' => 101]);
```

> **Note**
> Instead of marshaling manually, pass a plain array. `Kitar\Dynamodb\Query\Grammar` will automatically marshal them before querying.
#### putItem()
Expand Down Expand Up @@ -547,7 +549,6 @@ DB::table('ProductCatalog')
]);
```

> **Note**
> Note that we specify `attribute_not_exists` for the operator of condition. This is DynamoDB-specific operator which called `function`. See [DynamoDB-specific operators for condition() and filter()](#dynamodb-specific-operators-for-condition-and-filter) for more details.
OR statements
Expand Down Expand Up @@ -625,7 +626,6 @@ $response = DB::table('Thread')
->query();
```

> **Note**
> Note that DynamoDB's `ScanIndexForward` is a feature for `query`. It will not work with `scan`.
### Working with Scans
Expand Down Expand Up @@ -755,6 +755,86 @@ DB::('Thread')->key([
]);
```

### Batch Operations

Batch operations can get, put or delete multiple items with a single call. There are some DynamoDB limitations (such as items count, payload size, etc), so please check the documentation in advance. ([BatchGetItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html), [BatchWriteItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html))

#### batchGetItem()

```php
DB::table('Thread')
->batchGetItem([
[
'ForumName' => 'Amazon DynamoDB',
'Subject' => 'DynamoDB Thread 1'
],
[
'ForumName' => 'Amazon DynamoDB',
'Subject' => 'DynamoDB Thread 2'
]
]);
```

#### batchPutItem()

```php
DB::table('Thread')
->batchPutItem([
[
'ForumName' => 'Amazon DynamoDB',
'Subject' => 'DynamoDB Thread 3'
],
[
'ForumName' => 'Amazon DynamoDB',
'Subject' => 'DynamoDB Thread 4'
]
]);
```

> This is a handy method to batch-put items using `batchWriteItem`
#### batchDeleteItem()

```php
DB::table('Thread')
->batchDeleteItem([
[
'ForumName' => 'Amazon DynamoDB',
'Subject' => 'DynamoDB Thread 1'
],
[
'ForumName' => 'Amazon DynamoDB',
'Subject' => 'DynamoDB Thread 2'
]
]);
```

> This is a handy method to batch-delete items using `batchWriteItem`
#### batchWriteItem()

```php
DB::table('Thread')
->batchWriteItem([
[
'PutRequest' => [
'Item' => [
'ForumName' => 'Amazon DynamoDB',
'Subject' => 'DynamoDB Thread 3'
]
]
],
[
'DeleteRequest' => [
'Key' => [
'ForumName' => 'Amazon DynamoDB',
'Subject' => 'DynamoDB Thread 1'
]
]
]
]);
```

### DynamoDB-specific operators for condition() and filter()

For `condition` and `filter` clauses, we can use DynamoDB's comparators and functions.
Expand All @@ -779,7 +859,6 @@ filter($key, 'begins_with', $value);
filter($key, 'contains', $value);
```

> **Note**
> `size` function is not supported at this time.
## Testing
Expand Down
4 changes: 4 additions & 0 deletions src/Kitar/Dynamodb/Model/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,10 @@ public function __call($method, $parameters)
"putItem",
"deleteItem",
"updateItem",
"batchGetItem",
"batchPutItem",
"batchDeleteItem",
"batchWriteItem",
"scan",
"filter",
"filterIn",
Expand Down
58 changes: 58 additions & 0 deletions src/Kitar/Dynamodb/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ class Builder extends BaseBuilder
'remove' => []
];

/**
* Keys array for BatchGetItem
* https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html
* @var array
*/
public $batch_get_keys = [];

/**
* RequestItems array for BatchWriteItem
* https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html
* @var array
*/
public $batch_write_request_items = [];

/**
* ScanIndexForward option.
*/
Expand Down Expand Up @@ -312,6 +326,48 @@ public function updateItem($item)
return $this->process('updateItem', 'processSingleItem');
}

public function batchGetItem($keys)
{
$this->batch_get_keys = $keys;

return $this->process('batchGetItem', 'processBatchGetItems');
}

public function batchPutItem($items)
{
$this->batch_write_request_items = collect($items)->map(function ($item) {
return [
'PutRequest' => [
'Item' => $item,
],
];
})->toArray();

return $this->batchWriteItem();
}

public function batchDeleteItem($keys)
{
$this->batch_write_request_items = collect($keys)->map(function ($key) {
return [
'DeleteRequest' => [
'Key' => $key,
],
];
})->toArray();

return $this->batchWriteItem();
}

public function batchWriteItem($request_items = [])
{
if (! empty($request_items)) {
$this->batch_write_request_items = $request_items;
}

return $this->process('batchWriteItem', null);
}

/**
* @inheritdoc
*/
Expand Down Expand Up @@ -539,6 +595,8 @@ protected function process($query_method, $processor_method)
$this->grammar->compileKey($this->key),
$this->grammar->compileItem($this->item),
$this->grammar->compileUpdates($this->updates),
$this->grammar->compileBatchGetRequestItems($this->from, $this->batch_get_keys),
$this->grammar->compileBatchWriteRequestItems($this->from, $this->batch_write_request_items),
$this->grammar->compileDynamodbLimit($this->limit),
$this->grammar->compileScanIndexForward($this->scan_index_forward),
$this->grammar->compileExclusiveStartKey($this->exclusive_start_key),
Expand Down
48 changes: 48 additions & 0 deletions src/Kitar/Dynamodb/Query/Grammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,54 @@ public function compileUpdates($updates)
];
}

public function compileBatchGetRequestItems($table_name, $keys)
{
if (empty($keys)) {
return [];
}

$marshaler = $this->marshaler;
$marshaled_items = collect($keys)->map(function ($key) use ($marshaler) {
return $marshaler->marshalItem($key);
})->toArray();

$table_name = $this->tablePrefix . $table_name;

return [
'RequestItems' => [
$table_name => [
'Keys' => $marshaled_items,
],
]
];
}

public function compileBatchWriteRequestItems($table_name, $request_items)
{
if (empty($request_items)) {
return [];
}

$marshaler = $this->marshaler;
$marshaled_items = collect($request_items)->map(function ($request_item) use ($marshaler) {
return collect($request_item)->map(function ($request_body) use ($marshaler) {
$marshaled = [];
foreach ($request_body as $key => $body) {
$marshaled[$key] = $marshaler->marshalItem($body);
}
return $marshaled;
});
})->toArray();

$table_name = $this->tablePrefix . $table_name;

return [
'RequestItems' => [
$table_name => $marshaled_items,
],
];
}

/**
* Compile the Limit attribute.
*
Expand Down
33 changes: 33 additions & 0 deletions src/Kitar/Dynamodb/Query/Processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ protected function unmarshal(Result $res)
$responseArray['Attributes'] = $this->marshaler->unmarshalItem($responseArray['Attributes']);
}

if (! empty($responseArray['Responses'])) {
foreach ($responseArray['Responses'] as &$items) {
foreach ($items as &$item) {
$item = $this->marshaler->unmarshalItem($item);
}
}
}

return $responseArray;
}

Expand Down Expand Up @@ -78,4 +86,29 @@ public function processMultipleItems(Result $awsResponse, $modelClass = null)
return $item;
});
}

public function processBatchGetItems(Result $awsResponse, $modelClass = null)
{
$response = $this->unmarshal($awsResponse);

if (empty($modelClass)) {
return $response;
}

$items = collect();

foreach ($response['Responses'] as $_ => $table_items) {
foreach ($table_items as $item) {
$item = (new $modelClass)->newFromBuilder($item);
$items->push($item);
}
}

unset($response['Responses']);

return $items->map(function ($item) use ($response) {
$item->setMeta($response);
return $item;
});
}
}
Loading

0 comments on commit 83dfe30

Please sign in to comment.