Skip to content

Commit 91d11f9

Browse files
committed
Implement aggregation queries
1 parent c7492bc commit 91d11f9

5 files changed

Lines changed: 125 additions & 19 deletions

File tree

src/main/java/com/googlecode/objectify/cmd/SimpleQuery.java

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
package com.googlecode.objectify.cmd;
22

3+
import com.google.cloud.datastore.AggregationQuery;
4+
import com.google.cloud.datastore.AggregationResult;
5+
import com.google.cloud.datastore.AggregationResults;
36
import com.google.cloud.datastore.Cursor;
7+
import com.google.cloud.datastore.Query;
8+
import com.google.cloud.datastore.aggregation.Aggregation;
9+
import com.google.cloud.datastore.aggregation.AggregationBuilder;
10+
import com.google.common.collect.Iterables;
11+
12+
import static com.google.cloud.datastore.aggregation.Aggregation.count;
413

514

615
/**
@@ -209,10 +218,57 @@ public interface SimpleQuery<T> extends QueryExecute<T>
209218
QueryKeys<T> keys();
210219

211220
/**
212-
* <p>Count the total number of values in the result. <em>limit</em> and <em>offset</em> are obeyed.</p>
213-
* <p>Runs a server-side aggregation query.</p>
221+
* <p>Run the specified aggregations given the query setup as currently defined. <em>limit</em> and <em>offset</em> are obeyed.</p>
222+
*
223+
* @see <a href="https://cloud.google.com/datastore/docs/aggregation-queries">Google's Aggregation Query Documentation</a>
224+
*/
225+
AggregationResult aggregate(final Aggregation... aggregations);
226+
227+
/**
228+
* <p>Run the specified aggregations given the query setup as currently defined. <em>limit</em> and <em>offset</em> are obeyed.</p>
229+
*
230+
* @see <a href="https://cloud.google.com/datastore/docs/aggregation-queries">Google's Aggregation Query Documentation</a>
231+
*/
232+
AggregationResult aggregate(final AggregationBuilder<?>... aggregations);
233+
234+
/**
235+
* <p>Count the total number of values in the result.</p>
236+
*
237+
* <p>Shorthand for {@code aggregate(Aggregation.count().as("count")).getLong("count")}.</p>
238+
*
239+
* <p>This method should return {@code long}, but to preserve backwards compatibility it returns int.
240+
* This may change in the future.</p>
241+
*
242+
* @see <a href="https://cloud.google.com/datastore/docs/aggregation-queries#behavior_and_limitations">Aggregation Query Behavior and Limitations</a>
243+
*/
244+
default int count() {
245+
final AggregationResult result = aggregate(Aggregation.count().as("count"));
246+
return result.getLong("count").intValue();
247+
}
248+
249+
/**
250+
* <p>Sum the values of the specified property over the specified query.</p>
251+
*
252+
* <p>Shorthand for {@code aggregate(Aggregation.sum().as("value")).getDouble("value")}.</p>
253+
*
254+
* @see <a href="https://cloud.google.com/datastore/docs/aggregation-queries#behavior_and_limitations">Aggregation Query Behavior and Limitations</a>
255+
*/
256+
default double sum(final String property) {
257+
final AggregationResult result = aggregate(Aggregation.sum(property).as("sum"));
258+
return result.getDouble("sum");
259+
}
260+
261+
/**
262+
* <p>Average the values of the specified property over the specified query.</p>
263+
*
264+
* <p>Shorthand for {@code aggregate(Aggregation.avg().as("value")).getDouble("value")}.</p>
265+
*
266+
* @see <a href="https://cloud.google.com/datastore/docs/aggregation-queries#behavior_and_limitations">Aggregation Query Behavior and Limitations</a>
214267
*/
215-
int count();
268+
default double avg(final String property) {
269+
final AggregationResult result = aggregate(Aggregation.avg(property).as("avg"));
270+
return result.getDouble("avg");
271+
}
216272

217273
/**
218274
* <p>Generates a string that consistently and uniquely specifies this query. There

src/main/java/com/googlecode/objectify/impl/QueryEngine.java

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import com.google.cloud.datastore.Query;
1111
import com.google.cloud.datastore.QueryResults;
1212
import com.google.cloud.datastore.StructuredQuery;
13+
import com.google.cloud.datastore.aggregation.Aggregation;
14+
import com.google.cloud.datastore.aggregation.AggregationBuilder;
1315
import com.google.common.collect.Iterables;
1416
import com.googlecode.objectify.Key;
1517
import lombok.RequiredArgsConstructor;
@@ -84,20 +86,34 @@ public <T> QueryResults<T> queryProjection(final ProjectionEntityQuery query) {
8486
}
8587

8688
/**
87-
* Run a count() aggregation query.
89+
* Run an arbitrary aggregation query.
8890
*/
8991
@SneakyThrows
90-
public int queryCount(final StructuredQuery<?> query) {
91-
log.trace("Starting count query");
92+
public AggregationResult queryAggregations(final StructuredQuery<?> query, final Aggregation... aggregations) {
93+
log.trace("Starting aggregation query");
9294

9395
final AggregationQuery aggQuery = Query.newAggregationQueryBuilder()
9496
.over(query)
95-
.addAggregation(count().as("count"))
97+
.addAggregations(aggregations)
9698
.build();
9799

98100
final AggregationResults results = ds.runAggregation(aggQuery).get();
99-
final AggregationResult result = Iterables.getOnlyElement(results);
100-
final Long value = result.getLong("count");
101-
return value.intValue();
101+
return Iterables.getOnlyElement(results);
102+
}
103+
104+
/**
105+
* Run an arbitrary aggregation query.
106+
*/
107+
@SneakyThrows
108+
public AggregationResult queryAggregations(final StructuredQuery<?> query, final AggregationBuilder<?>... aggregations) {
109+
log.trace("Starting aggregation query");
110+
111+
final AggregationQuery aggQuery = Query.newAggregationQueryBuilder()
112+
.over(query)
113+
.addAggregations(aggregations)
114+
.build();
115+
116+
final AggregationResults results = ds.runAggregation(aggQuery).get();
117+
return Iterables.getOnlyElement(results);
102118
}
103119
}

