From 43fa63e1dae81e7fd52140222a22c08eaf16f9de Mon Sep 17 00:00:00 2001 From: Ajay Kannan Date: Tue, 9 Feb 2016 10:34:03 -0800 Subject: [PATCH 1/5] Initial commit for datastore concepts snippets and getting started sample app. --- datastore/README.md | 14 + datastore/pom.xml | 65 + .../google/datastore/snippets/Concepts.java | 1058 +++++++++++++++++ .../google/datastore/snippets/TaskList.java | 232 ++++ pom.xml | 1 + 5 files changed, 1370 insertions(+) create mode 100644 datastore/README.md create mode 100644 datastore/pom.xml create mode 100644 datastore/src/main/java/com/google/datastore/snippets/Concepts.java create mode 100644 datastore/src/main/java/com/google/datastore/snippets/TaskList.java diff --git a/datastore/README.md b/datastore/README.md new file mode 100644 index 00000000000..7d888580ae0 --- /dev/null +++ b/datastore/README.md @@ -0,0 +1,14 @@ +## Datastore Samples + +This directory contains sample code used in Google Cloud Datastore documentation. Included here is a sample command line application, `TaskList`, that interacts with Datastore to manage a to-do list. + +## Run the `TaskList` sample application. + +1. Ensure that you have: + * Created a Google Developers Console project with the Datastore API enabled. Follow [these instructions](https://cloud.google.com/docs/authentication#preparation) to get your project set up. + * Installed the Google Cloud SDK and run the following commands in command line: `gcloud auth login` and `gcloud config set project [YOUR PROJECT ID]`. + * Installed [Maven](https://maven.apache.org/) and Java 7 (or above). + +2. Compile the program by typing `mvn clean compile` in command line. + +3. Run the program by typing `mvn exec:java` in command line. diff --git a/datastore/pom.xml b/datastore/pom.xml new file mode 100644 index 00000000000..9febec996cf --- /dev/null +++ b/datastore/pom.xml @@ -0,0 +1,65 @@ + + + 4.0.0 + com.google.datastore.snippets + datastore-snippets + 1.0 + jar + Google Cloud Datastore Snippets + + Example snippets for Datastore concepts and getting started documentation. + + + + com.google.gcloud + gcloud-java-datastore + 0.1.3 + + + junit + junit + 4.12 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + + 1.7 + 1.7 + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.4.0 + + com.google.datastore.snippets.TaskList + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.17 + + ../google-checks.xml + true + true + true + + + check + + + + + diff --git a/datastore/src/main/java/com/google/datastore/snippets/Concepts.java b/datastore/src/main/java/com/google/datastore/snippets/Concepts.java new file mode 100644 index 00000000000..ae38b0070aa --- /dev/null +++ b/datastore/src/main/java/com/google/datastore/snippets/Concepts.java @@ -0,0 +1,1058 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.datastore.snippets; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterators; +import com.google.common.collect.Multimap; +import com.google.gcloud.datastore.Batch; +import com.google.gcloud.datastore.Cursor; +import com.google.gcloud.datastore.Datastore; +import com.google.gcloud.datastore.DatastoreException; +import com.google.gcloud.datastore.DatastoreOptions; +import com.google.gcloud.datastore.DateTime; +import com.google.gcloud.datastore.Entity; +import com.google.gcloud.datastore.EntityQuery; +import com.google.gcloud.datastore.FullEntity; +import com.google.gcloud.datastore.IncompleteKey; +import com.google.gcloud.datastore.Key; +import com.google.gcloud.datastore.KeyFactory; +import com.google.gcloud.datastore.PathElement; +import com.google.gcloud.datastore.ProjectionEntity; +import com.google.gcloud.datastore.Query; +import com.google.gcloud.datastore.Query.ResultType; +import com.google.gcloud.datastore.QueryResults; +import com.google.gcloud.datastore.StringValue; +import com.google.gcloud.datastore.StructuredQuery; +import com.google.gcloud.datastore.StructuredQuery.CompositeFilter; +import com.google.gcloud.datastore.StructuredQuery.OrderBy; +import com.google.gcloud.datastore.StructuredQuery.PropertyFilter; +import com.google.gcloud.datastore.Transaction; +import com.google.gcloud.datastore.Value; +import com.google.gcloud.datastore.testing.LocalGcdHelper; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.TimeZone; + +/** + * Contains Cloud Datastore snippets linked from the Concepts documentation. + * + * @see Key Datastore Concepts + */ +public class Concepts { + + private static final String PROJECT_ID = LocalGcdHelper.DEFAULT_PROJECT_ID; + private static LocalGcdHelper gcdHelper; + private static final int PORT = LocalGcdHelper.findAvailablePort(LocalGcdHelper.DEFAULT_PORT); + private static final FullEntity TEST_FULL_ENTITY = FullEntity.builder().build(); + + private Datastore datastore; + private KeyFactory keyFactory; + private Key taskKey; + private Entity testEntity; + private DateTime startDate; + private DateTime endDate; + private DateTime includedDate; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + /** + * Starts the local Datastore emulator. + * + * @throws IOException if there are errors starting the local Datastore + * @throws InterruptedException if there are errors starting the local Datastore + */ + @BeforeClass + public static void beforeClass() throws IOException, InterruptedException { + if (!LocalGcdHelper.isActive(PROJECT_ID, PORT)) { + gcdHelper = LocalGcdHelper.start(PROJECT_ID, PORT); + } + } + + /** + * Initializes Datastore and cleans out any residual values. Also initializes global variables + * used for testing. + */ + @Before + public void setUp() { + datastore = DatastoreOptions.builder() + .projectId(PROJECT_ID) + .namespace("ghijklmnop") // for namespace metadata query + .host("http://localhost:" + PORT) + .build() + .service(); + StructuredQuery query = Query.keyQueryBuilder().build(); + QueryResults result = datastore.run(query); + datastore.delete(Iterators.toArray(result, Key.class)); + keyFactory = datastore.newKeyFactory().kind("Task"); + taskKey = keyFactory.newKey("some-arbitrary-key"); + testEntity = Entity.builder(taskKey, TEST_FULL_ENTITY).build(); + Calendar startDateCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + startDateCalendar.set(1990, 1, 1); + startDate = DateTime.copyFrom(startDateCalendar); + Calendar endDateCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + endDateCalendar.set(2000, 1, 1); + endDate = DateTime.copyFrom(endDateCalendar); + Calendar includedCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + includedCalendar.set(1999, 12, 31); + includedDate = DateTime.copyFrom(includedCalendar); + } + + /** + * Stops the local Datastore emulator. + * + * @throws IOException if there are errors stopping the local Datastore + * @throws InterruptedException if there are errors stopping the local Datastore + */ + @AfterClass + public static void afterClass() throws IOException, InterruptedException { + if (gcdHelper != null) { + gcdHelper.stop(); + } + } + + private void assertValidKey(Key taskKey) { + datastore.put(Entity.builder(taskKey, TEST_FULL_ENTITY).build()); + } + + @Test + public void testIncompleteKey() { + // [START incomplete_key] + KeyFactory keyFactory = datastore.newKeyFactory().kind("Task"); + Key taskKey = datastore.allocateId(keyFactory.newKey()); + // [END incomplete_key] + assertValidKey(taskKey); + } + + @Test + public void testNamedKey() { + // [START named_key] + KeyFactory keyFactory = datastore.newKeyFactory().kind("Task"); + Key taskKey = keyFactory.newKey("sampleTask"); + // [END named_key] + assertValidKey(taskKey); + } + + @Test + public void testKeyWithParent() { + // [START key_with_parent] + KeyFactory keyFactory = + datastore.newKeyFactory().ancestors(PathElement.of("TaskList", "default")).kind("Task"); + Key taskKey = keyFactory.newKey("sampleTask"); + // [END key_with_parent] + assertValidKey(taskKey); + } + + @Test + public void testKeyWithMultilevelParent() { + // [START key_with_multilevel_parent] + KeyFactory keyFactory = datastore.newKeyFactory() + .ancestors(PathElement.of("User", "Alice"), PathElement.of("TaskList", "default")) + .kind("Task"); + Key taskKey = keyFactory.newKey("sampleTask"); + // [END key_with_multilevel_parent] + assertValidKey(taskKey); + } + + private void assertValidEntity(Entity original) { + datastore.put(original); + assertEquals(original, datastore.get(original.key())); + } + + @Test + public void testEntityWithParent() { + // [START entity_with_parent] + KeyFactory keyFactory = + datastore.newKeyFactory().ancestors(PathElement.of("TaskList", "default")).kind("Task"); + Entity task = Entity.builder(keyFactory.newKey("sampleTask")) + .set("type", "Personal") + .set("done", false) + .set("priority", 4) + .set("description", "Learn Cloud Datastore") + .build(); + // [END entity_with_parent] + assertValidEntity(task); + } + + @Test + public void testProperties() { + // [START properties] + Entity task = Entity.builder(taskKey) + .set("type", "Personal") + .set("created", DateTime.now()) + .set("done", false) + .set("priority", 4) + .set("percent_complete", 10.0) + .set("description", "Learn Cloud Datastore") + .build(); + // [END properties] + assertValidEntity(task); + } + + @Test + public void testArrayValue() { + // [START array_value] + Entity task = Entity.builder(taskKey) + .set("tags", StringValue.of("fun"), StringValue.of("programming")) + .set("collaborators", StringValue.of("alice"), StringValue.of("bob")) + .build(); + // [END array_value] + assertValidEntity(task); + } + + @Test + public void testBasicEntity() { + // [START basic_entity] + Entity task = Entity.builder(taskKey) + .set("type", "Personal") + .set("done", false) + .set("priority", 4) + .set("description", "Learn Cloud Datastore") + .build(); + // [END basic_entity] + assertValidEntity(task); + } + + @Test + public void testUpsert() { + // [START upsert] + Key taskKey = datastore.allocateId(keyFactory.newKey()); + Entity task = Entity.builder(taskKey).build(); + datastore.put(task); + // [END upsert] + assertEquals(task, datastore.get(task.key())); + } + + @Test + public void testInsert() { + // [START insert] + Entity task = Entity.builder(taskKey).build(); + Key taskKey = datastore.add(task).key(); + // [END insert] + assertEquals(task, datastore.get(taskKey)); + } + + @Test + public void testLookup() { + datastore.put(testEntity); + // [START lookup] + Entity task = datastore.get(taskKey); + // [END lookup] + assertEquals(testEntity, task); + } + + @Test + public void testUpdate() { + datastore.put(testEntity); + // [START update] + Entity task = Entity.builder(datastore.get(taskKey)).set("priority", 5).build(); + datastore.update(task); + // [END update] + assertEquals(task, datastore.get(taskKey)); + } + + @Test + public void testDelete() { + datastore.put(testEntity); + // [START delete] + datastore.delete(taskKey); + // [END delete] + assertNull(datastore.get(taskKey)); + } + + private List setUpBatchTests(Key taskKey1, Key taskKey2) { + Entity task1 = Entity.builder(taskKey1) + .set("type", "Personal") + .set("done", false) + .set("priority", 4) + .set("description", "Learn Cloud Datastore") + .build(); + Entity task2 = Entity.builder(taskKey2) + .set("type", "Personal") + .set("done", false) + .set("priority", 5) + .set("description", "Integrate Cloud Datastore") + .build(); + datastore.put(task1, task2); + return ImmutableList.of(task1, task2); + } + + @Test + public void testBatchUpsert() { + Key taskKey1 = keyFactory.newKey(1); + Key taskKey2 = keyFactory.newKey(2); + Entity task1 = Entity.builder(taskKey1) + .set("type", "Personal") + .set("done", false) + .set("priority", 4) + .set("description", "Learn Cloud Datastore") + .build(); + Entity task2 = Entity.builder(taskKey2) + .set("type", "Personal") + .set("done", false) + .set("priority", 5) + .set("description", "Integrate Cloud Datastore") + .build(); + // [START batch_upsert] + Batch batch = datastore.newBatch(); + batch.put(task1, task2); + batch.submit(); + // [END batch_upsert] + assertEquals(task1, datastore.get(taskKey1)); + assertEquals(task2, datastore.get(taskKey2)); + } + + @Test + public void testBatchLookup() { + Key taskKey1 = keyFactory.newKey(1); + Key taskKey2 = keyFactory.newKey(2); + List expectedTasks = setUpBatchTests(taskKey1, taskKey2); + // [START batch_lookup] + Iterator tasks = datastore.get(taskKey1, taskKey2); + // [END batch_lookup] + assertEquals(expectedTasks.get(0), tasks.next()); + assertEquals(expectedTasks.get(1), tasks.next()); + } + + @Test + public void testBatchDelete() { + Key taskKey1 = keyFactory.newKey(1); + Key taskKey2 = keyFactory.newKey(2); + setUpBatchTests(taskKey1, taskKey2); + // [START batch_delete] + Batch batch = datastore.newBatch(); + batch.delete(taskKey1, taskKey2); + batch.submit(); + // [END batch_delete] + assertNull(datastore.get(taskKey1)); + assertNull(datastore.get(taskKey2)); + } + + private void setUpQueryTests() { + KeyFactory keyFactory = + datastore.newKeyFactory().kind("Task").ancestors(PathElement.of("TaskList", "default")); + datastore.put(Entity.builder(keyFactory.newKey("someTask")) + .set("type", "Personal") + .set("done", false) + .set("completed", false) + .set("priority", 4) + .set("created", includedDate) + .set("percent_complete", 10.0) + .set("description", StringValue.builder("Learn Cloud Datastore").indexed(false).build()) + .set("tag", ImmutableList.of(StringValue.of("fun"), StringValue.of("l"), + StringValue.of("programming"))) + .build()); + } + + private V assertValidQuery(Query query) { + QueryResults results = datastore.run(query); + V result = results.next(); + assertFalse(results.hasNext()); + return result; + } + + private void assertInvalidQuery(Query query) { + thrown.expect(DatastoreException.class); + datastore.run(query); + } + + @Test + public void testBasicQuery() { + setUpQueryTests(); + // [START basic_query] + Query query = Query.entityQueryBuilder() + .kind("Task") + .filter(CompositeFilter.and( + PropertyFilter.eq("done", false), PropertyFilter.ge("priority", 4))) + .orderBy(OrderBy.desc("priority")) + .build(); + // [END basic_query] + assertValidQuery(query); + } + + @Test + public void testRunQuery() { + setUpQueryTests(); + Query query = Query.entityQueryBuilder().kind("Task").build(); + // [START run_query] + QueryResults tasks = datastore.run(query); + // [END run_query] + assertNotNull(tasks.next()); + assertFalse(tasks.hasNext()); + } + + @Test + public void testPropertyFilter() { + setUpQueryTests(); + // [START property_filter] + Query query = + Query.entityQueryBuilder().kind("Task").filter(PropertyFilter.eq("done", false)).build(); + // [END property_filter] + assertValidQuery(query); + } + + @Test + public void testCompositeFilter() { + setUpQueryTests(); + // [START composite_filter] + Query query = Query.entityQueryBuilder() + .kind("Task") + .filter( + CompositeFilter.and(PropertyFilter.eq("done", false), PropertyFilter.eq("priority", 4))) + .build(); + // [END composite_filter] + assertValidQuery(query); + } + + @Test + public void testKeyFilter() { + setUpQueryTests(); + // [START key_filter] + Query query = Query.entityQueryBuilder() + .kind("Task") + .filter(PropertyFilter.gt("__key__", keyFactory.newKey("someTask"))) + .build(); + // [END key_filter] + assertValidQuery(query); + } + + @Test + public void testAscendingSort() { + setUpQueryTests(); + // [START ascending_sort] + Query query = + Query.entityQueryBuilder().kind("Task").orderBy(OrderBy.asc("created")).build(); + // [END ascending_sort] + assertValidQuery(query); + } + + @Test + public void testDescendingSort() { + setUpQueryTests(); + // [START descending_sort] + Query query = + Query.entityQueryBuilder().kind("Task").orderBy(OrderBy.desc("created")).build(); + // [END descending_sort] + assertValidQuery(query); + } + + @Test + public void testMultiSort() { + setUpQueryTests(); + // [START multi_sort] + Query query = Query.entityQueryBuilder() + .kind("Task") + .orderBy(OrderBy.desc("priority"), OrderBy.asc("created")) + .build(); + // [END multi_sort] + assertValidQuery(query); + } + + @Test + public void testKindlessQuery() { + Key lastSeenKey = keyFactory.newKey("a"); + setUpQueryTests(); + // [START kindless_query] + Query query = + Query.entityQueryBuilder().filter(PropertyFilter.gt("__key__", lastSeenKey)).build(); + // [END kindless_query] + assertValidQuery(query); + } + + @Test + public void testAncestorQuery() { + setUpQueryTests(); + // [START ancestor_query] + Query query = Query.entityQueryBuilder() + .kind("Task") + .filter(PropertyFilter.hasAncestor( + datastore.newKeyFactory().kind("TaskList").newKey("default"))) + .build(); + // [END ancestor_query] + assertValidQuery(query); + } + + @Test + public void testProjectionQuery() { + setUpQueryTests(); + // [START projection_query] + Query query = Query.projectionEntityQueryBuilder() + .kind("Task") + .projection( + StructuredQuery.Projection.property("priority"), + StructuredQuery.Projection.property("percent_complete")) + .build(); + // [END projection_query] + assertValidQuery(query); + } + + @Test + public void testRunProjectionQuery() { + setUpQueryTests(); + Query query = Query.projectionEntityQueryBuilder() + .kind("Task") + .projection( + StructuredQuery.Projection.property("priority"), + StructuredQuery.Projection.property("percent_complete")) + .build(); + List priorities = new LinkedList<>(); + List percentCompletes = new LinkedList<>(); + // [START run_query_projection] + QueryResults tasks = datastore.run(query); + while (tasks.hasNext()) { + ProjectionEntity task = tasks.next(); + priorities.add(task.getLong("priority")); + percentCompletes.add(task.getDouble("percent_complete")); + } + // [END run_query_projection] + assertEquals(ImmutableList.of(4L), priorities); + assertEquals(ImmutableList.of(10.0), percentCompletes); + } + + @Test + public void testKeysOnlyQuery() { + setUpQueryTests(); + // [START keys_only_query] + Query query = Query.keyQueryBuilder().kind("Task").build(); + // [END keys_only_query] + assertValidQuery(query); + } + + @Test + public void testRunKeysOnlyQuery() { + setUpQueryTests(); + Query query = Query.keyQueryBuilder().kind("Task").build(); + // [START run_keys_only_query] + QueryResults taskKeys = datastore.run(query); + // [END run_keys_only_query] + assertNotNull(taskKeys.next()); + assertFalse(taskKeys.hasNext()); + } + + @Test + public void testDistinctQuery() { + setUpQueryTests(); + // [START distinct_query] + Query query = Query.projectionEntityQueryBuilder() + .kind("Task") + .projection(StructuredQuery.Projection.property("type"), + StructuredQuery.Projection.property("priority")) + .groupBy("type", "priority") + .orderBy(OrderBy.asc("type"), OrderBy.asc("priority")) + .build(); + // [END distinct_query] + assertValidQuery(query); + } + + @Test + public void testDistinctOnQuery() { + setUpQueryTests(); + // [START distinct_on_query] + Query query = Query.projectionEntityQueryBuilder() + .kind("Task") + .projection(StructuredQuery.Projection.property("type"), + StructuredQuery.Projection.first("priority")) + .groupBy("type") + .orderBy(OrderBy.asc("type"), OrderBy.asc("priority")) + .build(); + // [END distinct_on_query] + assertValidQuery(query); + } + + @Test + public void testArrayValueInequalityRange() { + setUpQueryTests(); + // [START array_value_inequality_range] + Query query = Query.entityQueryBuilder() + .kind("Task") + .filter(CompositeFilter.and( + PropertyFilter.gt("tag", "learn"), PropertyFilter.lt("tag", "math"))) + .build(); + // [END array_value_inequality_range] + QueryResults results = datastore.run(query); + assertFalse(results.hasNext()); + } + + @Test + public void testArrayValueEquality() { + setUpQueryTests(); + // [START array_value_equality] + Query query = Query.entityQueryBuilder() + .kind("Task") + .filter(CompositeFilter.and( + PropertyFilter.eq("tag", "fun"), PropertyFilter.eq("tag", "programming"))) + .build(); + // [END array_value_equality] + assertValidQuery(query); + } + + @Test + public void testInequalityRange() { + setUpQueryTests(); + // [START inequality_range] + Query query = Query.entityQueryBuilder() + .kind("Task") + .filter(CompositeFilter.and( + PropertyFilter.gt("created", startDate), PropertyFilter.lt("created", endDate))) + .build(); + // [END inequality_range] + assertValidQuery(query); + } + + @Test + public void testInequalityInvalid() { + // [START inequality_invalid] + Query query = Query.entityQueryBuilder() + .kind("Task") + .filter(CompositeFilter.and( + PropertyFilter.gt("created", startDate), PropertyFilter.gt("priority", 3))) + .build(); + // [END inequality_invalid] + assertInvalidQuery(query); + } + + @Test + public void testEqualAndInequalityRange() { + setUpQueryTests(); + // [START equal_and_inequality_range] + Query query = Query.entityQueryBuilder() + .kind("Task") + .filter(CompositeFilter.and(PropertyFilter.eq("priority", 4), + PropertyFilter.gt("created", startDate), PropertyFilter.lt("created", endDate))) + .build(); + // [END equal_and_inequality_range] + assertValidQuery(query); + } + + @Test + public void testInequalitySort() { + setUpQueryTests(); + // [START inequality_sort] + Query query = Query.entityQueryBuilder() + .kind("Task") + .filter(PropertyFilter.gt("priority", 3)) + .orderBy(OrderBy.asc("priority"), OrderBy.asc("created")) + .build(); + // [END inequality_sort] + assertValidQuery(query); + } + + @Test + public void testInequalitySortInvalidNotSame() { + // [START inequality_sort_invalid_not_same] + Query query = Query.entityQueryBuilder() + .kind("Task") + .filter(PropertyFilter.gt("priority", 3)) + .orderBy(OrderBy.asc("created")) + .build(); + // [END inequality_sort_invalid_not_same] + assertInvalidQuery(query); + } + + @Test + public void testInequalitySortInvalidNotFirst() { + // [START inequality_sort_invalid_not_first] + Query query = Query.entityQueryBuilder() + .kind("Task") + .filter(PropertyFilter.gt("priority", 3)) + .orderBy(OrderBy.asc("created"), OrderBy.asc("priority")) + .build(); + // [END inequality_sort_invalid_not_first] + assertInvalidQuery(query); + } + + @Test + public void testLimit() { + setUpQueryTests(); + // [START limit] + Query query = Query.entityQueryBuilder().kind("Task").limit(5).build(); + // [END limit] + assertValidQuery(query); + } + + @Test + public void testCursorPaging() { + setUpQueryTests(); + datastore.put(testEntity); + Cursor nextPageCursor = cursorPaging(1, null); + assertNotNull(nextPageCursor); + nextPageCursor = cursorPaging(1, nextPageCursor); + assertNotNull(nextPageCursor); + } + + private Cursor cursorPaging(int pageSize, Cursor pageCursor) { + // [START cursor_paging] + EntityQuery.Builder queryBuilder = Query.entityQueryBuilder().kind("Task").limit(pageSize); + if (pageCursor != null) { + queryBuilder.startCursor(pageCursor); + } + QueryResults tasks = datastore.run(queryBuilder.build()); + Entity task = null; + while (tasks.hasNext()) { + task = tasks.next(); + // do something with the task + } + Cursor nextPageCursor = null; + if (task != null && tasks.cursorAfter() != null) { + nextPageCursor = tasks.cursorAfter(); + // Call nextPageCursor.toUrlSafe() if you want an encoded cursor that can be used as part of a + // URL. + } + // [END cursor_paging] + return nextPageCursor; + } + + @Test + public void testEventualConsistentQuery() { + setUpQueryTests(); + // [START eventual_consistent_query] + Query query = Query.entityQueryBuilder() + .kind("Task") + .filter(PropertyFilter.hasAncestor( + datastore.newKeyFactory().kind("TaskList").newKey("default"))) + .build(); + // [END eventual_consistent_query] + assertValidQuery(query); + } + + @Test + public void testUnindexedPropertyQuery() { + setUpQueryTests(); + // [START unindexed_property_query] + Query query = Query.entityQueryBuilder() + .kind("Task") + .filter(PropertyFilter.eq("description", "A task description")) + .build(); + // [END unindexed_property_query] + QueryResults results = datastore.run(query); + assertFalse(results.hasNext()); + } + + @Test + public void testExplodingProperties() { + // [START exploding_properties] + Entity task = Entity.builder(taskKey) + .set("tags", StringValue.of("fun"), StringValue.of("programming"), StringValue.of("learn")) + .set("collaborators", StringValue.of("alice"), StringValue.of("bob"), + StringValue.of("charlie")) + .set("created", DateTime.now()) + .build(); + // [END exploding_properties] + assertValidEntity(task); + } + + private List setUpTransferTests() { + KeyFactory keyFactory = datastore.newKeyFactory().kind("People"); + Key from = keyFactory.newKey("from"); + Key to = keyFactory.newKey("to"); + datastore.put(Entity.builder(from).set("balance", 100).build()); + datastore.put(Entity.builder(to).set("balance", 0).build()); + return ImmutableList.of(from, to); + } + + private void assertSuccessfulTransfer(Key from, Key to) { + assertEquals(90, datastore.get(from).getLong("balance")); + assertEquals(10, datastore.get(to).getLong("balance")); + } + + @Test + public void testTransactionalUpdate() { + List keys = setUpTransferTests(); + transferFunds(keys.get(0), keys.get(1), 10); + assertSuccessfulTransfer(keys.get(0), keys.get(1)); + } + + private void transferFunds(Key fromKey, Key toKey, long amount) { + // [START transactional_update] + Transaction txn = datastore.newTransaction(); + try { + Entity from = txn.get(fromKey); + Entity updatedFrom = + Entity.builder(from).set("balance", from.getLong("balance") - amount).build(); + Entity to = txn.get(toKey); + Entity updatedTo = Entity.builder(to).set("balance", to.getLong("balance") + amount).build(); + txn.put(updatedFrom, updatedTo); + txn.commit(); + } finally { + if (txn.active()) { + txn.rollback(); + } + } + // [END transactional_update] + } + + @Test + public void testTransactionalRetry() { + List keys = setUpTransferTests(); + Key fromKey = keys.get(0); + Key toKey = keys.get(1); + // [START transactional_retry] + int retries = 5; + while (true) { + try { + transferFunds(fromKey, toKey, 10); + break; + } catch (DatastoreException e) { + if (retries == 0) { + throw e; + } + --retries; + } + } + // [END transactional_retry] + assertSuccessfulTransfer(keys.get(0), keys.get(1)); + } + + @Test + public void testTransactionalGetOrCreate() { + // [START transactional_get_or_create] + Entity task; + Transaction txn = datastore.newTransaction(); + try { + try { + task = txn.get(taskKey); + } catch (DatastoreException e) { + task = Entity.builder(taskKey).build(); + txn.put(task); + txn.commit(); + } + } finally { + if (txn.active()) { + txn.rollback(); + } + } + // [END transactional_get_or_create] + assertEquals(task, datastore.get(taskKey)); + } + + @Test + public void testTransactionalSingleEntityGroupReadOnly() { + setUpQueryTests(); + Key taskListKey = datastore.newKeyFactory().kind("TaskList").newKey("default"); + Entity taskListEntity = Entity.builder(taskListKey).build(); + datastore.put(taskListEntity); + // [START transactional_single_entity_group_read_only] + Entity taskList; + QueryResults tasks; + Transaction txn = datastore.newTransaction(); + try { + taskList = txn.get(taskListKey); + Query query = Query.entityQueryBuilder() + .kind("Task") + .filter(PropertyFilter.hasAncestor(taskListKey)) + .build(); + tasks = txn.run(query); + txn.commit(); + } finally { + if (txn.active()) { + txn.rollback(); + } + } + // [END transactional_single_entity_group_read_only] + assertEquals(taskListEntity, taskList); + assertNotNull(tasks.next()); + assertFalse(tasks.hasNext()); + } + + @Test + public void testNamespaceRunQuery() { + setUpQueryTests(); + // [START namespace_run_query] + KeyFactory keyFactory = datastore.newKeyFactory().kind("__namespace__"); + Key startNamespace = keyFactory.newKey("g"); + Key endNamespace = keyFactory.newKey("h"); + Query query = Query.keyQueryBuilder() + .kind("__namespace__") + .filter(CompositeFilter.and( + PropertyFilter.gt("__key__", startNamespace), + PropertyFilter.lt("__key__", endNamespace))) + .build(); + List namespaces = new ArrayList<>(); + QueryResults results = datastore.run(query); + while (results.hasNext()) { + namespaces.add(results.next().name()); + } + // [END namespace_run_query] + assertEquals(ImmutableList.of("ghijklmnop"), namespaces); + } + + @Test + public void testKindRunQuery() { + setUpQueryTests(); + // [START kind_run_query] + Query query = Query.keyQueryBuilder().kind("__kind__").build(); + List kinds = new ArrayList<>(); + QueryResults results = datastore.run(query); + while (results.hasNext()) { + kinds.add(results.next().name()); + } + // [END kind_run_query] + assertEquals(ImmutableList.of("Task"), kinds); + } + + @Test + public void testPropertyRunQuery() { + setUpQueryTests(); + // [START property_run_query] + Query query = Query.keyQueryBuilder().kind("__property__").build(); + QueryResults results = datastore.run(query); + Multimap propertiesByKind = HashMultimap.create(); + while (results.hasNext()) { + Key property = results.next(); + String kind = property.ancestors().get(property.ancestors().size() - 1).name(); + String propertyName = property.name(); + propertiesByKind.put(kind, propertyName); + } + // [END property_run_query] + Multimap expected = HashMultimap.create(); + expected.put("Task", "type"); + expected.put("Task", "done"); + expected.put("Task", "completed"); + expected.put("Task", "priority"); + expected.put("Task", "created"); + expected.put("Task", "percent_complete"); + expected.put("Task", "tag"); + assertEquals(expected, propertiesByKind); + } + + @Test + public void testPropertyByKindRunQuery() { + setUpQueryTests(); + // [START property_by_kind_run_query] + KeyFactory keyFactory = datastore.newKeyFactory().kind("__kind__"); + Query query = Query.entityQueryBuilder() + .kind("__property__") + .filter(PropertyFilter.hasAncestor(keyFactory.newKey("Task"))) + .build(); + QueryResults results = datastore.run(query); + Multimap representationsByProperty = HashMultimap.create(); + while (results.hasNext()) { + Entity property = results.next(); + String propertyName = property.key().name(); + List> representations = property.getList("property_representation"); + for (Value value : representations) { + representationsByProperty.put(propertyName, (String) value.get()); + } + } + // [END property_by_kind_run_query] + Multimap expected = HashMultimap.create(); + expected.put("type", "STRING"); + expected.put("done", "BOOLEAN"); + expected.put("completed", "BOOLEAN"); + expected.put("priority", "INT64"); + expected.put("created", "INT64"); + expected.put("percent_complete", "DOUBLE"); + expected.put("tag", "STRING"); + assertEquals(expected, representationsByProperty); + } + + @Test + public void testPropertyFilteringRunQuery() { + setUpQueryTests(); + // [START property_filtering_run_query] + KeyFactory keyFactory = datastore.newKeyFactory() + .kind("__property__") + .ancestors(PathElement.of("__kind__", "Task")); + Query query = Query.keyQueryBuilder() + .kind("__property__") + .filter(PropertyFilter.ge("__key__", keyFactory.newKey("priority"))) + .build(); + Multimap propertiesByKind = HashMultimap.create(); + QueryResults results = datastore.run(query); + while (results.hasNext()) { + Key property = results.next(); + String kind = property.ancestors().get(property.ancestors().size() - 1).name(); + String propertyName = property.name(); + propertiesByKind.put(kind, propertyName); + } + // [END property_filtering_run_query] + Multimap expected = HashMultimap.create(); + expected.put("Task", "priority"); + expected.put("Task", "tag"); + expected.put("Task", "type"); + assertEquals(expected, propertiesByKind); + } + + @Test + public void testGqlRunQuery() { + setUpQueryTests(); + // [START gql_run_query] + Query query = + Query.gqlQueryBuilder(ResultType.ENTITY, "select * from Task order by created asc").build(); + // [END gql_run_query] + assertValidQuery(query); + } + + @Test + public void testGqlNamedBindingQuery() { + setUpQueryTests(); + // [START gql_named_binding_query] + Query query = + Query.gqlQueryBuilder( + ResultType.ENTITY, + "select * from Task where completed = @completed and priority = @priority") + .setBinding("completed", false) + .setBinding("priority", 4) + .build(); + // [END gql_named_binding_query] + assertValidQuery(query); + } + + @Test + public void testGqlPositionalBindingQuery() { + setUpQueryTests(); + // [START gql_positional_binding_query] + Query query = Query.gqlQueryBuilder( + ResultType.ENTITY, "select * from Task where completed = @1 and priority = @2") + .addBinding(false) + .addBinding(4) + .build(); + // [END gql_positional_binding_query] + assertValidQuery(query); + } + + @Test + public void testGqlLiteralQuery() { + setUpQueryTests(); + // [START gql_literal_query] + Query query = Query.gqlQueryBuilder( + ResultType.ENTITY, "select * from Task where completed = false and priority = 4") + .allowLiteral(true) + .build(); + // [END gql_literal_query] + assertValidQuery(query); + } +} diff --git a/datastore/src/main/java/com/google/datastore/snippets/TaskList.java b/datastore/src/main/java/com/google/datastore/snippets/TaskList.java new file mode 100644 index 00000000000..e5d9b8a0490 --- /dev/null +++ b/datastore/src/main/java/com/google/datastore/snippets/TaskList.java @@ -0,0 +1,232 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.datastore.snippets; + +import com.google.gcloud.datastore.Datastore; +import com.google.gcloud.datastore.DatastoreException; +import com.google.gcloud.datastore.DatastoreOptions; +import com.google.gcloud.datastore.DateTime; +import com.google.gcloud.datastore.Entity; +import com.google.gcloud.datastore.Key; +import com.google.gcloud.datastore.KeyFactory; +import com.google.gcloud.datastore.Query; +import com.google.gcloud.datastore.StringValue; +import com.google.gcloud.datastore.StructuredQuery.OrderBy; +import com.google.gcloud.datastore.Transaction; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * A simple Task List application demonstrating how to connect to Cloud Datastore, create, modify, + * delete, and query entities. + */ +public class TaskList { + + // [START build_service] + // Create an authorized Datastore service using Application Default Credentials. + private final Datastore datastore = DatastoreOptions.defaultInstance().service(); + + // Create a Key factory to construct keys associated with this project. + private final KeyFactory keyFactory = datastore.newKeyFactory().kind("Task"); + // [END build_service] + + // [START add_entity] + /** + * Adds a task entity to the Datastore. + * + * @param description The task description + * + * @return The {@link Key} of the entity. + */ + Key addTask(String description) { + Key key = datastore.allocateId(keyFactory.newKey()); + Entity task = Entity.builder(key) + .set("description", StringValue.builder(description).indexed(false).build()) + .set("created", DateTime.now()) + .set("done", false) + .build(); + datastore.put(task); + return key; + } + // [END add_entity] + + // [START update_entity] + /** + * Marks a task entity as done. + * + * @param id The ID of the task entity as given by {@link Key#id()} + * @throws DatastoreException if the task does not exist + */ + void markDone(long id) { + Transaction transaction = datastore.newTransaction(); + try { + Entity task = transaction.get(keyFactory.newKey(id)); + transaction.put(Entity.builder(task).set("done", true).build()); + transaction.commit(); + } finally { + if (transaction.active()) { + transaction.rollback(); + } + } + } + // [END update_entity] + + // [START retrieve_entities] + /** + * Returns a list of all task entities in ascending order of creation time. + */ + Iterator listTasks() { + Query query = + Query.entityQueryBuilder().kind("Task").orderBy(OrderBy.asc("created")).build(); + return datastore.run(query); + } + // [END retrieve_entities] + + // [START delete_entity] + /** + * Deletes a task entity. + * + * @param id The ID of the task entity as given by {@link Key#id()} + */ + void deleteTask(long id) { + datastore.delete(keyFactory.newKey(id)); + } + // [END delete_entity] + + // [START format_results] + /** + * Converts a list of task entities to a list of formatted task strings. + * + * @param tasks An iterator over task entities + * @returns A list of tasks strings, one per entity + */ + static List formatTasks(Iterator tasks) { + List strings = new ArrayList<>(); + while (tasks.hasNext()) { + Entity task = tasks.next(); + if (task.getBoolean("done")) { + strings.add( + String.format("%d : %s (done)", task.key().id(), task.getString("description"))); + } else { + strings.add(String.format("%d : %s (created %s)", task.key().id(), + task.getString("description"), task.getDateTime("created"))); + } + } + return strings; + } + // [END format_results] + + /** + * Handles a single command. + * + * @param commandLine A line of input provided by the user + */ + void handleCommandLine(String commandLine) { + String[] args = commandLine.split("\\s+"); + + if (args.length < 1) { + throw new IllegalArgumentException("not enough args"); + } + + String command = args[0]; + switch (command) { + case "new": + // Everything after the first whitespace token is interpreted to be the description. + args = commandLine.split("\\s+", 2); + if (args.length != 2) { + throw new IllegalArgumentException("missing description"); + } + // Set created to NOW() and done to false. + addTask(args[1]); + System.out.println("task added"); + break; + case "done": + assertArgsLength(args, 2); + long id = Long.parseLong(args[1]); + try { + markDone(id); + System.out.println("task marked done"); + } catch (DatastoreException e) { + System.out.printf("did not find a Task entity with ID %d\n", id); + } + break; + case "list": + assertArgsLength(args, 1); + List tasks = formatTasks(listTasks()); + System.out.printf("found %d tasks:\n", tasks.size()); + System.out.println("task ID : description"); + System.out.println("---------------------"); + for (String taskString : tasks) { + System.out.println(taskString); + } + break; + case "delete": + assertArgsLength(args, 2); + deleteTask(Long.parseLong(args[1])); + System.out.println("task deleted"); + break; + default: + throw new IllegalArgumentException("unrecognized command: " + command); + } + } + + private void assertArgsLength(String[] args, int expectedLength) { + if (args.length != expectedLength) { + throw new IllegalArgumentException( + String.format("expected exactly %d arg(s), found %d", expectedLength, args.length)); + } + } + + /** + * Exercises the methods defined in this class. + * + *

