Skip to content

Commit a056612

Browse files
author
Takashi Matsuo
authored
Adding snippets for Indexes and Transactions. (GoogleCloudPlatform#194)
1 parent 19b8250 commit a056612

File tree

2 files changed

+236
-0
lines changed

2 files changed

+236
-0
lines changed

datastore/api/src/functions/concepts.php

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
// [START datastore_use]
2323
use Google\Cloud\Datastore\DatastoreClient;
2424
// [END datastore_use]
25+
use Google\Cloud\Datastore\Entity;
2526
use Google\Cloud\Datastore\Key;
2627
use Google\Cloud\Datastore\Query\Query;
2728

@@ -725,3 +726,148 @@ function inequality_sort_invalid_not_first(DatastoreClient $datastore)
725726
// [END inequality_sort_invalid_not_first]
726727
return $query;
727728
}
729+
730+
/**
731+
* Create a query with an equality filter on 'description'.
732+
*
733+
* @param DatastoreClient $datastore
734+
* @return Query
735+
*/
736+
function unindexed_property_query(DatastoreClient $datastore)
737+
{
738+
// [START unindexed_property_query]
739+
$query = $datastore->query()
740+
->kind('Task')
741+
->filter('description', '=', 'A task description.');
742+
// [END unindexed_property_query]
743+
return $query;
744+
}
745+
746+
/**
747+
* Create an entity with two array properties.
748+
*
749+
* @param DatastoreClient $datastore
750+
* @return Google\Cloud\Datastore\Entity
751+
*/
752+
function exploding_properties(DatastoreClient $datastore)
753+
{
754+
// [START exploding_properties]
755+
$task = $datastore->entity(
756+
$datastore->key('Task'),
757+
[
758+
'tags' => ['fun', 'programming', 'learn'],
759+
'collaborators' => ['alice', 'bob', 'charlie'],
760+
'created' => new \DateTime(),
761+
]
762+
);
763+
// [END exploding_properties]
764+
return $task;
765+
}
766+
767+
// [START transactional_update]
768+
/**
769+
* Update two entities in a transaction.
770+
*
771+
* @param DatastoreClient $datastore
772+
* @param Key $fromKey
773+
* @param Key $toKey
774+
* @param $amount
775+
*/
776+
function transfer_funds(
777+
DatastoreClient $datastore,
778+
Key $fromKey,
779+
Key $toKey,
780+
$amount
781+
) {
782+
$transaction = $datastore->transaction();
783+
$result = $transaction->lookupBatch([$fromKey, $toKey]);
784+
if (count($result['found']) != 2) {
785+
$transaction->rollback();
786+
}
787+
// Currently, the result from lookupBatch doesn't guarantee the same order
788+
// as the given keys with the client library.
789+
// TODO: remove this hack once the issue below is fixed.
790+
// https://github.com/GoogleCloudPlatform/google-cloud-php/issues/175
791+
if ($result['found'][0]->key()->path() == $fromKey->path()) {
792+
$fromAccount = $result['found'][0];
793+
$toAccount = $result['found'][1];
794+
} else {
795+
$fromAccount = $result['found'][1];
796+
$toAccount = $result['found'][0];
797+
}
798+
$fromAccount['balance'] -= $amount;
799+
$toAccount['balance'] += $amount;
800+
$transaction->updateBatch([$fromAccount, $toAccount]);
801+
$transaction->commit();
802+
}
803+
// [END transactional_update]
804+
805+
/**
806+
* Call a function and retry upon conflicts for several times.
807+
*
808+
* @param DatastoreClient $datastore
809+
* @param Key $fromKey
810+
* @param Key $toKey
811+
*/
812+
function transactional_retry(
813+
DatastoreClient $datastore,
814+
Key $fromKey,
815+
Key $toKey
816+
) {
817+
// [START transactional_retry]
818+
$retries = 5;
819+
for ($i = 0; $i < $retries; $i++) {
820+
try {
821+
transfer_funds($datastore, $fromKey, $toKey, 10);
822+
} catch (Google\Cloud\Exception\ConflictException $e) {
823+
// if $i >= $retries, the failure is final
824+
continue;
825+
}
826+
// Succeeded!
827+
break;
828+
}
829+
// [END transactional_retry]
830+
}
831+
832+
/**
833+
* Insert an entity only if there is no entity with the same key.
834+
*
835+
* @param DatastoreClient $datastore
836+
* @param Entity $task
837+
*/
838+
function get_or_create(DatastoreClient $datastore, Entity $task)
839+
{
840+
// [START transactional_get_or_create]
841+
$transaction = $datastore->transaction();
842+
$existed = $transaction->lookup($task->key());
843+
if ($existed === null) {
844+
$transaction->insert($task);
845+
$transaction->commit();
846+
}
847+
// [END transactional_get_or_create]
848+
}
849+
850+
/**
851+
* Run a query with an ancestor inside a transaction.
852+
*
853+
* @param DatastoreClient $datastore
854+
* @return array<Entity>
855+
*/
856+
function get_task_list_entities(DatastoreClient $datastore)
857+
{
858+
// [START transactional_single_entity_group_read_only]
859+
$transaction = $datastore->transaction();
860+
$taskListKey = $datastore->key('TaskList', 'default');
861+
$query = $datastore->query()
862+
->kind('Task')
863+
->filter('__key__', Query::OP_HAS_ANCESTOR, $taskListKey);
864+
$result = $transaction->runQuery($query);
865+
$taskListEntities = [];
866+
/* @var Entity $task */
867+
foreach ($result as $task) {
868+
$taskListEntities[] = $task;
869+
}
870+
$transaction->commit();
871+
// [END transactional_single_entity_group_read_only]
872+
return $taskListEntities;
873+
}

