Skip to content

Commit cf9983d

Browse files
jainsahabgcf-owl-bot[bot]Sita04
authored
samples: Add sample snippets for count aggregations (#871)
* Add method in Datastore client to invoke rpc for aggregation query * Creating count aggregation and using it to populate Aggregation proto * Moving aggregation builder method to root level aggregation class * Introducing RecordQuery to represent queries which returns entity records when executed * Updating gitignore with patch extension * Setting up structure of Aggregation query and its builder * Introducing ProtoPreparer to populate the request protos * Delegating responsibility of preparing query proto to QueryPreparer * Populating aggregation query with nested structured query * Delegating responsibility of preparing query proto in GqlQuery to QueryPreparer * Removing RecordQuery from the query hierarchy and making it a standalone interface for now * Populating aggregation query with nested gql query * Removing deprecation warning by using assertThrows instead of ExpectedException rule * Making DatastoreRpc call aggregation query method on client * Creating response transformer to transform aggregation query response into domain objects * Implementing aggregation query executor to execute AggergationQuery * Adding missing assertion statements * Creating RetryExecutor to inject it as a dependency in other components * Making RetryExecutor accept RetrySettings when creating it * Revert "Making RetryExecutor accept RetrySettings when creating it" This reverts commit 1dfafb7. * Revert "Creating RetryExecutor to inject it as a dependency in other components" This reverts commit 8872a55. * Introducing RetryAndTraceDatastoreRpcDecorator to have retry and traceability logic on top of another DatastoreRpc * Extracting out the responsibility of preparing ReadOption in it's own ProtoPreparer * Making QueryExecutor to execute query with provided ReadOptions * Exposing readTime to the user * Ignoring runAggregationQuery method from clirr check * Making readTime final * Allowing namespace to be optional in AggregationQuery * Add capability to fetch aggregation result by passing alias * Implementing User facing datastore.runAggrgation method to run aggregation query * Add integration test for count aggregation * Add transaction Id support in ReadOptionsProtoPreparer * Supporting aggregation query with transactions * Allowing user to create Aggregation directly without involving its builder * Preventing creating duplicated aggregation when creating an aggregation query * Marking RecordQuery implemented method as InternalApi * Writing comments and JavaDoc for aggregation query related class * Adding a default implementation in the public interfaces to avoid compile time failures * covering a scenario to maintain consistent snapshot when executing aggregation query in a transaction * Creating emulator proxy to simulate AggregationQuery response from emulator * Integration test to execute an aggregation query in a read only transaction * Count aggregation samples with structuredQuery and gql query * Count aggregation samples to demonstrate stale read * Getting rid off limit operation on count aggregation as same behaviour can be achieved by using 'limit' operation on the underlying query * Removing import statement from javadoc and undo changes in .gitignore file * Using Optional instead of returning null from ReadOptionsProtoPreparer * using assertThat from Truth library * Removing 'limit' api from count query samples * fixing unit test * Getting rid off Double braces initialization syntax * Fixing lint * Getting rid off emulator proxy and using easy mock to check the aggregationQuery triggered * Deleting a entity created locally in other test which is causing failure in other test * Deleting all keys in datastore in integration test so that new test can start fresh * Executing two read write transaction simultaneously and verifying their behaviour * Removing tests to verify serializability as it's an underlying implementation detail * Fixing lint * Adding runAggregationQuery method to reflect config so that it's available and accessible in native image through reflection * Aggregation query samples with limit and order by option * Aggregation query samples with transactions * fixing lint and reusing sysoutRule * correcting region tag * simplifying tearDown method * Reverting back to using Task domain for aggregation query samples * Limiting tearDown method to delete only Task entities * Splitting count aggregation query samples into individual java files * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Adding missing header * Sticking to the practise of deleting the old created data in setUp of test * Moving comment to the top of statement. Co-authored-by: Sita Lakshmi Sangameswaran <[email protected]> * Making builder syntax multiline Co-authored-by: Sita Lakshmi Sangameswaran <[email protected]> * Adding comment describing the purpose of GQL query Co-authored-by: Sita Lakshmi Sangameswaran <[email protected]> * Rephrasing the comment Co-authored-by: Sita Lakshmi Sangameswaran <[email protected]> * Adding a comment describing the purpose of the GQL query Co-authored-by: Sita Lakshmi Sangameswaran <[email protected]> * Rephrasing the comment Co-authored-by: Sita Lakshmi Sangameswaran <[email protected]> * Relocating region tags to include import statments * Few styling and comment fixes * Adding comment in the transaction block * Adding fullstop to the comment lines * Showcasing with/without alias usage separately * Importing static methods are we are now including import statements in samples * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com> Co-authored-by: Sita Lakshmi Sangameswaran <[email protected]>
1 parent e48e7fc commit cf9983d

File tree

11 files changed

+734
-13
lines changed

11 files changed

+734
-13
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,13 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-datastore/tre
234234
| --------------------------- | --------------------------------- | ------ |
235235
| Native Image Datastore Sample | [source code](https://github.com/googleapis/java-datastore/blob/main/samples/native-image-sample/src/main/java/com/example/datastore/NativeImageDatastoreSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-datastore&page=editor&open_in_editor=samples/native-image-sample/src/main/java/com/example/datastore/NativeImageDatastoreSample.java) |
236236
| Quickstart Sample | [source code](https://github.com/googleapis/java-datastore/blob/main/samples/snippets/src/main/java/com/example/datastore/QuickstartSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-datastore&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/datastore/QuickstartSample.java) |
237+
| Count Aggregation In Transaction | [source code](https://github.com/googleapis/java-datastore/blob/main/samples/snippets/src/main/java/com/example/datastore/aggregation/CountAggregationInTransaction.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-datastore&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/datastore/aggregation/CountAggregationInTransaction.java) |
238+
| Count Aggregation On Kind | [source code](https://github.com/googleapis/java-datastore/blob/main/samples/snippets/src/main/java/com/example/datastore/aggregation/CountAggregationOnKind.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-datastore&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/datastore/aggregation/CountAggregationOnKind.java) |
239+
| Count Aggregation With Gql Query | [source code](https://github.com/googleapis/java-datastore/blob/main/samples/snippets/src/main/java/com/example/datastore/aggregation/CountAggregationWithGqlQuery.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-datastore&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/datastore/aggregation/CountAggregationWithGqlQuery.java) |
240+
| Count Aggregation With Limit | [source code](https://github.com/googleapis/java-datastore/blob/main/samples/snippets/src/main/java/com/example/datastore/aggregation/CountAggregationWithLimit.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-datastore&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/datastore/aggregation/CountAggregationWithLimit.java) |
241+
| Count Aggregation With Order By | [source code](https://github.com/googleapis/java-datastore/blob/main/samples/snippets/src/main/java/com/example/datastore/aggregation/CountAggregationWithOrderBy.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-datastore&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/datastore/aggregation/CountAggregationWithOrderBy.java) |
242+
| Count Aggregation With Property Filter | [source code](https://github.com/googleapis/java-datastore/blob/main/samples/snippets/src/main/java/com/example/datastore/aggregation/CountAggregationWithPropertyFilter.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-datastore&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/datastore/aggregation/CountAggregationWithPropertyFilter.java) |
243+
| Count Aggregation With Stale Read | [source code](https://github.com/googleapis/java-datastore/blob/main/samples/snippets/src/main/java/com/example/datastore/aggregation/CountAggregationWithStaleRead.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-datastore&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/datastore/aggregation/CountAggregationWithStaleRead.java) |
237244
| Task List | [source code](https://github.com/googleapis/java-datastore/blob/main/samples/snippets/src/main/java/com/google/datastore/snippets/TaskList.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-datastore&page=editor&open_in_editor=samples/snippets/src/main/java/com/google/datastore/snippets/TaskList.java) |
238245

239246

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2022 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.datastore.aggregation;
18+
19+
// [START datastore_count_aggregation_query_in_transaction]
20+
21+
import static com.google.cloud.datastore.aggregation.Aggregation.count;
22+
23+
import com.google.cloud.datastore.AggregationQuery;
24+
import com.google.cloud.datastore.Datastore;
25+
import com.google.cloud.datastore.Datastore.TransactionCallable;
26+
import com.google.cloud.datastore.DatastoreOptions;
27+
import com.google.cloud.datastore.Entity;
28+
import com.google.cloud.datastore.EntityQuery;
29+
import com.google.cloud.datastore.Key;
30+
import com.google.cloud.datastore.Query;
31+
import com.google.cloud.datastore.StructuredQuery.PropertyFilter;
32+
import com.google.common.collect.Iterables;
33+
34+
public class CountAggregationInTransaction {
35+
36+
public static void invoke() {
37+
// Instantiates a client.
38+
Datastore datastore = DatastoreOptions.getDefaultInstance().getService();
39+
40+
// The kind for the new entity.
41+
String kind = "Task";
42+
43+
Key task1Key = datastore.newKeyFactory().setKind(kind).newKey("task1");
44+
Key task2Key = datastore.newKeyFactory().setKind(kind).newKey("task2");
45+
46+
// Save all the tasks.
47+
datastore.put(
48+
Entity.newBuilder(task1Key).set("owner", "john").build(),
49+
Entity.newBuilder(task2Key).set("owner", "john").build());
50+
51+
// Using transactions to maintain consistent application state.
52+
datastore.runInTransaction(
53+
(TransactionCallable<Void>)
54+
transaction -> {
55+
// Create a query to get the count of all tasks of owner 'John'.
56+
EntityQuery tasksOfJohn =
57+
Query.newEntityQueryBuilder()
58+
.setKind(kind)
59+
.setFilter(PropertyFilter.eq("owner", "john"))
60+
.build();
61+
AggregationQuery totalTasksQuery =
62+
Query.newAggregationQueryBuilder()
63+
.over(tasksOfJohn)
64+
.addAggregation(count().as("tasks_count"))
65+
.build();
66+
67+
// Executing aggregation query in the ongoing transaction.
68+
Long tasksCount =
69+
Iterables.getOnlyElement(transaction.runAggregation(totalTasksQuery))
70+
.get("tasks_count");
71+
72+
if (tasksCount < 2) {
73+
Key newTaskKey = datastore.newKeyFactory().setKind(kind).newKey("task3");
74+
Entity newTask = Entity.newBuilder(newTaskKey).set("owner", "john").build();
75+
// Inserting a new entity in the transaction.
76+
transaction.put(newTask);
77+
} else {
78+
System.out.printf("Found existing %d tasks, rolling back", tasksCount);
79+
throw new Exception("User 'John' cannot have more than 2 tasks");
80+
}
81+
return null;
82+
});
83+
}
84+
}
85+
// [END datastore_count_aggregation_query_in_transaction]
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2022 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.datastore.aggregation;
18+
19+
// [START datastore_count_aggregation_query_on_kind]
20+
import static com.google.cloud.datastore.aggregation.Aggregation.count;
21+
22+
import com.google.cloud.datastore.AggregationQuery;
23+
import com.google.cloud.datastore.AggregationResult;
24+
import com.google.cloud.datastore.Datastore;
25+
import com.google.cloud.datastore.DatastoreOptions;
26+
import com.google.cloud.datastore.Entity;
27+
import com.google.cloud.datastore.EntityQuery;
28+
import com.google.cloud.datastore.Key;
29+
import com.google.cloud.datastore.Query;
30+
import com.google.common.collect.Iterables;
31+
32+
public class CountAggregationOnKind {
33+
// Instantiates a client.
34+
private static final Datastore datastore = DatastoreOptions.getDefaultInstance().getService();
35+
36+
// The kind for the new entity.
37+
private static final String kind = "Task";
38+
39+
// Setting up Tasks in database
40+
private static void setUpTasks() {
41+
Key task1Key = datastore.newKeyFactory().setKind(kind).newKey("task1");
42+
Key task2Key = datastore.newKeyFactory().setKind(kind).newKey("task2");
43+
Key task3Key = datastore.newKeyFactory().setKind(kind).newKey("task3");
44+
45+
// Save all the tasks.
46+
datastore.put(
47+
Entity.newBuilder(task1Key).set("done", true).build(),
48+
Entity.newBuilder(task2Key).set("done", false).build(),
49+
Entity.newBuilder(task3Key).set("done", true).build());
50+
}
51+
52+
// Accessing aggregation result by the generated alias.
53+
private static void usageWithGeneratedAlias() {
54+
EntityQuery selectAllTasks = Query.newEntityQueryBuilder().setKind(kind).build();
55+
// Creating an aggregation query to get the count of all tasks.
56+
AggregationQuery allTasksCountQuery =
57+
Query.newAggregationQueryBuilder().over(selectAllTasks).addAggregation(count()).build();
58+
// Executing aggregation query.
59+
AggregationResult aggregationResult =
60+
Iterables.getOnlyElement(datastore.runAggregation(allTasksCountQuery));
61+
62+
System.out.printf(
63+
"Total tasks (accessible from default alias) is %d",
64+
aggregationResult.get("property_1")); // 3
65+
}
66+
67+
// Accessing aggregation result by the provided custom alias.
68+
private static void usageWithCustomAlias() {
69+
EntityQuery selectAllTasks = Query.newEntityQueryBuilder().setKind(kind).build();
70+
// Creating an aggregation query to get the count of all tasks.
71+
AggregationQuery allTasksCountQuery =
72+
Query.newAggregationQueryBuilder()
73+
.over(selectAllTasks)
74+
// passing 'total_count' as alias in the aggregation query.
75+
.addAggregation(count().as("total_count"))
76+
.build();
77+
// Executing aggregation query.
78+
AggregationResult aggregationResult =
79+
Iterables.getOnlyElement(datastore.runAggregation(allTasksCountQuery));
80+
81+
System.out.printf("Total tasks count is %d", aggregationResult.get("total_count")); // 3
82+
}
83+
84+
public static void invoke() {
85+
setUpTasks();
86+
usageWithGeneratedAlias();
87+
usageWithCustomAlias();
88+
}
89+
}
90+
// [END datastore_count_aggregation_query_on_kind]
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2022 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.datastore.aggregation;
18+
19+
// [START datastore_count_aggregation_query_gql]
20+
import com.google.cloud.datastore.AggregationQuery;
21+
import com.google.cloud.datastore.AggregationResult;
22+
import com.google.cloud.datastore.Datastore;
23+
import com.google.cloud.datastore.DatastoreOptions;
24+
import com.google.cloud.datastore.Entity;
25+
import com.google.cloud.datastore.GqlQuery;
26+
import com.google.cloud.datastore.Key;
27+
import com.google.cloud.datastore.Query;
28+
import com.google.common.collect.Iterables;
29+
30+
public class CountAggregationWithGqlQuery {
31+
32+
public static void invoke() {
33+
// Instantiates a client.
34+
Datastore datastore = DatastoreOptions.getDefaultInstance().getService();
35+
36+
// The kind for the new entity.
37+
String kind = "Task";
38+
39+
Key task1Key = datastore.newKeyFactory().setKind(kind).newKey("task1");
40+
Key task2Key = datastore.newKeyFactory().setKind(kind).newKey("task2");
41+
Key task3Key = datastore.newKeyFactory().setKind(kind).newKey("task3");
42+
43+
// Save all the tasks.
44+
datastore.put(
45+
Entity.newBuilder(task1Key).set("done", true).build(),
46+
Entity.newBuilder(task2Key).set("done", false).build(),
47+
Entity.newBuilder(task3Key).set("done", true).build());
48+
49+
// Create a GQL query to get the count of all tasks.
50+
GqlQuery<?> selectAllTasks =
51+
Query.newGqlQueryBuilder(
52+
"AGGREGATE COUNT(*) AS total_count, COUNT_UP_TO(2) AS count_with_limit "
53+
+ "OVER (SELECT * FROM Task)")
54+
.setAllowLiteral(true)
55+
.build();
56+
// Create the aggregation query builder and set the query.
57+
AggregationQuery allTasksCountQuery =
58+
Query.newAggregationQueryBuilder().over(selectAllTasks).build();
59+
// Executing aggregation query.
60+
AggregationResult allTasksCountQueryResult =
61+
Iterables.getOnlyElement(datastore.runAggregation(allTasksCountQuery));
62+
63+
System.out.printf(
64+
"We have at least %d tasks", allTasksCountQueryResult.get("count_with_limit")); // 2
65+
System.out.printf("Total tasks count is %d", allTasksCountQueryResult.get("total_count")); // 3
66+
67+
// Create a query to get the count of all completed tasks.
68+
GqlQuery<?> completedTasks =
69+
Query.newGqlQueryBuilder(
70+
"AGGREGATE COUNT(*) AS total_completed_count "
71+
+ "OVER (SELECT * FROM Task WHERE done = true)")
72+
.setAllowLiteral(true)
73+
.build();
74+
// Create the aggregation query builder and set the query.
75+
AggregationQuery completedTasksCountQuery =
76+
Query.newAggregationQueryBuilder().over(completedTasks).build();
77+
78+
// Executing aggregation query.
79+
AggregationResult completedTasksCountQueryResult =
80+
Iterables.getOnlyElement(datastore.runAggregation(completedTasksCountQuery));
81+
82+
System.out.printf(
83+
"Total completed tasks count is %d",
84+
completedTasksCountQueryResult.get("total_completed_count")); // 2
85+
}
86+
}
87+
// [END datastore_count_aggregation_query_gql]
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2022 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.datastore.aggregation;
18+
19+
// [START datastore_count_aggregation_query_with_limit]
20+
21+
import static com.google.cloud.datastore.aggregation.Aggregation.count;
22+
23+
import com.google.cloud.datastore.AggregationQuery;
24+
import com.google.cloud.datastore.AggregationResult;
25+
import com.google.cloud.datastore.Datastore;
26+
import com.google.cloud.datastore.DatastoreOptions;
27+
import com.google.cloud.datastore.Entity;
28+
import com.google.cloud.datastore.EntityQuery;
29+
import com.google.cloud.datastore.Key;
30+
import com.google.cloud.datastore.Query;
31+
import com.google.common.collect.Iterables;
32+
33+
public class CountAggregationWithLimit {
34+
public static void invoke() {
35+
// Instantiates a client.
36+
Datastore datastore = DatastoreOptions.getDefaultInstance().getService();
37+
38+
// The kind for the new entity.
39+
String kind = "Task";
40+
41+
Key task1Key = datastore.newKeyFactory().setKind(kind).newKey("task1");
42+
Key task2Key = datastore.newKeyFactory().setKind(kind).newKey("task2");
43+
Key task3Key = datastore.newKeyFactory().setKind(kind).newKey("task3");
44+
45+
// Save all the tasks.
46+
datastore.put(
47+
Entity.newBuilder(task1Key).set("done", true).build(),
48+
Entity.newBuilder(task2Key).set("done", false).build(),
49+
Entity.newBuilder(task3Key).set("done", true).build());
50+
51+
EntityQuery selectAllTasks = Query.newEntityQueryBuilder().setKind(kind).setLimit(2).build();
52+
// Creating an aggregation query to get the count of all tasks.
53+
AggregationQuery allTasksCountQuery =
54+
Query.newAggregationQueryBuilder()
55+
.over(selectAllTasks)
56+
.addAggregation(count().as("at_least"))
57+
.build();
58+
// Executing aggregation query.
59+
AggregationResult limitQueryResult =
60+
Iterables.getOnlyElement(datastore.runAggregation(allTasksCountQuery));
61+
62+
System.out.printf("We have at least %d tasks", limitQueryResult.get("at_least")); // 2
63+
}
64+
}
65+
// [END datastore_count_aggregation_query_with_limit]

0 commit comments

Comments
 (0)