diff --git a/datastore/api/README.md b/datastore/api/README.md index b7bb839763..15949a271e 100644 --- a/datastore/api/README.md +++ b/datastore/api/README.md @@ -17,4 +17,5 @@ To run the tests do the following: 1. **Install dependencies** via [Composer](http://getcomposer.org/doc/00-intro.md). Run `php composer.phar install` (if composer is installed locally) or `composer install` (if composer is installed globally). +1. Create Datastore indexes by running `gcloud preview datastore create-indexes index.yaml` 1. Run `phpunit` diff --git a/datastore/api/composer.json b/datastore/api/composer.json index d6ee8c4d8a..4685d5ac9d 100644 --- a/datastore/api/composer.json +++ b/datastore/api/composer.json @@ -1,9 +1,10 @@ { "require": { - "google/cloud": "dev-master" + "google/cloud": "~1.0|~0.10" }, "require-dev": { - "phpunit/phpunit": "~4.8" + "phpunit/phpunit": "~4.8", + "google/cloud-tools": "~1.0|~0.5" }, "autoload": { "psr-4": { "Google\\Cloud\\Samples\\Datastore\\": "src" }, diff --git a/datastore/api/composer.lock b/datastore/api/composer.lock index e91b1fcdbd..a3e371d3e4 100644 --- a/datastore/api/composer.lock +++ b/datastore/api/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "8585afa8c1d4587c1620b194e863c847", - "content-hash": "cc613b45668bdd9f988235621efe49b0", + "hash": "23ff977eebca4dfc0ebf4fdae9c9b881", + "content-hash": "73bb877c2f05f502073a5cfe66c42d20", "packages": [ { "name": "firebase/php-jwt", @@ -100,16 +100,16 @@ }, { "name": "google/cloud", - "version": "dev-master", + "version": "v0.10.2", "source": { "type": "git", "url": "https://github.com/GoogleCloudPlatform/google-cloud-php.git", - "reference": "3cf8be5b52f5cf7918e522f18d882b80b4952cda" + "reference": "97d06b1106ee9bca591ad0573ee71e32a8500992" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GoogleCloudPlatform/google-cloud-php/zipball/3cf8be5b52f5cf7918e522f18d882b80b4952cda", - "reference": "3cf8be5b52f5cf7918e522f18d882b80b4952cda", + "url": "https://api.github.com/repos/GoogleCloudPlatform/google-cloud-php/zipball/97d06b1106ee9bca591ad0573ee71e32a8500992", + "reference": "97d06b1106ee9bca591ad0573ee71e32a8500992", "shasum": "" }, "require": { @@ -180,20 +180,20 @@ "translate", "vision" ], - "time": "2016-09-29 14:07:37" + "time": "2016-10-11 17:58:10" }, { "name": "guzzlehttp/guzzle", - "version": "6.2.1", + "version": "6.2.2", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "3f808fba627f2c5b69e2501217bf31af349c1427" + "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/3f808fba627f2c5b69e2501217bf31af349c1427", - "reference": "3f808fba627f2c5b69e2501217bf31af349c1427", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ebf29dee597f02f09f4d5bbecc68230ea9b08f60", + "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60", "shasum": "" }, "require": { @@ -242,7 +242,7 @@ "rest", "web service" ], - "time": "2016-07-15 17:22:37" + "time": "2016-10-08 15:01:37" }, { "name": "guzzlehttp/promises", @@ -529,16 +529,16 @@ }, { "name": "psr/log", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "5277094ed527a1c4477177d102fe4c53551953e0" + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/5277094ed527a1c4477177d102fe4c53551953e0", - "reference": "5277094ed527a1c4477177d102fe4c53551953e0", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", "shasum": "" }, "require": { @@ -572,7 +572,7 @@ "psr", "psr-3" ], - "time": "2016-09-19 16:02:08" + "time": "2016-10-10 12:19:37" }, { "name": "rize/uri-template", @@ -674,6 +674,56 @@ ], "time": "2015-06-14 21:17:01" }, + { + "name": "google/cloud-tools", + "version": "v0.5.0", + "source": { + "type": "git", + "url": "https://github.com/GoogleCloudPlatform/php-tools.git", + "reference": "79317583835061e4f4bf981376be84c41a4f4534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GoogleCloudPlatform/php-tools/zipball/79317583835061e4f4bf981376be84c41a4f4534", + "reference": "79317583835061e4f4bf981376be84c41a4f4534", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "~5.3|~6.0", + "php": ">=5.5", + "phpunit/phpunit": "~4|~5", + "symfony/browser-kit": "~2|~3", + "symfony/process": "~2|~3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Google\\Cloud\\TestUtils\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Takashi Matsuo", + "email": "tmatsuo@google.com", + "homepage": "https://wp.gaeflex.ninja/" + } + ], + "description": "PHP tools for Google Cloud Platform", + "homepage": "https://github.com/GoogleCloudPlatform/php-tools", + "keywords": [ + "appengine", + "gcp", + "test" + ], + "time": "2016-10-07 22:03:44" + }, { "name": "phpdocumentor/reflection-common", "version": "1.0", @@ -730,16 +780,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "3.1.0", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "9270140b940ff02e58ec577c237274e92cd40cdd" + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9270140b940ff02e58ec577c237274e92cd40cdd", - "reference": "9270140b940ff02e58ec577c237274e92cd40cdd", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", "shasum": "" }, "require": { @@ -771,7 +821,7 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2016-06-10 09:48:41" + "time": "2016-09-30 07:12:33" }, { "name": "phpdocumentor/type-resolver", @@ -1625,18 +1675,239 @@ "homepage": "https://github.com/sebastianbergmann/version", "time": "2015-06-21 13:59:46" }, + { + "name": "symfony/browser-kit", + "version": "v3.1.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/browser-kit.git", + "reference": "901319a31c9b3cee7857b4aeeb81b5d64dfa34fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/901319a31c9b3cee7857b4aeeb81b5d64dfa34fc", + "reference": "901319a31c9b3cee7857b4aeeb81b5d64dfa34fc", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/dom-crawler": "~2.8|~3.0" + }, + "require-dev": { + "symfony/css-selector": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0" + }, + "suggest": { + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\BrowserKit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony BrowserKit Component", + "homepage": "https://symfony.com", + "time": "2016-09-06 11:02:40" + }, + { + "name": "symfony/dom-crawler", + "version": "v3.1.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "bb7395e8b1db3654de82b9f35d019958276de4d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/bb7395e8b1db3654de82b9f35d019958276de4d7", + "reference": "bb7395e8b1db3654de82b9f35d019958276de4d7", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/css-selector": "~2.8|~3.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DomCrawler Component", + "homepage": "https://symfony.com", + "time": "2016-08-05 08:37:39" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "dff51f72b0706335131b00a7f49606168c582594" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/dff51f72b0706335131b00a7f49606168c582594", + "reference": "dff51f72b0706335131b00a7f49606168c582594", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2016-05-18 14:26:46" + }, + { + "name": "symfony/process", + "version": "v3.1.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "66de154ae86b1a07001da9fbffd620206e4faf94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/66de154ae86b1a07001da9fbffd620206e4faf94", + "reference": "66de154ae86b1a07001da9fbffd620206e4faf94", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2016-09-29 14:13:09" + }, { "name": "symfony/yaml", - "version": "v3.1.4", + "version": "v3.1.5", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "f291ed25eb1435bddbe8a96caaef16469c2a092d" + "reference": "368b9738d4033c8b93454cb0dbd45d305135a6d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/f291ed25eb1435bddbe8a96caaef16469c2a092d", - "reference": "f291ed25eb1435bddbe8a96caaef16469c2a092d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/368b9738d4033c8b93454cb0dbd45d305135a6d3", + "reference": "368b9738d4033c8b93454cb0dbd45d305135a6d3", "shasum": "" }, "require": { @@ -1672,7 +1943,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2016-09-02 02:12:52" + "time": "2016-09-25 08:27:07" }, { "name": "webmozart/assert", @@ -1727,9 +1998,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "google/cloud": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": [], diff --git a/datastore/api/index.yaml b/datastore/api/index.yaml new file mode 100644 index 0000000000..c8a2af1aed --- /dev/null +++ b/datastore/api/index.yaml @@ -0,0 +1,31 @@ +indexes: +- kind: Task + properties: + - name: done + - name: priority + +- kind: Task + properties: + - name: percent_complete + - name: priority + +- kind: Task + properties: + - name: category + - name: priority + +- kind: Task + properties: + - name: collaborators + - name: tags + +- kind: Task + properties: + - name: done + - name: priority + - name: created + +- kind: Task + properties: + - name: priority + - name: created diff --git a/datastore/api/src/functions/concepts.php b/datastore/api/src/functions/concepts.php index 7832962ff0..72dc1bec0f 100644 --- a/datastore/api/src/functions/concepts.php +++ b/datastore/api/src/functions/concepts.php @@ -17,10 +17,13 @@ namespace Google\Cloud\Samples\Datastore; -// [START datastore_use ] +use Generator; +use Google; +// [START datastore_use] use Google\Cloud\Datastore\DatastoreClient; -// [END datastore_use ] +// [END datastore_use] use Google\Cloud\Datastore\Key; +use Google\Cloud\Datastore\Query\Query; /** * Initialize the Datastore client. @@ -29,9 +32,9 @@ */ function initialize_client() { - // [START init_client] + // [START initialize_client] $datastore = new DatastoreClient(); - // [END init_client] + // [END initialize_client] return $datastore; } @@ -39,18 +42,18 @@ function initialize_client() * Create a Datastore entity. * * @param DatastoreClient $datastore - * @return \Google\Cloud\Datastore\Entity + * @return Google\Cloud\Datastore\Entity */ -function create_entity(DatastoreClient $datastore) +function basic_entity(DatastoreClient $datastore) { - // [START create_entity] + // [START basic_entity] $task = $datastore->entity('Task', [ 'category' => 'Personal', 'done' => false, 'priority' => 4, 'description' => 'Learn Cloud Datastore' ]); - // [END create_entity] + // [END basic_entity] return $task; } @@ -58,11 +61,11 @@ function create_entity(DatastoreClient $datastore) * Create a Datastore entity and upsert it. * * @param DatastoreClient $datastore - * @return \Google\Cloud\Datastore\Entity + * @return Google\Cloud\Datastore\Entity */ -function upsert_entity(DatastoreClient $datastore) +function upsert(DatastoreClient $datastore) { - // [START upsert_entity] + // [START upsert] $key = $datastore->key('Task', 'sampleTask'); $task = $datastore->entity($key, [ 'category' => 'Personal', @@ -71,7 +74,7 @@ function upsert_entity(DatastoreClient $datastore) 'description' => 'Learn Cloud Datastore' ]); $datastore->upsert($task); - // [END upsert_entity] + // [END upsert] return $task; } @@ -81,11 +84,11 @@ function upsert_entity(DatastoreClient $datastore) * an entity with the same key. * * @param DatastoreClient $datastore - * @return \Google\Cloud\Datastore\Entity + * @return Google\Cloud\Datastore\Entity */ -function insert_entity(DatastoreClient $datastore) +function insert(DatastoreClient $datastore) { - // [START insert_entity] + // [START insert] $task = $datastore->entity('Task', [ 'category' => 'Personal', 'done' => false, @@ -93,7 +96,7 @@ function insert_entity(DatastoreClient $datastore) 'description' => 'Learn Cloud Datastore' ]); $datastore->insert($task); - // [END insert_entity] + // [END insert] return $task; } @@ -101,14 +104,14 @@ function insert_entity(DatastoreClient $datastore) * Look up a Datastore entity with the given key. * * @param DatastoreClient $datastore - * @return \Google\Cloud\Datastore\Entity|null + * @return Google\Cloud\Datastore\Entity|null */ function lookup(DatastoreClient $datastore) { - // [START lookup_entity] + // [START lookup] $key = $datastore->key('Task', 'sampleTask'); $task = $datastore->lookup($key); - // [END lookup_entity] + // [END lookup] return $task; } @@ -116,18 +119,18 @@ function lookup(DatastoreClient $datastore) * Update a Datastore entity in a transaction. * * @param DatastoreClient $datastore - * @return \Google\Cloud\Datastore\Entity|null + * @return Google\Cloud\Datastore\Entity|null */ -function update_entity(DatastoreClient $datastore) +function update(DatastoreClient $datastore) { - // [START update_entity] + // [START update] $transaction = $datastore->transaction(); $key = $datastore->key('Task', 'sampleTask'); $task = $transaction->lookup($key); $task['priority'] = 5; $transaction->upsert($task); $transaction->commit(); - // [END update_entity] + // [END update] return $task; } @@ -137,24 +140,24 @@ function update_entity(DatastoreClient $datastore) * @param DatastoreClient $datastore * @param Key $taskKey */ -function delete_entity(DatastoreClient $datastore, Key $taskKey) +function delete(DatastoreClient $datastore, Key $taskKey) { - // [START delete_entity] + // [START delete] $datastore->delete($taskKey); - // [END delete_entity] + // [END delete] } /** * Upsert multiple Datastore entities. * * @param DatastoreClient $datastore - * @param array <\Google\Cloud\Datastore\Entity> $tasks + * @param array $tasks */ -function upsert_multi(DatastoreClient $datastore, array $tasks) +function batch_upsert(DatastoreClient $datastore, array $tasks) { - // [START upsert_multi] + // [START batch_upsert] $datastore->upsertBatch($tasks); - // [END upsert_multi] + // [END batch_upsert] } /** @@ -162,18 +165,18 @@ function upsert_multi(DatastoreClient $datastore, array $tasks) * * @param DatastoreClient $datastore * @param array $keys - * @return array <\Google\Cloud\Datastore\Entity> + * @return array */ -function lookup_multi(DatastoreClient $datastore, array $keys) +function batch_lookup(DatastoreClient $datastore, array $keys) { - // [START lookup_multi] + // [START batch_lookup] $result = $datastore->lookupBatch($keys); if (isset($result['found'])) { // $result['found'] is an array of entities. } else { // No entities found. } - // [END lookup_multi] + // [END batch_lookup] return $result; } @@ -183,11 +186,11 @@ function lookup_multi(DatastoreClient $datastore, array $keys) * @param DatastoreClient $datastore * @param array $keys */ -function delete_multi(DatastoreClient $datastore, array $keys) +function batch_delete(DatastoreClient $datastore, array $keys) { - // [START delete_multi] + // [START batch_delete] $datastore->deleteBatch($keys); - // [END delete_multi] + // [END batch_delete] } /** @@ -196,7 +199,7 @@ function delete_multi(DatastoreClient $datastore, array $keys) * @param DatastoreClient $datastore * @return Key */ -function create_complete_key(DatastoreClient $datastore) +function named_key(DatastoreClient $datastore) { // [START named_key] $taskKey = $datastore->key('Task', 'sampleTask'); @@ -210,7 +213,7 @@ function create_complete_key(DatastoreClient $datastore) * @param DatastoreClient $datastore * @return Key */ -function create_incomplete_key(DatastoreClient $datastore) +function incomplete_key(DatastoreClient $datastore) { // [START incomplete_key] $taskKey = $datastore->key('Task'); @@ -224,7 +227,7 @@ function create_incomplete_key(DatastoreClient $datastore) * @param DatastoreClient $datastore * @return Key */ -function create_key_with_parent(DatastoreClient $datastore) +function key_with_parent(DatastoreClient $datastore) { // [START key_with_parent] $taskKey = $datastore->key('TaskList', 'default') @@ -239,13 +242,13 @@ function create_key_with_parent(DatastoreClient $datastore) * @param DatastoreClient $datastore * @return Key */ -function create_key_with_multi_level_parent(DatastoreClient $datastore) +function key_with_multilevel_parent(DatastoreClient $datastore) { - // [START key_with_multi_level_parent] + // [START key_with_multilevel_parent] $taskKey = $datastore->key('User', 'alice') ->pathElement('TaskList', 'default') ->pathElement('Task', 'sampleTask'); - // [END key_with_multi_level_parent] + // [END key_with_multilevel_parent] return $taskKey; } @@ -254,9 +257,9 @@ function create_key_with_multi_level_parent(DatastoreClient $datastore) * * @param DatastoreClient $datastore * @param Key $key - * @return \Google\Cloud\Datastore\Entity + * @return Google\Cloud\Datastore\Entity */ -function create_entity_with_option(DatastoreClient $datastore, Key $key) +function properties(DatastoreClient $datastore, Key $key) { // [START properties] $task = $datastore->entity( @@ -265,6 +268,7 @@ function create_entity_with_option(DatastoreClient $datastore, Key $key) 'category' => 'Personal', 'created' => new \DateTime(), 'done' => false, + 'priority' => 4, 'percent_complete' => 10.0, 'description' => 'Learn Cloud Datastore' ], @@ -279,11 +283,11 @@ function create_entity_with_option(DatastoreClient $datastore, Key $key) * * @param DatastoreClient $datastore * @param Key $key - * @return \Google\Cloud\Datastore\Entity + * @return Google\Cloud\Datastore\Entity */ -function create_entity_with_array_property(DatastoreClient $datastore, Key $key) +function array_value(DatastoreClient $datastore, Key $key) { - // [START entity_with_array_property] + // [START array_value] $task = $datastore->entity( $key, [ @@ -291,6 +295,433 @@ function create_entity_with_array_property(DatastoreClient $datastore, Key $key) 'collaborators' => ['alice', 'bob'] ] ); - // [END entity_with_option] + // [END array_value] return $task; } + +/** + * Create a basic Datastore query. + * + * @param DatastoreClient $datastore + * @return Query + */ +function basic_query(DatastoreClient $datastore) +{ + // [START basic_query] + $query = $datastore->query() + ->kind('Task') + ->filter('done', '=', false) + ->filter('priority', '>=', 4) + ->order('priority', Query::ORDER_DESCENDING); + // [END basic_query] + return $query; +} + +/** + * Run a given query. + * + * @param DatastoreClient $datastore + * @return Generator + */ +function run_query(DatastoreClient $datastore, Query $query) +{ + // [START run_query] + $result = $datastore->runQuery($query); + // [END run_query] + return $result; +} + +/** + * Create a query with a property filter. + * + * @param DatastoreClient $datastore + * @return Query + */ +function property_filter(DatastoreClient $datastore) +{ + // [START property_filter] + $query = $datastore->query() + ->kind('Task') + ->filter('done', '=', false); + // [END property_filter] + return $query; +} + +/** + * Create a query with a composite filter. + * + * @param DatastoreClient $datastore + * @return Query + */ +function composite_filter(DatastoreClient $datastore) +{ + // [START composite_filter] + $query = $datastore->query() + ->kind('Task') + ->filter('done', '=', false) + ->filter('priority', '=', 4); + // [END composite_filter] + return $query; +} + +/** + * Create a query with a key filter. + * + * @param DatastoreClient $datastore + * @return Query + */ +function key_filter(DatastoreClient $datastore) +{ + // [START key_filter] + $query = $datastore->query() + ->kind('Task') + ->filter('__key__', '>', $datastore->key('Task', 'someTask')); + // [END key_filter] + return $query; +} + +/** + * Create a query with ascending sort. + * + * @param DatastoreClient $datastore + * @return Query + */ +function ascending_sort(DatastoreClient $datastore) +{ + // [START ascending_sort] + $query = $datastore->query() + ->kind('Task') + ->order('created'); + // [END ascending_sort] + return $query; +} + +/** + * Create a query with descending sort. + * + * @param DatastoreClient $datastore + * @return Query + */ +function descending_sort(DatastoreClient $datastore) +{ + // [START descending_sort] + $query = $datastore->query() + ->kind('Task') + ->order('create', Query::ORDER_DESCENDING); + // [END descending_sort] + return $query; +} + +/** + * Create a query sorting with multiple properties. + * + * @param DatastoreClient $datastore + * @return Query + */ +function multi_sort(DatastoreClient $datastore) +{ + // [START multi_sort] + $query = $datastore->query() + ->kind('Task') + ->order('priority', Query::ORDER_DESCENDING) + ->order('created'); + // [END multi_sort] + return $query; +} + +/** + * Create an ancestor query. + * + * @param DatastoreClient $datastore + * @return Query + */ +function ancestor_query(DatastoreClient $datastore) +{ + // TODO: Move to `hasAncestor` once it's available. + // https://github.com/GoogleCloudPlatform/google-cloud-php/issues/186 + // [START ancestor_query] + $ancestorKey = $datastore->key('TaskList', 'default'); + $query = $datastore->query() + ->kind('Task') + ->filter('__key__', Query::OP_HAS_ANCESTOR, $ancestorKey); + // [END ancestor_query] + return $query; +} + +/** + * Create a kindless query. + * + * @param DatastoreClient $datastore + * @param Key $lastSeenKey + * @return Query + */ +function kindless_query(DatastoreClient $datastore, Key $lastSeenKey) +{ + // [START kindless_query] + $query = $datastore->query() + ->filter('__key__', '>', $lastSeenKey); + // [END kindless_query] + return $query; +} + +/** + * Create a keys-only query. + * + * @param DatastoreClient $datastore + * @return Query + */ +function keys_only_query(DatastoreClient $datastore) +{ + // TODO: Move to `keysOnly()` once it's available. + // https://github.com/GoogleCloudPlatform/google-cloud-php/issues/187 + // [START keys_only_query] + $query = $datastore->query() + ->projection('__key__') + ->limit(1); + // [END keys_only_query] + return $query; +} + +/** + * Create a projection query. + * + * @param DatastoreClient $datastore + * @return Query + */ +function projection_query(DatastoreClient $datastore) +{ + // [START projection_query] + $query = $datastore->query() + ->kind('Task') + ->projection(['priority', 'percent_complete']); + // [END projection_query] + return $query; +} + +/** + * Run the given projection query and collect the projected properties. + * + * @param DatastoreClient $datastore + * @param Query $query + * @return array + */ +function run_projection_query(DatastoreClient $datastore, Query $query) +{ + // [START run_projection_query] + $priorities = array(); + $percentCompletes = array(); + $result = $datastore->runQuery($query); + /* @var Google\Cloud\Datastore\Entity $task */ + foreach ($result as $task) { + $priorities[] = $task['priority']; + $percentCompletes[] = $task['percent_complete']; + } + // [END run_projection_query] + return array($priorities, $percentCompletes); +} + +/** + * Create a query with distinctOn. + * + * @param DatastoreClient $datastore + * @return Query + */ +function distinct_on(DatastoreClient $datastore) +{ + // [START distinct_on] + $query = $datastore->query() + ->kind('Task') + ->order('category') + ->order('priority') + ->projection(['category', 'priority']) + ->distinctOn('category'); + // [END distinct_on] + return $query; +} + +/** + * Create a query with inequality filters. + * + * @param DatastoreClient $datastore + * @return Query + */ +function array_value_inequality_range(DatastoreClient $datastore) +{ + // [START array_value_inequality_range] + $query = $datastore->query() + ->kind('Task') + ->filter('tag', '>', 'learn') + ->filter('tag', '<', 'math'); + // [END array_value_inequality_range] + return $query; +} + +/** + * Create a query with equality filters. + * + * @param DatastoreClient $datastore + * @return Query + */ +function array_value_equality(DatastoreClient $datastore) +{ + // [START array_value_equality] + $query = $datastore->query() + ->kind('Task') + ->filter('tag', '=', 'fun') + ->filter('tag', '=', 'programming'); + // [END array_value_equality] + return $query; +} + +/** + * Create a query with a limit. + * + * @param DatastoreClient $datastore + * @return Query + */ +function limit(DatastoreClient $datastore) +{ + // [START limit] + $query = $datastore->query() + ->kind('Task') + ->limit(5); + // [END limit] + return $query; +} + +/** + * Fetch a query cursor. + * + * @param DatastoreClient $datastore + * @param string $pageSize + * @param string $pageCursor + * @return string $nextPageCursor + */ +function cursor_paging(DatastoreClient $datastore, $pageSize, $pageCursor = '') +{ + // [START cursor_paging] + $query = $datastore->query() + ->kind('Task') + ->limit($pageSize) + ->start($pageCursor); + $result = $datastore->runQuery($query); + $nextPageCursor = ''; + /* ignoreOnTheDocs */ $entities = []; + /* @var Google\Cloud\Datastore\Entity $entity */ + foreach ($result as $entity) { + $nextPageCursor = $entity->cursor(); + /* ignoreOnTheDocs */ $entities[] = $entity; + } + // [END cursor_paging] + return array( + 'nextPageCursor' => $nextPageCursor, + 'entities' => $entities + ); +} + +/** + * Create a query with inequality range filters on the same property. + * + * @param DatastoreClient $datastore + * @return Query + */ +function inequality_range(DatastoreClient $datastore) +{ + // [START inequality_range] + $query = $datastore->query() + ->kind('Task') + ->filter('created', '>', new \DateTime('1990-01-01T00:00:00z')) + ->filter('created', '<', new \DateTime('2000-12-31T23:59:59z')); + // [END inequality_range] + return $query; +} + +/** + * Create an invalid query with inequality filters on multiple properties. + * + * @param DatastoreClient $datastore + * @return Query + */ +function inequality_invalid(DatastoreClient $datastore) +{ + // [START inequality_invalid] + $query = $datastore->query() + ->kind('Task') + ->filter('priority', '>', 3) + ->filter('created', '>', new \DateTime('1990-01-01T00:00:00z')); + // [END inequality_invalid] + return $query; +} + +/** + * Create a query with equality filters and inequality range filters on a + * single property. + * + * @param DatastoreClient $datastore + * @return Query + */ +function equal_and_inequality_range(DatastoreClient $datastore) +{ + // [START equal_and_inequality_range] + $query = $datastore->query() + ->kind('Task') + ->filter('priority', '=', 4) + ->filter('done', '=', false) + ->filter('created', '>', new \DateTime('1990-01-01T00:00:00z')) + ->filter('created', '<', new \DateTime('2000-12-31T23:59:59z')); + // [END equal_and_inequality_range] + return $query; +} + +/** + * Create a query with an inequality filter and multiple sort orders. + * + * @param DatastoreClient $datastore + * @return Query + */ +function inequality_sort(DatastoreClient $datastore) +{ + // [START inequality_sort] + $query = $datastore->query() + ->kind('Task') + ->filter('priority', '>', 3) + ->order('priority') + ->order('created'); + // [END inequality_sort] + return $query; +} + +/** + * Create an invalid query with an inequality filter and a wrong sort order. + * + * @param DatastoreClient $datastore + * @return Query + */ +function inequality_sort_invalid_not_same(DatastoreClient $datastore) +{ + // [START inequality_sort_invalid_not_same] + $query = $datastore->query() + ->kind('Task') + ->filter('priority', '>', 3) + ->order('created'); + // [END inequality_sort_invalid_not_same] + return $query; +} + +/** + * Create an invalid query with an inequality filter and a wrong sort order. + * + * @param DatastoreClient $datastore + * @return Query + */ +function inequality_sort_invalid_not_first(DatastoreClient $datastore) +{ + // [START inequality_sort_invalid_not_first] + $query = $datastore->query() + ->kind('Task') + ->filter('priority', '>', 3) + ->order('created') + ->order('priority'); + // [END inequality_sort_invalid_not_first] + return $query; +} diff --git a/datastore/api/test/ConceptsTest.php b/datastore/api/test/ConceptsTest.php index 0f26427247..d7fab19c18 100644 --- a/datastore/api/test/ConceptsTest.php +++ b/datastore/api/test/ConceptsTest.php @@ -17,10 +17,30 @@ namespace Google\Cloud\Samples\Datastore; +use Google; use Google\Cloud\Datastore\DatastoreClient; +use Google\Cloud\Datastore\Entity; +use Google\Cloud\Datastore\Query\Query; +use Google\Cloud\TestUtils\EventuallyConsistentTestTrait; + +/** + * @param int $length + * @return string + */ +function generateRandomString($length = 10) +{ + $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + $ret = ''; + for ($i = 0; $i < $length; $i++) { + $ret .= $chars[rand(0, strlen($chars) - 1)]; + } + return $ret; +} class ConceptsTest extends \PHPUnit_Framework_TestCase { + use EventuallyConsistentTestTrait; + /* @var $hasCredentials boolean */ protected static $hasCredentials; @@ -36,8 +56,9 @@ public static function setUpBeforeClass() self::$hasCredentials = $path && file_exists($path) && filesize($path) > 0; self::$datastore = new DatastoreClient( - array('namespaceId' => uniqid()) + array('namespaceId' => generateRandomString()) ); + self::$keys[] = self::$datastore->key('Task', 'sampleTask'); } public function setUp() @@ -48,44 +69,46 @@ public function setUp() 'No application credentials were found, also not using the ' . 'datastore emulator'); } + self::$keys = []; } - public function testCreateEntity() + public function testBasicEntity() { - $task = create_entity(self::$datastore); + $task = basic_entity(self::$datastore); $this->assertEquals('Personal', $task['category']); $this->assertEquals(false, $task['done']); $this->assertEquals(4, $task['priority']); $this->assertEquals('Learn Cloud Datastore', $task['description']); } - public function testUpsertEntity() + public function testUpsert() { - $task = upsert_entity(self::$datastore); + self::$keys[] = self::$datastore->key('Task', 'sampleTask'); + $task = upsert(self::$datastore); $task = self::$datastore->lookup($task->key()); $this->assertEquals('Personal', $task['category']); $this->assertEquals(false, $task['done']); $this->assertEquals(4, $task['priority']); $this->assertEquals('Learn Cloud Datastore', $task['description']); $this->assertEquals('sampleTask', $task->key()->pathEnd()['name']); - self::$keys[] = $task->key(); } - public function testInsertEntity() + public function testInsert() { - $task = insert_entity(self::$datastore); + $task = insert(self::$datastore); + self::$keys[] = $task->key(); $task = self::$datastore->lookup($task->key()); $this->assertEquals('Personal', $task['category']); $this->assertEquals(false, $task['done']); $this->assertEquals(4, $task['priority']); $this->assertEquals('Learn Cloud Datastore', $task['description']); $this->assertArrayHasKey('id', $task->key()->pathEnd()); - self::$keys[] = $task->key(); } public function testLookup() { - upsert_entity(self::$datastore); + self::$keys[] = self::$datastore->key('Task', 'sampleTask'); + upsert(self::$datastore); $task = lookup(self::$datastore); $this->assertEquals('Personal', $task['category']); $this->assertEquals(false, $task['done']); @@ -96,8 +119,9 @@ public function testLookup() public function testUpdate() { - upsert_entity(self::$datastore); - update_entity(self::$datastore); + self::$keys[] = self::$datastore->key('Task', 'sampleTask'); + upsert(self::$datastore); + update(self::$datastore); $task = lookup(self::$datastore); $this->assertEquals('Personal', $task['category']); $this->assertEquals(false, $task['done']); @@ -108,21 +132,22 @@ public function testUpdate() public function testDelete() { - $taskKey = self::$datastore->key('Task', uniqid()); + $taskKey = self::$datastore->key('Task', generateRandomString()); + self::$keys[] = $taskKey; $task = self::$datastore->entity($taskKey); $task['category'] = 'Personal'; $task['done'] = false; $task['priority'] = 4; $task['description'] = 'Learn Cloud Datastore'; - delete_entity(self::$datastore, $taskKey); + delete(self::$datastore, $taskKey); $task = self::$datastore->lookup($taskKey); $this->assertNull($task); } - public function testUpsertMulti() + public function testBatchUpsert() { - $path1 = uniqid(); - $path2 = uniqid(); + $path1 = generateRandomString(); + $path2 = generateRandomString(); $key1 = self::$datastore->key('Task', $path1); $key2 = self::$datastore->key('Task', $path2); $task1 = self::$datastore->entity($key1); @@ -138,7 +163,7 @@ public function testUpsertMulti() self::$keys[] = $key1; self::$keys[] = $key2; - upsert_multi(self::$datastore, [$task1, $task2]); + batch_upsert(self::$datastore, [$task1, $task2]); $task1 = self::$datastore->lookup($key1); $task2 = self::$datastore->lookup($key2); @@ -155,10 +180,10 @@ public function testUpsertMulti() $this->assertEquals($path2, $task2->key()->pathEnd()['name']); } - public function testLookupMulti() + public function testBatchLookup() { - $path1 = uniqid(); - $path2 = uniqid(); + $path1 = generateRandomString(); + $path2 = generateRandomString(); $key1 = self::$datastore->key('Task', $path1); $key2 = self::$datastore->key('Task', $path2); $task1 = self::$datastore->entity($key1); @@ -174,13 +199,14 @@ public function testLookupMulti() self::$keys[] = $key1; self::$keys[] = $key2; - upsert_multi(self::$datastore, [$task1, $task2]); - $result = lookup_multi(self::$datastore, [$key1, $key2]); + batch_upsert(self::$datastore, [$task1, $task2]); + $result = batch_lookup(self::$datastore, [$key1, $key2]); $this->assertArrayHasKey('found', $result); $tasks = $result['found']; $this->assertEquals(2, count($tasks)); + /* @var Entity $task */ foreach ($tasks as $task) { if ($task->key()->pathEnd()['name'] === $path1) { $task1 = $task; @@ -208,10 +234,10 @@ public function testLookupMulti() $this->assertEquals($path2, $task2->key()->pathEnd()['name']); } - public function testDeleteMulti() + public function testBatchDelete() { - $path1 = uniqid(); - $path2 = uniqid(); + $path1 = generateRandomString(); + $path2 = generateRandomString(); $key1 = self::$datastore->key('Task', $path1); $key2 = self::$datastore->key('Task', $path2); $task1 = self::$datastore->entity($key1); @@ -227,40 +253,40 @@ public function testDeleteMulti() self::$keys[] = $key1; self::$keys[] = $key2; - upsert_multi(self::$datastore, [$task1, $task2]); - delete_multi(self::$datastore, [$key1, $key2]); + batch_upsert(self::$datastore, [$task1, $task2]); + batch_delete(self::$datastore, [$key1, $key2]); - $result = lookup_multi(self::$datastore, [$key1, $key2]); + $result = batch_lookup(self::$datastore, [$key1, $key2]); $this->assertArrayNotHasKey('found', $result); } - public function testCreateCompleteKey() + public function testNamedKey() { - $key = create_complete_key(self::$datastore); + $key = named_key(self::$datastore); $this->assertEquals('Task', $key->pathEnd()['kind']); $this->assertEquals('sampleTask', $key->pathEnd()['name']); } - public function testCreateIncompleteKey() + public function testIncompleteKey() { - $key = create_incomplete_key(self::$datastore); + $key = incomplete_key(self::$datastore); $this->assertEquals('Task', $key->pathEnd()['kind']); $this->assertArrayNotHasKey('name', $key->pathEnd()); $this->assertArrayNotHasKey('id', $key->pathEnd()); } - public function testCreateKeyWithParent() + public function testKeyWithParent() { - $key = create_key_with_parent(self::$datastore); + $key = key_with_parent(self::$datastore); $this->assertEquals('Task', $key->path()[1]['kind']); $this->assertEquals('sampleTask', $key->path()[1]['name']); $this->assertEquals('TaskList', $key->path()[0]['kind']); $this->assertEquals('default', $key->path()[0]['name']); } - public function testCreateKeyWithMultiLevelParent() + public function testKeyWithMultilevelParent() { - $key = create_key_with_multi_level_parent(self::$datastore); + $key = key_with_multilevel_parent(self::$datastore); $this->assertEquals('Task', $key->path()[2]['kind']); $this->assertEquals('sampleTask', $key->path()[2]['name']); $this->assertEquals('TaskList', $key->path()[1]['kind']); @@ -269,11 +295,11 @@ public function testCreateKeyWithMultiLevelParent() $this->assertEquals('alice', $key->path()[0]['name']); } - public function testCreateEntityWithOption() + public function testProperties() { - $key = self::$datastore->key('Task', uniqid()); + $key = self::$datastore->key('Task', generateRandomString()); self::$keys[] = $key; - $task = create_entity_with_option(self::$datastore, $key); + $task = properties(self::$datastore, $key); $now = new \DateTime(); self::$datastore->upsert($task); $task = self::$datastore->lookup($key); @@ -283,22 +309,423 @@ public function testCreateEntityWithOption() $this->assertGreaterThanOrEqual($task['created'], new \DateTime()); $this->assertEquals(false, $task['done']); $this->assertEquals(10.0, $task['percent_complete']); + $this->assertEquals(4, $task['priority']); $this->assertEquals('Learn Cloud Datastore', $task['description']); } - public function testCreateEntityWithArrayProperty() + public function testArrayValue() { - $key = self::$datastore->key('Task', uniqid()); + $key = self::$datastore->key('Task', generateRandomString()); self::$keys[] = $key; - $task = create_entity_with_array_property(self::$datastore, $key); + $task = array_value(self::$datastore, $key); self::$datastore->upsert($task); $task = self::$datastore->lookup($key); $this->assertEquals(['fun', 'programming'], $task['tags']); $this->assertEquals(['alice', 'bob'], $task['collaborators']); + + $this->runEventuallyConsistentTest(function () use ($key) { + $query = self::$datastore->query() + ->kind('Task') + ->projection(['tags', 'collaborators']) + ->filter('collaborators', '<', 'charlie'); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + $num = 0; + /* @var Entity $e */ + foreach ($result as $e) { + $this->assertEquals($e->key()->path(), $key->path()); + $this->assertTrue( + ($e['tags'] == 'fun') + || + ($e['tags'] == 'programming') + ); + $this->assertTrue( + ($e['collaborators'] == 'alice') + || + ($e['collaborators'] == 'bob') + ); + $num += 1; + } + // The following 4 combinations should be in the result: + // tags = 'fun', collaborators = 'alice' + // tags = 'fun', collaborators = 'bob' + // tags = 'programming', collaborators = 'alice' + // tags = 'programming', collaborators = 'bob' + self::assertEquals(4, $num); + }); + } + + public function testBasicQuery() + { + $query = basic_query(self::$datastore); + $this->assertInstanceOf(Query::class, $query); + } + + public function testRunQuery() + { + $query = basic_query(self::$datastore); + $result = run_query(self::$datastore, $query); + $this->assertInstanceOf(\Generator::class, $result); + } + + public function testPropertyFilter() + { + $query = property_filter(self::$datastore); + $this->assertInstanceOf(Query::class, $query); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + } + + public function testCompositeFilter() + { + $query = composite_filter(self::$datastore); + $this->assertInstanceOf(Query::class, $query); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); } - public static function tearDownAfterClass() + public function testKeyFilter() { - self::$datastore->deleteBatch(self::$keys); + $query = key_filter(self::$datastore); + $this->assertInstanceOf(Query::class, $query); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + } + + public function testAscendingSort() + { + $query = ascending_sort(self::$datastore); + $this->assertInstanceOf(Query::class, $query); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + } + + public function testDescendingSort() + { + $query = descending_sort(self::$datastore); + $this->assertInstanceOf(Query::class, $query); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + } + + public function testMultiSort() + { + $query = multi_sort(self::$datastore); + $this->assertInstanceOf(Query::class, $query); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + } + + public function testAncestorQuery() + { + $key = self::$datastore->key('Task', generateRandomString()) + ->ancestor('TaskList', 'default'); + $entity = self::$datastore->entity($key); + $uniqueValue = generateRandomString(); + $entity['prop'] = $uniqueValue; + self::$keys[] = $key; + self::$datastore->upsert($entity); + $query = ancestor_query(self::$datastore); + $this->assertInstanceOf(Query::class, $query); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + $found = false; + foreach ($result as $e) { + $found = true; + self::assertEquals($uniqueValue, $e['prop']); + } + self::assertTrue($found); + } + + public function testKindlessQuery() + { + $lastSeenKey = self::$datastore->key('Task', 'lastSeen'); + $query = kindless_query(self::$datastore, $lastSeenKey); + $this->assertInstanceOf(Query::class, $query); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + } + + public function testKeysOnlyQuery() + { + $key = self::$datastore->key('Task', generateRandomString()); + $entity = self::$datastore->entity($key); + $entity['prop'] = 'value'; + self::$keys[] = $key; + self::$datastore->upsert($entity); + $this->runEventuallyConsistentTest(function () { + $query = keys_only_query(self::$datastore); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + $found = false; + foreach ($result as $e) { + $this->assertNull($e['prop']); + $found = true; + } + self::assertTrue($found); + }); + } + + public function testProjectionQuery() + { + $key = self::$datastore->key('Task', generateRandomString()); + $entity = self::$datastore->entity($key); + $entity['prop'] = 'value'; + $entity['priority'] = 4; + $entity['percent_complete'] = 50; + self::$keys[] = $key; + self::$datastore->upsert($entity); + $this->runEventuallyConsistentTest(function () { + $query = projection_query(self::$datastore); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + $found = false; + foreach ($result as $e) { + $this->assertEquals(4, $e['priority']); + $this->assertEquals(50, $e['percent_complete']); + $this->assertNull($e['prop']); + $found = true; + } + self::assertTrue($found); + }); + } + + public function testRunProjectionQuery() + { + $key = self::$datastore->key('Task', generateRandomString()); + $entity = self::$datastore->entity($key); + $entity['prop'] = 'value'; + $entity['priority'] = 4; + $entity['percent_complete'] = 50; + self::$keys[] = $key; + self::$datastore->upsert($entity); + $this->runEventuallyConsistentTest(function () { + $query = projection_query(self::$datastore); + $result = run_projection_query(self::$datastore, $query); + $this->assertEquals(2, count($result)); + $this->assertEquals([4], $result[0]); + $this->assertEquals([50], $result[1]); + }); + } + + public function testDistinctOn() + { + $key1 = self::$datastore->key('Task', generateRandomString()); + $key2 = self::$datastore->key('Task', generateRandomString()); + $entity1 = self::$datastore->entity($key1); + $entity2 = self::$datastore->entity($key2); + $entity1['prop'] = 'value'; + $entity1['priority'] = 4; + $entity1['category'] = 'work'; + $entity2['priority'] = 5; + $entity2['category'] = 'work'; + self::$keys[] = $key1; + self::$keys[] = $key2; + self::$datastore->upsert($entity1); + self::$datastore->upsert($entity2); + $this->runEventuallyConsistentTest(function () use ($key1) { + $query = distinct_on(self::$datastore); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + $num = 0; + /* @var Entity $e */ + foreach ($result as $e) { + $this->assertEquals(4, $e['priority']); + $this->assertEquals('work', $e['category']); + $this->assertNull($e['prop']); + $this->assertEquals($e->key()->path(), $key1->path()); + $num += 1; + } + self::assertEquals(1, $num); + }); + } + + public function testArrayValueFilters() + { + $key = self::$datastore->key('Task', generateRandomString()); + $entity = self::$datastore->entity($key); + $entity['tag'] = ['fun', 'programming']; + self::$keys[] = $key; + self::$datastore->upsert($entity); + // This is a test for non-matching query for eventually consistent + // query. This is hard, here we only sleep 5 seconds. + sleep(5); + $query = array_value_inequality_range(self::$datastore); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + /* @var Entity $e */ + foreach ($result as $e) { + $this->fail( + sprintf( + 'Should not match the entity. Here is the tag: %s', + var_export($e['tag'], true) + ) + ); + } + $this->runEventuallyConsistentTest(function () use ($key) { + $query = array_value_equality(self::$datastore); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + $num = 0; + /* @var Entity $e */ + foreach ($result as $e) { + $this->assertEquals(['fun', 'programming'], $e['tag']); + $this->assertEquals($e->key()->path(), $key->path()); + $num += 1; + } + self::assertEquals(1, $num); + }); + } + + public function testLimit() + { + $entities = []; + for ($i = 0; $i < 10; $i++) { + $key = self::$datastore->key('Task', generateRandomString()); + self::$keys[] = $key; + $entities[] = self::$datastore->entity($key); + } + self::$datastore->upsertBatch($entities); + $this->runEventuallyConsistentTest(function () { + $query = limit(self::$datastore); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + $num = 0; + /* @var Entity $e */ + foreach ($result as $e) { + $this->assertEquals('Task', $e->key()->path()[0]['kind']); + $num += 1; + } + self::assertEquals(5, $num); + }); + } + + public function testCursorPaging() + { + $entities = []; + for ($i = 0; $i < 5; $i++) { + $key = self::$datastore->key('Task', generateRandomString()); + self::$keys[] = $key; + $entities[] = self::$datastore->entity($key); + } + self::$datastore->upsertBatch($entities); + $this->runEventuallyConsistentTest(function () { + $res = cursor_paging(self::$datastore, 3); + $this->assertEquals(3, count($res['entities'])); + $res = cursor_paging(self::$datastore, 3, $res['nextPageCursor']); + $this->assertEquals(2, count($res['entities'])); + }); + } + + public function testInequalityRange() + { + $query = inequality_range(self::$datastore); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + /* @var Google\Cloud\Datastore\Entity $e */ + foreach ($result as $e) { + $this->fail( + sprintf( + 'Should not match the entity with a key: %s', + var_export($e->key()->path(), true) + ) + ); + } + } + + /** + * @expectedException Google\Cloud\Exception\BadRequestException + */ + public function testInequalityInvalid() + { + $query = inequality_invalid(self::$datastore); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + /* @var Google\Cloud\Datastore\Entity $e */ + foreach ($result as $e) { + $this->fail( + sprintf( + 'Should not match the entity with a key: %s', + var_export($e->key()->path(), true) + ) + ); + } + } + + public function testEqualAndInequalityRange() + { + $query = equal_and_inequality_range(self::$datastore); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + /* @var Google\Cloud\Datastore\Entity $e */ + foreach ($result as $e) { + $this->fail( + sprintf( + 'Should not match the entity with a key: %s', + var_export($e->key()->path(), true) + ) + ); + } + } + + public function testInequalitySort() + { + $query = inequality_sort(self::$datastore); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + /* @var Google\Cloud\Datastore\Entity $e */ + foreach ($result as $e) { + $this->fail( + sprintf( + 'Should not match the entity with a key: %s', + var_export($e->key()->path(), true) + ) + ); + } + } + + /** + * @expectedException Google\Cloud\Exception\BadRequestException + */ + public function testInequalitySortInvalidNotSame() + { + $query = inequality_sort_invalid_not_same(self::$datastore); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + /* @var Google\Cloud\Datastore\Entity $e */ + foreach ($result as $e) { + $this->fail( + sprintf( + 'Should not match the entity with a key: %s', + var_export($e->key()->path(), true) + ) + ); + } + } + + /** + * @expectedException Google\Cloud\Exception\BadRequestException + */ + public function testInequalitySortInvalidNotFirst() + { + $query = inequality_sort_invalid_not_first(self::$datastore); + $result = self::$datastore->runQuery($query); + $this->assertInstanceOf(\Generator::class, $result); + /* @var Google\Cloud\Datastore\Entity $e */ + foreach ($result as $e) { + $this->fail( + sprintf( + 'Should not match the entity with a key: %s', + var_export($e->key()->path(), true) + ) + ); + } + } + + public function tearDown() + { + if (! empty(self::$keys)) { + self::$datastore->deleteBatch(self::$keys); + } } }