datastore/api/test/ConceptsTest.php

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,96 @@ public function testInequalitySortInvalidNotFirst()
722722
}
723723
}
724724

725+
public function testUnindexedPropertyQuery()
726+
{
727+
$query = unindexed_property_query(self::$datastore);
728+
$result = self::$datastore->runQuery($query);
729+
$this->assertInstanceOf(\Generator::class, $result);
730+
/* @var Google\Cloud\Datastore\Entity $e */
731+
foreach ($result as $e) {
732+
$this->fail(
733+
sprintf(
734+
'Should not match the entity with this query with '
735+
. ' a description: %s',
736+
$e['description']
737+
)
738+
);
739+
}
740+
}
741+
742+
public function testExplodingProperties()
743+
{
744+
$task = exploding_properties(self::$datastore);
745+
self::$datastore->insert($task);
746+
self::$keys[] = $task->key();
747+
$this->assertEquals(['fun', 'programming', 'learn'], $task['tags']);
748+
$this->assertEquals(
749+
['alice', 'bob', 'charlie'],
750+
$task['collaborators']
751+
);
752+
$this->assertArrayHasKey('id', $task->key()->pathEnd());
753+
}
754+
755+
public function testTransferFunds()
756+
{
757+
$key1 = self::$datastore->key('Account', generateRandomString());
758+
$key2 = self::$datastore->key('Account', generateRandomString());
759+
$entity1 = self::$datastore->entity($key1);
760+
$entity2 = self::$datastore->entity($key2);
761+
$entity1['balance'] = 100;
762+
$entity2['balance'] = 0;
763+
self::$keys[] = $key1;
764+
self::$keys[] = $key2;
765+
self::$datastore->upsert($entity1);
766+
self::$datastore->upsert($entity2);
767+
transfer_funds(self::$datastore, $key1, $key2, 100);
768+
$fromAccount = self::$datastore->lookup($key1);
769+
$this->assertEquals(0, $fromAccount['balance']);
770+
$toAccount = self::$datastore->lookup($key2);
771+
$this->assertEquals(100, $toAccount['balance']);
772+
}
773+
774+
public function testTransactionalRetry()
775+
{
776+
$key1 = self::$datastore->key('Account', generateRandomString());
777+
$key2 = self::$datastore->key('Account', generateRandomString());
778+
$entity1 = self::$datastore->entity($key1);
779+
$entity2 = self::$datastore->entity($key2);
780+
$entity1['balance'] = 10;
781+
$entity2['balance'] = 0;
782+
self::$keys[] = $key1;
783+
self::$keys[] = $key2;
784+
self::$datastore->upsert($entity1);
785+
self::$datastore->upsert($entity2);
786+
transactional_retry(self::$datastore, $key1, $key2);
787+
$fromAccount = self::$datastore->lookup($key1);
788+
$this->assertEquals(0, $fromAccount['balance']);
789+
$toAccount = self::$datastore->lookup($key2);
790+
$this->assertEquals(10, $toAccount['balance']);
791+
}
792+
793+
public function testGetTaskListEntities()
794+
{
795+
$taskListKey = self::$datastore->key('TaskList', 'default');
796+
$taskKey = self::$datastore->key('Task', 'first task')
797+
->ancestorKey($taskListKey);
798+
$task = self::$datastore->entity(
799+
$taskKey,
800+
['description' => 'finish datastore sample']
801+
);
802+
self::$keys[] = $taskKey;
803+
self::$datastore->upsert($task);
804+
$result = get_task_list_entities(self::$datastore);
805+
$num = 0;
806+
/* @var Entity $e */
807+
foreach ($result as $e) {
808+
$this->assertEquals($taskKey->path(), $e->key()->path());
809+
$this->assertEquals('finish datastore sample', $e['description']);
810+
$num += 1;
811+
}
812+
self::assertEquals(1, $num);
813+
}
814+
725815
public function tearDown()
726816
{
727817
if (! empty(self::$keys)) {

0 commit comments

Comments
 (0)