Assumes that you are authenticated using the Google Cloud SDK (using + * {@code gcloud auth login}). + */ + public static void main(String[] args) throws Exception { + TaskList taskList = new TaskList(); + System.out.println("Cloud Datastore Task List"); + System.out.println(); + printUsage(); + while (true) { + String commandLine = System.console().readLine("> "); + if (commandLine.trim().isEmpty()) { + break; + } + try { + taskList.handleCommandLine(commandLine); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + printUsage(); + } + } + System.out.println("exiting"); + System.exit(0); + } + + private static void printUsage() { + System.out.println("Usage:"); + System.out.println(); + System.out.println(" new Adds a task with a description "); + System.out.println(" done Marks a task as done"); + System.out.println(" list Lists all tasks by creation time"); + System.out.println(" delete Deletes a task"); + System.out.println(); + } +} diff --git a/pom.xml b/pom.xml index c8f6370685d..1fac8254359 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,7 @@ managed_vms/async-rest managed_vms/sparkjava bigquery + datastore logging monitoring storage/json-api From d6bfb60cd1957f0d46b4dfd6f66a68095883c871 Mon Sep 17 00:00:00 2001 From: Ajay Kannan Date: Wed, 10 Feb 2016 14:31:23 -0800 Subject: [PATCH 2/5] Minor fixes --- .../google/datastore/snippets/Concepts.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/datastore/src/main/java/com/google/datastore/snippets/Concepts.java b/datastore/src/main/java/com/google/datastore/snippets/Concepts.java index ae38b0070aa..b1b5624c59a 100644 --- a/datastore/src/main/java/com/google/datastore/snippets/Concepts.java +++ b/datastore/src/main/java/com/google/datastore/snippets/Concepts.java @@ -312,8 +312,9 @@ private List setUpBatchTests(Key taskKey1, Key taskKey2) { @Test public void testBatchUpsert() { - Key taskKey1 = keyFactory.newKey(1); - Key taskKey2 = keyFactory.newKey(2); + // [START batch_upsert] + Key taskKey1 = datastore.allocateId(keyFactory.newKey()); + Key taskKey2 = datastore.allocateId(keyFactory.newKey()); Entity task1 = Entity.builder(taskKey1) .set("type", "Personal") .set("done", false) @@ -326,7 +327,6 @@ public void testBatchUpsert() { .set("priority", 5) .set("description", "Integrate Cloud Datastore") .build(); - // [START batch_upsert] Batch batch = datastore.newBatch(); batch.put(task1, task2); batch.submit(); @@ -737,15 +737,9 @@ private Cursor cursorPaging(int pageSize, Cursor pageCursor) { @Test public void testEventualConsistentQuery() { - setUpQueryTests(); // [START eventual_consistent_query] - Query query = Query.entityQueryBuilder() - .kind("Task") - .filter(PropertyFilter.hasAncestor( - datastore.newKeyFactory().kind("TaskList").newKey("default"))) - .build(); + // Read consistency cannot be specified in gcloud-java. // [END eventual_consistent_query] - assertValidQuery(query); } @Test @@ -795,8 +789,8 @@ public void testTransactionalUpdate() { assertSuccessfulTransfer(keys.get(0), keys.get(1)); } - private void transferFunds(Key fromKey, Key toKey, long amount) { - // [START transactional_update] + // [START transactional_update] + void transferFunds(Key fromKey, Key toKey, long amount) { Transaction txn = datastore.newTransaction(); try { Entity from = txn.get(fromKey); @@ -811,8 +805,8 @@ private void transferFunds(Key fromKey, Key toKey, long amount) { txn.rollback(); } } - // [END transactional_update] } + // [END transactional_update] @Test public void testTransactionalRetry() { From c7b4340cebfdc7cc3dda24c2d16d581fbfd722d7 Mon Sep 17 00:00:00 2001 From: Ajay Kannan Date: Wed, 10 Feb 2016 16:54:24 -0800 Subject: [PATCH 3/5] Fix batchUpsert snippet + minor changes for consiceness --- .../google/datastore/snippets/Concepts.java | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/datastore/src/main/java/com/google/datastore/snippets/Concepts.java b/datastore/src/main/java/com/google/datastore/snippets/Concepts.java index b1b5624c59a..640ea130d28 100644 --- a/datastore/src/main/java/com/google/datastore/snippets/Concepts.java +++ b/datastore/src/main/java/com/google/datastore/snippets/Concepts.java @@ -110,7 +110,7 @@ public static void beforeClass() throws IOException, InterruptedException { public void setUp() { datastore = DatastoreOptions.builder() .projectId(PROJECT_ID) - .namespace("ghijklmnop") // for namespace metadata query + .namespace("ghijklmnop") .host("http://localhost:" + PORT) .build() .service(); @@ -120,15 +120,13 @@ public void setUp() { keyFactory = datastore.newKeyFactory().kind("Task"); taskKey = keyFactory.newKey("some-arbitrary-key"); testEntity = Entity.builder(taskKey, TEST_FULL_ENTITY).build(); - Calendar startDateCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - startDateCalendar.set(1990, 1, 1); - startDate = DateTime.copyFrom(startDateCalendar); - Calendar endDateCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - endDateCalendar.set(2000, 1, 1); - endDate = DateTime.copyFrom(endDateCalendar); - Calendar includedCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - includedCalendar.set(1999, 12, 31); - includedDate = DateTime.copyFrom(includedCalendar); + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + calendar.set(1990, 1, 1); + startDate = DateTime.copyFrom(calendar); + calendar.set(2000, 1, 1); + endDate = DateTime.copyFrom(calendar); + calendar.set(1999, 12, 31); + includedDate = DateTime.copyFrom(calendar); } /** @@ -195,9 +193,11 @@ private void assertValidEntity(Entity original) { @Test public void testEntityWithParent() { // [START entity_with_parent] - KeyFactory keyFactory = - datastore.newKeyFactory().ancestors(PathElement.of("TaskList", "default")).kind("Task"); - Entity task = Entity.builder(keyFactory.newKey("sampleTask")) + Key taskKey = datastore.newKeyFactory() + .ancestors(PathElement.of("TaskList", "default")) + .kind("Task") + .newKey("sampleTask"); + Entity task = Entity.builder(taskKey) .set("type", "Personal") .set("done", false) .set("priority", 4) @@ -313,26 +313,25 @@ private List setUpBatchTests(Key taskKey1, Key taskKey2) { @Test public void testBatchUpsert() { // [START batch_upsert] - Key taskKey1 = datastore.allocateId(keyFactory.newKey()); - Key taskKey2 = datastore.allocateId(keyFactory.newKey()); - Entity task1 = Entity.builder(taskKey1) + FullEntity task1 = FullEntity.builder(keyFactory.newKey()) .set("type", "Personal") .set("done", false) .set("priority", 4) .set("description", "Learn Cloud Datastore") .build(); - Entity task2 = Entity.builder(taskKey2) + FullEntity task2 = Entity.builder(keyFactory.newKey()) .set("type", "Personal") .set("done", false) .set("priority", 5) .set("description", "Integrate Cloud Datastore") .build(); Batch batch = datastore.newBatch(); - batch.put(task1, task2); - batch.submit(); + batch.addWithDeferredIdAllocation(task1, task2); + Batch.Response response = batch.submit(); + List taskKeys = response.generatedKeys(); // keys listed in order of insertion // [END batch_upsert] - assertEquals(task1, datastore.get(taskKey1)); - assertEquals(task2, datastore.get(taskKey2)); + assertEquals(Entity.builder(taskKeys.get(0), task1).build(), datastore.get(taskKeys.get(0))); + assertEquals(Entity.builder(taskKeys.get(1), task2).build(), datastore.get(taskKeys.get(1))); } @Test @@ -443,7 +442,7 @@ public void testKeyFilter() { // [START key_filter] Query query = Query.entityQueryBuilder() .kind("Task") - .filter(PropertyFilter.gt("__key__", keyFactory.newKey("someTask"))) + .filter(PropertyFilter.gt("__key__", keyFactory.newKey("someTask"))) .build(); // [END key_filter] assertValidQuery(query); @@ -946,10 +945,10 @@ public void testPropertyRunQuery() { public void testPropertyByKindRunQuery() { setUpQueryTests(); // [START property_by_kind_run_query] - KeyFactory keyFactory = datastore.newKeyFactory().kind("__kind__"); + Key key = datastore.newKeyFactory().kind("__kind__").newKey("Task"); Query query = Query.entityQueryBuilder() .kind("__property__") - .filter(PropertyFilter.hasAncestor(keyFactory.newKey("Task"))) + .filter(PropertyFilter.hasAncestor(key)) .build(); QueryResults results = datastore.run(query); Multimap representationsByProperty = HashMultimap.create(); @@ -977,12 +976,14 @@ public void testPropertyByKindRunQuery() { public void testPropertyFilteringRunQuery() { setUpQueryTests(); // [START property_filtering_run_query] - KeyFactory keyFactory = datastore.newKeyFactory() + Key key = + datastore.newKeyFactory() .kind("__property__") - .ancestors(PathElement.of("__kind__", "Task")); + .ancestors(PathElement.of("__kind__", "Task")) + .newKey("priority"); Query query = Query.keyQueryBuilder() .kind("__property__") - .filter(PropertyFilter.ge("__key__", keyFactory.newKey("priority"))) + .filter(PropertyFilter.ge("__key__", key)) .build(); Multimap propertiesByKind = HashMultimap.create(); QueryResults results = datastore.run(query); From 7f34191c93eefbeb4a360f2d09242feccea39673 Mon Sep 17 00:00:00 2001 From: Ajay Kannan Date: Thu, 11 Feb 2016 10:10:27 -0800 Subject: [PATCH 4/5] Style fixes and minor bug fixes --- .../google/datastore/snippets/Concepts.java | 154 +++++++++--------- .../google/datastore/snippets/TaskList.java | 9 +- 2 files changed, 79 insertions(+), 84 deletions(-) diff --git a/datastore/src/main/java/com/google/datastore/snippets/Concepts.java b/datastore/src/main/java/com/google/datastore/snippets/Concepts.java index 640ea130d28..0fac1d1b125 100644 --- a/datastore/src/main/java/com/google/datastore/snippets/Concepts.java +++ b/datastore/src/main/java/com/google/datastore/snippets/Concepts.java @@ -16,16 +16,16 @@ package com.google.datastore.snippets; +import static java.util.Calendar.DECEMBER; +import static java.util.Calendar.JANUARY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterators; -import com.google.common.collect.Multimap; -import com.google.gcloud.datastore.Batch; import com.google.gcloud.datastore.Cursor; import com.google.gcloud.datastore.Datastore; import com.google.gcloud.datastore.DatastoreException; @@ -46,6 +46,7 @@ import com.google.gcloud.datastore.StructuredQuery; import com.google.gcloud.datastore.StructuredQuery.CompositeFilter; import com.google.gcloud.datastore.StructuredQuery.OrderBy; +import com.google.gcloud.datastore.StructuredQuery.Projection; import com.google.gcloud.datastore.StructuredQuery.PropertyFilter; import com.google.gcloud.datastore.Transaction; import com.google.gcloud.datastore.Value; @@ -61,9 +62,14 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.TimeZone; /** @@ -121,11 +127,11 @@ public void setUp() { taskKey = keyFactory.newKey("some-arbitrary-key"); testEntity = Entity.builder(taskKey, TEST_FULL_ENTITY).build(); Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - calendar.set(1990, 1, 1); + calendar.set(1990, JANUARY, 1); startDate = DateTime.copyFrom(calendar); - calendar.set(2000, 1, 1); + calendar.set(2000, JANUARY, 1); endDate = DateTime.copyFrom(calendar); - calendar.set(1999, 12, 31); + calendar.set(1999, DECEMBER, 31); includedDate = DateTime.copyFrom(calendar); } @@ -158,8 +164,7 @@ public void testIncompleteKey() { @Test public void testNamedKey() { // [START named_key] - KeyFactory keyFactory = datastore.newKeyFactory().kind("Task"); - Key taskKey = keyFactory.newKey("sampleTask"); + Key taskKey = datastore.newKeyFactory().kind("Task").newKey("sampleTask"); // [END named_key] assertValidKey(taskKey); } @@ -167,9 +172,10 @@ public void testNamedKey() { @Test public void testKeyWithParent() { // [START key_with_parent] - KeyFactory keyFactory = - datastore.newKeyFactory().ancestors(PathElement.of("TaskList", "default")).kind("Task"); - Key taskKey = keyFactory.newKey("sampleTask"); + Key taskKey = datastore.newKeyFactory() + .ancestors(PathElement.of("TaskList", "default")) + .kind("Task") + .newKey("sampleTask"); // [END key_with_parent] assertValidKey(taskKey); } @@ -249,8 +255,7 @@ public void testBasicEntity() { @Test public void testUpsert() { // [START upsert] - Key taskKey = datastore.allocateId(keyFactory.newKey()); - Entity task = Entity.builder(taskKey).build(); + Entity task = Entity.builder(keyFactory.newKey("sampleTask")).build(); datastore.put(task); // [END upsert] assertEquals(task, datastore.get(task.key())); @@ -259,10 +264,9 @@ public void testUpsert() { @Test public void testInsert() { // [START insert] - Entity task = Entity.builder(taskKey).build(); - Key taskKey = datastore.add(task).key(); + Key taskKey = datastore.add(FullEntity.builder(keyFactory.newKey()).build()).key(); // [END insert] - assertEquals(task, datastore.get(taskKey)); + assertEquals(FullEntity.builder(taskKey).build(), datastore.get(taskKey)); } @Test @@ -325,13 +329,12 @@ public void testBatchUpsert() { .set("priority", 5) .set("description", "Integrate Cloud Datastore") .build(); - Batch batch = datastore.newBatch(); - batch.addWithDeferredIdAllocation(task1, task2); - Batch.Response response = batch.submit(); - List taskKeys = response.generatedKeys(); // keys listed in order of insertion + List tasks = datastore.add(task1, task2); + Key taskKey1 = tasks.get(0).key(); + Key taskKey2 = tasks.get(1).key(); // [END batch_upsert] - assertEquals(Entity.builder(taskKeys.get(0), task1).build(), datastore.get(taskKeys.get(0))); - assertEquals(Entity.builder(taskKeys.get(1), task2).build(), datastore.get(taskKeys.get(1))); + assertEquals(Entity.builder(taskKey1, task1).build(), datastore.get(taskKey1)); + assertEquals(Entity.builder(taskKey2, task2).build(), datastore.get(taskKey2)); } @Test @@ -352,18 +355,18 @@ public void testBatchDelete() { Key taskKey2 = keyFactory.newKey(2); setUpBatchTests(taskKey1, taskKey2); // [START batch_delete] - Batch batch = datastore.newBatch(); - batch.delete(taskKey1, taskKey2); - batch.submit(); + datastore.delete(taskKey1, taskKey2); // [END batch_delete] assertNull(datastore.get(taskKey1)); assertNull(datastore.get(taskKey2)); } private void setUpQueryTests() { - KeyFactory keyFactory = - datastore.newKeyFactory().kind("Task").ancestors(PathElement.of("TaskList", "default")); - datastore.put(Entity.builder(keyFactory.newKey("someTask")) + Key taskKey = datastore.newKeyFactory() + .kind("Task") + .ancestors(PathElement.of("TaskList", "default")) + .newKey("someTask"); + datastore.put(Entity.builder(taskKey) .set("type", "Personal") .set("done", false) .set("completed", false) @@ -371,8 +374,7 @@ private void setUpQueryTests() { .set("created", includedDate) .set("percent_complete", 10.0) .set("description", StringValue.builder("Learn Cloud Datastore").indexed(false).build()) - .set("tag", ImmutableList.of(StringValue.of("fun"), StringValue.of("l"), - StringValue.of("programming"))) + .set("tag", StringValue.of("fun"), StringValue.of("l"), StringValue.of("programming")) .build()); } @@ -510,9 +512,7 @@ public void testProjectionQuery() { // [START projection_query] Query query = Query.projectionEntityQueryBuilder() .kind("Task") - .projection( - StructuredQuery.Projection.property("priority"), - StructuredQuery.Projection.property("percent_complete")) + .projection(Projection.property("priority"), Projection.property("percent_complete")) .build(); // [END projection_query] assertValidQuery(query); @@ -523,13 +523,11 @@ public void testRunProjectionQuery() { setUpQueryTests(); Query query = Query.projectionEntityQueryBuilder() .kind("Task") - .projection( - StructuredQuery.Projection.property("priority"), - StructuredQuery.Projection.property("percent_complete")) + .projection(Projection.property("priority"), Projection.property("percent_complete")) .build(); + // [START run_query_projection] List priorities = new LinkedList<>(); List percentCompletes = new LinkedList<>(); - // [START run_query_projection] QueryResults tasks = datastore.run(query); while (tasks.hasNext()) { ProjectionEntity task = tasks.next(); @@ -567,8 +565,7 @@ public void testDistinctQuery() { // [START distinct_query] Query query = Query.projectionEntityQueryBuilder() .kind("Task") - .projection(StructuredQuery.Projection.property("type"), - StructuredQuery.Projection.property("priority")) + .projection(Projection.property("type"), Projection.property("priority")) .groupBy("type", "priority") .orderBy(OrderBy.asc("type"), OrderBy.asc("priority")) .build(); @@ -582,8 +579,7 @@ public void testDistinctOnQuery() { // [START distinct_on_query] Query query = Query.projectionEntityQueryBuilder() .kind("Task") - .projection(StructuredQuery.Projection.property("type"), - StructuredQuery.Projection.first("priority")) + .projection(Projection.property("type"), Projection.first("priority")) .groupBy("type") .orderBy(OrderBy.asc("type"), OrderBy.asc("priority")) .build(); @@ -719,17 +715,12 @@ private Cursor cursorPaging(int pageSize, Cursor pageCursor) { queryBuilder.startCursor(pageCursor); } QueryResults tasks = datastore.run(queryBuilder.build()); - Entity task = null; + Entity task; while (tasks.hasNext()) { task = tasks.next(); // do something with the task } - Cursor nextPageCursor = null; - if (task != null && tasks.cursorAfter() != null) { - nextPageCursor = tasks.cursorAfter(); - // Call nextPageCursor.toUrlSafe() if you want an encoded cursor that can be used as part of a - // URL. - } + Cursor nextPageCursor = tasks.cursorAfter(); // [END cursor_paging] return nextPageCursor; } @@ -792,10 +783,11 @@ public void testTransactionalUpdate() { void transferFunds(Key fromKey, Key toKey, long amount) { Transaction txn = datastore.newTransaction(); try { - Entity from = txn.get(fromKey); + List entities = txn.fetch(fromKey, toKey); + Entity from = entities.get(0); Entity updatedFrom = Entity.builder(from).set("balance", from.getLong("balance") - amount).build(); - Entity to = txn.get(toKey); + Entity to = entities.get(1); Entity updatedTo = Entity.builder(to).set("balance", to.getLong("balance") + amount).build(); txn.put(updatedFrom, updatedTo); txn.commit(); @@ -825,6 +817,7 @@ public void testTransactionalRetry() { --retries; } } + // Retry handling can also be configured and automatically applied using gcloud-java. // [END transactional_retry] assertSuccessfulTransfer(keys.get(0), keys.get(1)); } @@ -835,9 +828,8 @@ public void testTransactionalGetOrCreate() { Entity task; Transaction txn = datastore.newTransaction(); try { - try { - task = txn.get(taskKey); - } catch (DatastoreException e) { + task = txn.get(taskKey); + if (task == null) { task = Entity.builder(taskKey).build(); txn.put(task); txn.commit(); @@ -922,22 +914,22 @@ public void testPropertyRunQuery() { // [START property_run_query] Query query = Query.keyQueryBuilder().kind("__property__").build(); QueryResults results = datastore.run(query); - Multimap propertiesByKind = HashMultimap.create(); + Map> propertiesByKind = new HashMap<>(); while (results.hasNext()) { Key property = results.next(); String kind = property.ancestors().get(property.ancestors().size() - 1).name(); String propertyName = property.name(); - propertiesByKind.put(kind, propertyName); + if (!propertiesByKind.containsKey(kind)) { + propertiesByKind.put(kind, new HashSet()); + } + propertiesByKind.get(kind).add(propertyName); } // [END property_run_query] - Multimap expected = HashMultimap.create(); - expected.put("Task", "type"); - expected.put("Task", "done"); - expected.put("Task", "completed"); - expected.put("Task", "priority"); - expected.put("Task", "created"); - expected.put("Task", "percent_complete"); - expected.put("Task", "tag"); + Map> expected = new HashMap<>(); + expected.put( + "Task", + ImmutableSet.of( + "done", "type", "done", "completed", "priority", "created", "percent_complete", "tag")); assertEquals(expected, propertiesByKind); } @@ -951,24 +943,27 @@ public void testPropertyByKindRunQuery() { .filter(PropertyFilter.hasAncestor(key)) .build(); QueryResults results = datastore.run(query); - Multimap representationsByProperty = HashMultimap.create(); + Map> representationsByProperty = new HashMap<>(); while (results.hasNext()) { Entity property = results.next(); String propertyName = property.key().name(); List> representations = property.getList("property_representation"); + if (!representationsByProperty.containsKey(propertyName)) { + representationsByProperty.put(propertyName, new HashSet()); + } for (Value value : representations) { - representationsByProperty.put(propertyName, (String) value.get()); + representationsByProperty.get(propertyName).add((String) value.get()); } } // [END property_by_kind_run_query] - Multimap expected = HashMultimap.create(); - expected.put("type", "STRING"); - expected.put("done", "BOOLEAN"); - expected.put("completed", "BOOLEAN"); - expected.put("priority", "INT64"); - expected.put("created", "INT64"); - expected.put("percent_complete", "DOUBLE"); - expected.put("tag", "STRING"); + Map> expected = new HashMap<>(); + expected.put("type", Collections.singleton("STRING")); + expected.put("done", Collections.singleton("BOOLEAN")); + expected.put("completed", Collections.singleton("BOOLEAN")); + expected.put("priority", Collections.singleton("INT64")); + expected.put("created", Collections.singleton("INT64")); + expected.put("percent_complete", Collections.singleton("DOUBLE")); + expected.put("tag", Collections.singleton("STRING")); assertEquals(expected, representationsByProperty); } @@ -985,19 +980,20 @@ public void testPropertyFilteringRunQuery() { .kind("__property__") .filter(PropertyFilter.ge("__key__", key)) .build(); - Multimap propertiesByKind = HashMultimap.create(); + Map> propertiesByKind = new HashMap<>(); QueryResults results = datastore.run(query); while (results.hasNext()) { Key property = results.next(); String kind = property.ancestors().get(property.ancestors().size() - 1).name(); String propertyName = property.name(); - propertiesByKind.put(kind, propertyName); + if (!propertiesByKind.containsKey(kind)) { + propertiesByKind.put(kind, new HashSet()); + } + propertiesByKind.get(kind).add(propertyName); } // [END property_filtering_run_query] - Multimap expected = HashMultimap.create(); - expected.put("Task", "priority"); - expected.put("Task", "tag"); - expected.put("Task", "type"); + Map> expected = new HashMap<>(); + expected.put("Task", ImmutableSet.of("priority", "tag", "type")); assertEquals(expected, propertiesByKind); } diff --git a/datastore/src/main/java/com/google/datastore/snippets/TaskList.java b/datastore/src/main/java/com/google/datastore/snippets/TaskList.java index e5d9b8a0490..40920e291cd 100644 --- a/datastore/src/main/java/com/google/datastore/snippets/TaskList.java +++ b/datastore/src/main/java/com/google/datastore/snippets/TaskList.java @@ -51,7 +51,6 @@ public class TaskList { * Adds a task entity to the Datastore. * * @param description The task description - * * @return The {@link Key} of the entity. */ Key addTask(String description) { @@ -114,7 +113,7 @@ void deleteTask(long id) { * Converts a list of task entities to a list of formatted task strings. * * @param tasks An iterator over task entities - * @returns A list of tasks strings, one per entity + * @return A list of tasks strings, one per entity */ static List formatTasks(Iterator tasks) { List strings = new ArrayList<>(); @@ -152,7 +151,7 @@ void handleCommandLine(String commandLine) { if (args.length != 2) { throw new IllegalArgumentException("missing description"); } - // Set created to NOW() and done to false. + // Set created to now() and done to false. addTask(args[1]); System.out.println("task added"); break; @@ -163,13 +162,13 @@ void handleCommandLine(String commandLine) { markDone(id); System.out.println("task marked done"); } catch (DatastoreException e) { - System.out.printf("did not find a Task entity with ID %d\n", id); + System.out.printf("did not find a Task entity with ID %d%n", id); } break; case "list": assertArgsLength(args, 1); List tasks = formatTasks(listTasks()); - System.out.printf("found %d tasks:\n", tasks.size()); + System.out.printf("found %d tasks:%n", tasks.size()); System.out.println("task ID : description"); System.out.println("---------------------"); for (String taskString : tasks) { From 63f756a4a7c17a66f7b755c9e7f85a46fcd5d962 Mon Sep 17 00:00:00 2001 From: Ajay Kannan Date: Thu, 11 Feb 2016 16:38:45 -0800 Subject: [PATCH 5/5] Fix nits in pom/README/concept snippets --- datastore/README.md | 2 +- datastore/pom.xml | 4 +- .../google/datastore/snippets/Concepts.java | 64 ++++++++++--------- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/datastore/README.md b/datastore/README.md index 7d888580ae0..a43ad13237d 100644 --- a/datastore/README.md +++ b/datastore/README.md @@ -11,4 +11,4 @@ This directory contains sample code used in Google Cloud Datastore documentation 2. Compile the program by typing `mvn clean compile` in command line. -3. Run the program by typing `mvn exec:java` in command line. +3. Run the program by typing `mvn exec:java` in command line. In addition to listing tasks via this command line interface, you can view tasks you create in the [Google Cloud Developer's Console](https://console.cloud.google.com/). diff --git a/datastore/pom.xml b/datastore/pom.xml index 9febec996cf..68806fa3731 100644 --- a/datastore/pom.xml +++ b/datastore/pom.xml @@ -31,8 +31,8 @@ maven-compiler-plugin 2.5.1 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/datastore/src/main/java/com/google/datastore/snippets/Concepts.java b/datastore/src/main/java/com/google/datastore/snippets/Concepts.java index 0fac1d1b125..eedd05c7bdc 100644 --- a/datastore/src/main/java/com/google/datastore/snippets/Concepts.java +++ b/datastore/src/main/java/com/google/datastore/snippets/Concepts.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertNull; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterators; import com.google.gcloud.datastore.Cursor; @@ -49,7 +50,6 @@ import com.google.gcloud.datastore.StructuredQuery.Projection; import com.google.gcloud.datastore.StructuredQuery.PropertyFilter; import com.google.gcloud.datastore.Transaction; -import com.google.gcloud.datastore.Value; import com.google.gcloud.datastore.testing.LocalGcdHelper; import org.junit.AfterClass; @@ -715,9 +715,8 @@ private Cursor cursorPaging(int pageSize, Cursor pageCursor) { queryBuilder.startCursor(pageCursor); } QueryResults tasks = datastore.run(queryBuilder.build()); - Entity task; while (tasks.hasNext()) { - task = tasks.next(); + Entity task = tasks.next(); // do something with the task } Cursor nextPageCursor = tasks.cursorAfter(); @@ -919,17 +918,16 @@ public void testPropertyRunQuery() { Key property = results.next(); String kind = property.ancestors().get(property.ancestors().size() - 1).name(); String propertyName = property.name(); - if (!propertiesByKind.containsKey(kind)) { - propertiesByKind.put(kind, new HashSet()); + Collection properties = propertiesByKind.get(kind); + if (properties == null) { + properties = new HashSet<>(); + propertiesByKind.put(kind, properties); } - propertiesByKind.get(kind).add(propertyName); + properties.add(propertyName); } // [END property_run_query] - Map> expected = new HashMap<>(); - expected.put( - "Task", - ImmutableSet.of( - "done", "type", "done", "completed", "priority", "created", "percent_complete", "tag")); + Map> expected = ImmutableMap.of("Task", ImmutableSet.of( + "done", "type", "done", "completed", "priority", "created", "percent_complete", "tag")); assertEquals(expected, propertiesByKind); } @@ -940,30 +938,34 @@ public void testPropertyByKindRunQuery() { Key key = datastore.newKeyFactory().kind("__kind__").newKey("Task"); Query query = Query.entityQueryBuilder() .kind("__property__") - .filter(PropertyFilter.hasAncestor(key)) + .filter(PropertyFilter.hasAncestor(key)) .build(); QueryResults results = datastore.run(query); Map> representationsByProperty = new HashMap<>(); while (results.hasNext()) { Entity property = results.next(); String propertyName = property.key().name(); - List> representations = property.getList("property_representation"); - if (!representationsByProperty.containsKey(propertyName)) { - representationsByProperty.put(propertyName, new HashSet()); + List representations = + (List) property.getList("property_representation"); + Collection currentRepresentations = representationsByProperty.get(propertyName); + if (currentRepresentations == null) { + currentRepresentations = new HashSet<>(); + representationsByProperty.put(propertyName, currentRepresentations); } - for (Value value : representations) { - representationsByProperty.get(propertyName).add((String) value.get()); + for (StringValue value : representations) { + currentRepresentations.add(value.get()); } } // [END property_by_kind_run_query] - Map> expected = new HashMap<>(); - expected.put("type", Collections.singleton("STRING")); - expected.put("done", Collections.singleton("BOOLEAN")); - expected.put("completed", Collections.singleton("BOOLEAN")); - expected.put("priority", Collections.singleton("INT64")); - expected.put("created", Collections.singleton("INT64")); - expected.put("percent_complete", Collections.singleton("DOUBLE")); - expected.put("tag", Collections.singleton("STRING")); + Map> expected = ImmutableMap.>builder() + .put("type", Collections.singleton("STRING")) + .put("done", Collections.singleton("BOOLEAN")) + .put("completed", Collections.singleton("BOOLEAN")) + .put("priority", Collections.singleton("INT64")) + .put("created", Collections.singleton("INT64")) + .put("percent_complete", Collections.singleton("DOUBLE")) + .put("tag", Collections.singleton("STRING")) + .build(); assertEquals(expected, representationsByProperty); } @@ -986,14 +988,16 @@ public void testPropertyFilteringRunQuery() { Key property = results.next(); String kind = property.ancestors().get(property.ancestors().size() - 1).name(); String propertyName = property.name(); - if (!propertiesByKind.containsKey(kind)) { - propertiesByKind.put(kind, new HashSet()); + Collection properties = propertiesByKind.get(kind); + if (properties == null) { + properties = new HashSet(); + propertiesByKind.put(kind, properties); } - propertiesByKind.get(kind).add(propertyName); + properties.add(propertyName); } // [END property_filtering_run_query] - Map> expected = new HashMap<>(); - expected.put("Task", ImmutableSet.of("priority", "tag", "type")); + Map> expected = + ImmutableMap.of("Task", ImmutableSet.of("priority", "tag", "type")); assertEquals(expected, propertiesByKind); }