src/main/java/com/googlecode/objectify/impl/QueryImpl.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.googlecode.objectify.impl;
22

3+
import com.google.cloud.datastore.AggregationResult;
34
import com.google.cloud.datastore.Cursor;
45
import com.google.cloud.datastore.KeyQuery;
56
import com.google.cloud.datastore.QueryResults;
@@ -8,6 +9,8 @@
89
import com.google.cloud.datastore.StructuredQuery.OrderBy;
910
import com.google.cloud.datastore.StructuredQuery.PropertyFilter;
1011
import com.google.cloud.datastore.Value;
12+
import com.google.cloud.datastore.aggregation.Aggregation;
13+
import com.google.cloud.datastore.aggregation.AggregationBuilder;
1114
import com.google.common.base.MoreObjects;
1215
import com.googlecode.objectify.Key;
1316
import com.googlecode.objectify.LoadResult;
@@ -295,12 +298,14 @@ public LoadResult<T> first() {
295298
return new LoadResult<>(null, new IteratorFirstResult<>(it));
296299
}
297300

298-
/* (non-Javadoc)
299-
* @see com.googlecode.objectify.Query#count()
300-
*/
301301
@Override
302-
public int count() {
303-
return loader.createQueryEngine().queryCount(this.actual.newKeyQuery());
302+
public AggregationResult aggregate(final Aggregation... aggregations) {
303+
return loader.createQueryEngine().queryAggregations(this.actual.newKeyQuery(), aggregations);
304+
}
305+
306+
@Override
307+
public AggregationResult aggregate(final AggregationBuilder<?>... aggregations) {
308+
return loader.createQueryEngine().queryAggregations(this.actual.newKeyQuery(), aggregations);
304309
}
305310

306311
/* (non-Javadoc)

src/main/java/com/googlecode/objectify/impl/Queryable.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package com.googlecode.objectify.impl;
22

3+
import com.google.cloud.datastore.AggregationResult;
34
import com.google.cloud.datastore.QueryResults;
5+
import com.google.cloud.datastore.aggregation.Aggregation;
6+
import com.google.cloud.datastore.aggregation.AggregationBuilder;
47
import com.googlecode.objectify.LoadResult;
58
import com.googlecode.objectify.cmd.QueryResultIterable;
69

@@ -40,9 +43,15 @@ public QueryResultIterable<T> iterable() {
4043
}
4144

4245
@Override
43-
public int count() {
46+
public AggregationResult aggregate(final Aggregation... aggregations) {
4447
final QueryImpl<T> q = createQuery();
45-
return q.count();
48+
return q.aggregate(aggregations);
49+
}
50+
51+
@Override
52+
public AggregationResult aggregate(final AggregationBuilder<?>... aggregations) {
53+
final QueryImpl<T> q = createQuery();
54+
return q.aggregate(aggregations);
4655
}
4756

4857
@Override

src/test/java/com/googlecode/objectify/test/QueryTests.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,31 @@ void filteringByAncestor() throws Exception {
211211
*/
212212
@Test
213213
@Disabled
214-
void countWorks() throws Exception {
215-
final int count = ofy().load().type(Trivial.class).count();
214+
void aggregateCount() throws Exception {
215+
final long count = ofy().load().type(Trivial.class).count();
216216
assertThat(count).isEqualTo(2);
217217
}
218218

219+
/**
220+
* TODO: re-enable when EMULATOR supports aggregation queries
221+
*/
222+
@Test
223+
@Disabled
224+
void aggregateSum() throws Exception {
225+
final double value = ofy().load().type(Trivial.class).sum("someNumber");
226+
assertThat((int)value).isEqualTo(3);
227+
}
228+
229+
/**
230+
* TODO: re-enable when EMULATOR supports aggregation queries
231+
*/
232+
@Test
233+
@Disabled
234+
void aggregateAvg() throws Exception {
235+
final double value = ofy().load().type(Trivial.class).avg("someNumber");
236+
assertThat(value).isEqualTo(1.5);
237+
}
238+
219239
/** */
220240
@Test
221241
void limitWorks() throws Exception {

0 commit comments

Comments
 (0)