Simple Document Mapper Library
Bego is a PHP library that makes working DynamoDb tables simpler., (*1)
Create your table's model class, (*2)
<?php namespace App\MyTables; class Music extends Bego\Model { /** * Table name */ protected $_name = 'Music'; /** * Table's partition key attribute */ protected $_partition = 'Artist'; /** * Table's sort key attribute */ protected $_sort = 'SongTitle'; /** * List of indexes available for this table */ protected $_indexes = [ 'My-Global-Index' => ['key' => 'Timestamp'], 'My-Local-Index' => [] ]; }
Instantiate the tables you need throughout your app..., (*3)
$config = [ 'version' => '2012-08-10', 'region' => 'eu-west-1', 'credentials' => [ 'key' => 'test', 'secret' => 'test', ], ]; $db = new Bego\Database( new Aws\DynamoDb\DynamoDbClient($config), new Aws\DynamoDb\Marshaler() ); $music = $db->table(new App\MyTables\Music());
You can Query any DynamoDb table or secondary index, provided that it has a composite primary key (partition key and sort key), (*4)
/* Query the table */ $results = $music->query() ->key('Bob Dylan') ->condition(Condition::attribute('SongTitle')->eq('How many roads')) ->filter(Condition::attribute('Year')->eq(1966)) ->fetch(); /* Query a global index */ $results = $music->query('My-Global-Index') ->key('Bob Dylan') ->condition(Condition::attribute('SongTitle')->eq('How many roads')) ->filter(Condition::attribute('Year')->eq(1966)) ->fetch(); /* Query a local index */ $results = $music->query('My-Local-Index') ->key('Bob Dylan') ->condition(Condition::attribute('SongTitle')->eq('How many roads')) ->filter(Condition::attribute('Year')->eq(1966)) ->fetch();
Multiple key condition / filter expressions can be added. DynamoDb applies key conditions to the query but filters are applied to the query results, (*5)
$results = $music->query() ->key('Bob Dylan') ->condition(Condition::attribute('SongTitle')->beginsWith('How')) ->filter(Condition::attribute('Year')->in(['1966', '1967'])) ->fetch();
DynamoDb always sorts results by the sort key value in ascending order. Getting results in descending order can be done using the reverse() flag:, (*6)
$results = $music->query() ->reverse() ->key('Bob Dylan') ->condition(Condition::attribute('SongTitle')->eq('How many roads')) ->fetch();
To get just some, rather than all of the attributes, use a projection expression., (*7)
$results = $music->query() ->key('Bob Dylan') ->projection(['Year', 'SongTitle']) ->condition(Condition::attribute('SongTitle')->beginsWith('How')) ->filter(Condition::attribute('Year')->eq('1966')) ->fetch();
The result set object implements the Iterator interface and canned by used straight way. It provived some handy methods as well., (*8)
/* Execute query and return first page of results */ $results = $music->query() ->key('Bob Dylan') ->condition(Condition::attribute('SongTitle')->eq('How many roads')) ->fetch(); foreach ($results as $item) { echo "{$item->attribute('Id')}\n"; } echo "{$results->count()} items in result set\n"; echo "{$results->getScannedCount()} items scanned in query\n"; echo "{$results->getQueryCount()} trips to the database\n"; echo "{$results->getQueryTime()} total execution time (seconds)\n"; $item = $results->first(); $item = $results->last(); //Get the 3rd item $item = $results->item(3); //Extract one attribute from all items $allTitles = $results->attribute('SongTitle'); //Aggregegate one attribute for all items $totalSales = $results->sum('Sales');
DynamoDb performs eventual consistent reads by default. For strongly consistent reads set the consistent() flag:, (*9)
$results = $music->query() ->key('Bob Dylan') ->condition(Condition::attribute('SongTitle')->eq('How many roads')) ->consistent() ->fetch();
DynamoDb allows you to limit the number of items returned in the result. Note that this limit is applied to the key conidtion only. DynamoDb will apply filters after the limit is imposed on the result set:, (*10)
$results = $music->query() ->key('Bob Dylan') ->condition(Condition::attribute('SongTitle')->eq('How many roads')) ->limit(100) ->fetch();
DynanmoDb limits the results to 1MB. Therefor, pagination has to be implemented to traverse beyond the first page. There are two options available to do the pagination work:, (*11)
$results = $music->query() ->key('Bob Dylan') ->condition(Condition::attribute('SongTitle')->eq('How many roads')); /* Option 1: Get one page orf results only (default) */ $results = $query->fetch(); /* Option 2: Execute up to 10 queries */ $results = $query->fetch(10); /* Option 3: Get all items in the dataset no matter the cost */ $results = $query->fetch(null);
In some cases one may want to paginate accross multiple hops;, (*12)
$results = $music->query() ->key('Bob Dylan') ->condition(Condition::attribute('SongTitle')->eq('How many roads')); /* First Hop: Get one page */ $results = $query->fetch(1); $pointer = $results->getLastEvaluatedKey(); /* Second Hop: Get one more page, continueing from previous request */ $results = $query->fetch(1, $pointer);
DynamoDb can calculate the total number of read capacity units for every query. This can be enabled using the consumption() flag:, (*13)
$results = $music->query() ->key('Bob Dylan') ->condition(Condition::attribute('SongTitle')->eq('How many roads')) ->consumption() ->fetch(); echo $results->getCapacityUnitsConsumed();
Basic table scan's are supported. Filter expressions, results and pagination work the same as with queries, (*14)
/* Scan the table */ $results = $table->scan() ->filter('Artist', '=', $artist) ->consistent() ->consumption() ->limit(100) ->fetch();
/* Scan the secondary index */ $results = $table->scan('My-Global-Index') ->filter(Condition::attribute('Year')->eq(1966)) ->fetch();
/* Create and persist a new item */ $item = $music->put([ 'Id' => uniqid(), 'Artitst' => 'Bob Dylan', 'SongTitle' => 'How many roads' ]);
Batch writing will automatically deal with a) DynamoDb's batch size limits, b) efficiency, i.e. running multiple workers in parallel, c) handling unresolved items and d) retrying any errors due to provision limits, (*15)
/* Create multiple items with batch writing */ $item = $music->putBatch( [ 'Id' => uniqid(), 'Artist' => 'Neil Diamond', 'SongTitle' => 'Red, red wine', 'Year' => 1968, 'Time' => date('Y-m-d H:i:s') ], [ 'Id' => uniqid(), 'Artist' => 'Bob Marley', 'SongTitle' => 'Buffalo Soldier', 'Year' => 1984, 'Time' => date('Y-m-d H:i:s') ] );
/* Fetch an item */ $item = $music->fetch( 'Bob Dylan', 'How many roads' ); if ($item->isEmpty()) { throw new \Exception("Requested record could not be found"); } if ($item->isSet('hit')) { echo "{$item->attribute('SongTitle')} is a hit"; } echo $item->attribute('Id');
/* Perform a consistent read */ $item = $music->fetch( 'Bob Dylan', 'How many roads', true ); if ($item->isEmpty()) { throw new \Exception("Requested record could not be found"); } echo $item->attribute('Id');
/* Return value if attribute exists, otherwise NULL */ echo $item->attribute('Artist'); echo $item->Artist; //shorthand /* Return value if attribute exists, otherwise throw exception */ echo $item->ping('Artist'); /* Checking if an attribute exists and not empty */ echo $item->isSet('Artist') ? 'Yes' : 'No'; echo isset($item->Artist) ? 'Yes' : 'No'; //shorthand
/* Update an item */ $item->set('Year', 1966); $item->Year = 1966; //shorthand $result = $music->update($item); $results = $music->query() ->key('Bob Dylan') ->condition(Condition::attribute('SongTitle')->eq('How many roads')) ->filter(Condition::attribute('Year')->eq(1966)) ->fetch(); foreach ($results as $item) { $item->set('Year', $item->attribute('Year') + 1); $music->update($item); }
Making use of an item's magic properties instead of set() and attribute(), (*16)
/* Update an item */ $item->Year = 1966; $result = $music->update($item); $results = $music->query() ->key('Bob Dylan') ->condition(Condition::attribute('SongTitle')->eq('How many roads')) ->filter(Condition::attribute('Year')->eq(1966)) ->fetch(); foreach ($results as $item) { $item->Year = $item->Year + 1; $music->update($item); }
use Bego\Condition; $conditions = [ Condition::attribute('Year')->beginsWith('19') Condition::attribute('Year')->exists() Condition::attribute('Year')->eq(1966) Condition::attribute('Year')->in([1966, 1967]) ]; $result = $music->update($item, $conditions); if ($result) { echo 'Item updated successfully' }
$music->delete($item);
/* Delete multiple items with batch writing */, (*17)
$response = $table->deleteBatch($items);
$spec = [ 'types' => [ 'partition' => 'S', 'sort' => 'S', ], 'capacity' => ['read' => 5, 'write' => 5], 'indexes' => [ 'My-Global-Index' => [ 'type' => 'global', 'keys' => [ ['name' => 'Year', 'types' => ['key' => 'HASH', 'attribute' => 'N']], ['name' => 'Artist', 'types' => ['key' => 'RANGE', 'attribute' => 'S']], ], 'capacity' => ['read' => 5, 'write' => 5] ], ], ]; $music->create($spec);