1 package com
.google
.appengine
.api
.datastore
;
3 import com
.google
.appengine
.api
.datastore
.Query
.FilterOperator
;
4 import com
.google
.appengine
.api
.datastore
.ReadPolicy
.Consistency
;
5 import com
.google
.apphosting
.api
.ApiProxy
.ApiConfig
;
6 import com
.google
.apphosting
.datastore
.DatastoreV4
;
7 import com
.google
.apphosting
.datastore
.DatastoreV4
.CompositeFilter
;
8 import com
.google
.apphosting
.datastore
.DatastoreV4
.DatastoreV4Service
.Method
;
9 import com
.google
.apphosting
.datastore
.DatastoreV4
.PropertyExpression
;
10 import com
.google
.apphosting
.datastore
.DatastoreV4
.PropertyFilter
;
11 import com
.google
.apphosting
.datastore
.DatastoreV4
.PropertyOrder
;
12 import com
.google
.apphosting
.datastore
.DatastoreV4
.PropertyReference
;
13 import com
.google
.apphosting
.datastore
.DatastoreV4
.ReadOptions
.ReadConsistency
;
14 import com
.google
.apphosting
.datastore
.DatastoreV4
.RunQueryRequest
;
15 import com
.google
.apphosting
.datastore
.DatastoreV4
.RunQueryResponse
;
16 import com
.google
.apphosting
.datastore
.EntityV4
;
17 import com
.google
.apphosting
.datastore
.EntityV4
.PartitionId
;
18 import com
.google
.common
.base
.Preconditions
;
19 import com
.google
.common
.collect
.Sets
;
21 import java
.util
.Collection
;
23 import java
.util
.concurrent
.Future
;
26 * V4 service specific code for constructing and sending queries.
27 * This class is threadsafe and has no state.
29 final class QueryRunnerV4
implements QueryRunner
{
32 public QueryResultsSource
runQuery(ApiConfig apiConfig
,
33 DatastoreServiceConfig datastoreServiceConfig
, FetchOptions fetchOptions
, Query query
,
36 RunQueryRequest
.Builder queryBldr
= toV4Query(query
, fetchOptions
);
38 TransactionImpl
.ensureTxnActive(txn
);
39 queryBldr
.getReadOptionsBuilder()
40 .setTransaction(InternalTransactionV4
.getById(txn
.getId()).getHandle());
41 } else if (datastoreServiceConfig
.getReadPolicy().getConsistency() == Consistency
.EVENTUAL
) {
42 queryBldr
.getReadOptionsBuilder().setReadConsistency(ReadConsistency
.EVENTUAL
);
45 Future
<RunQueryResponse
> result
= DatastoreApiHelper
.makeAsyncCall(apiConfig
, Method
.RunQuery
,
46 queryBldr
.build(), RunQueryResponse
.getDefaultInstance());
48 return new QueryResultsSourceV4(datastoreServiceConfig
.getDatastoreCallbacks(),
49 fetchOptions
, txn
, query
, result
, apiConfig
);
52 static RunQueryRequest
.Builder
toV4Query(Query query
, FetchOptions fetchOptions
) {
54 Preconditions
.checkArgument(query
.getFullTextSearch() == null, "full-text search unsupported");
56 Preconditions
.checkArgument(query
.getFilter() == null);
58 RunQueryRequest
.Builder requestBldr
= RunQueryRequest
.newBuilder();
60 if (fetchOptions
.getChunkSize() != null) {
61 requestBldr
.setSuggestedBatchSize(fetchOptions
.getChunkSize());
62 } else if (fetchOptions
.getPrefetchSize() != null) {
63 requestBldr
.setSuggestedBatchSize(fetchOptions
.getPrefetchSize());
66 PartitionId
.Builder partitionId
= requestBldr
.getPartitionIdBuilder()
67 .setDatasetId(query
.getAppId());
68 if (!query
.getNamespace().isEmpty()) {
69 partitionId
.setNamespace(query
.getNamespace());
72 DatastoreV4
.Query
.Builder queryBldr
= requestBldr
.getQueryBuilder();
74 if (query
.getKind() != null) {
75 queryBldr
.addKindBuilder().setName(query
.getKind());
78 if (fetchOptions
.getOffset() != null) {
79 queryBldr
.setOffset(fetchOptions
.getOffset());
82 if (fetchOptions
.getLimit() != null) {
83 queryBldr
.setLimit(fetchOptions
.getLimit());
86 if (fetchOptions
.getStartCursor() != null) {
87 queryBldr
.setStartCursor(fetchOptions
.getStartCursor().convertToPb().toByteString());
90 if (fetchOptions
.getEndCursor() != null) {
91 queryBldr
.setEndCursor(fetchOptions
.getEndCursor().convertToPb().toByteString());
94 Set
<String
> groupByProperties
= Sets
.newHashSet();
95 if (query
.getDistinct()) {
96 if (query
.getProjections().isEmpty()) {
97 throw new IllegalArgumentException(
98 "Projected properties must be set to allow for distinct projections");
100 for (Projection projection
: query
.getProjections()) {
101 String name
= projection
.getPropertyName();
102 groupByProperties
.add(name
);
103 queryBldr
.addGroupByBuilder().setName(name
);
107 for (Projection projection
: query
.getProjections()) {
108 String name
= projection
.getPropertyName();
109 PropertyExpression
.Builder projBuilder
= queryBldr
.addProjectionBuilder();
110 projBuilder
.getPropertyBuilder().setName(name
);
111 if (!groupByProperties
.isEmpty() && !groupByProperties
.contains(name
)) {
112 projBuilder
.setAggregationFunction(PropertyExpression
.AggregationFunction
.FIRST
);
116 if (query
.isKeysOnly()) {
117 PropertyExpression
.Builder projBuilder
= queryBldr
.addProjectionBuilder();
118 projBuilder
.getPropertyBuilder().setName(Entity
.KEY_RESERVED_PROPERTY
);
119 if (!groupByProperties
.isEmpty()
120 && !groupByProperties
.contains(Entity
.KEY_RESERVED_PROPERTY
)) {
121 projBuilder
.setAggregationFunction(PropertyExpression
.AggregationFunction
.FIRST
);
125 CompositeFilter
.Builder compositeFilter
= CompositeFilter
.newBuilder();
126 if (query
.getAncestor() != null) {
127 compositeFilter
.addFilterBuilder().getPropertyFilterBuilder()
128 .setOperator(PropertyFilter
.Operator
.HAS_ANCESTOR
)
129 .setProperty(PropertyReference
.newBuilder().setName(Entity
.KEY_RESERVED_PROPERTY
))
130 .setValue(EntityV4
.Value
.newBuilder()
131 .setKeyValue(DataTypeTranslator
.toV4Key(query
.getAncestor())));
133 for (Query
.FilterPredicate filterPredicate
: query
.getFilterPredicates()) {
134 compositeFilter
.addFilterBuilder().setPropertyFilter(toV4PropertyFilter(filterPredicate
));
136 if (compositeFilter
.getFilterCount() == 1) {
137 queryBldr
.setFilter(compositeFilter
.getFilter(0));
138 } else if (compositeFilter
.getFilterCount() > 1) {
139 queryBldr
.getFilterBuilder()
140 .setCompositeFilter(compositeFilter
.setOperator(CompositeFilter
.Operator
.AND
));
143 for (Query
.SortPredicate sortPredicate
: query
.getSortPredicates()) {
144 queryBldr
.addOrder(toV4PropertyOrder(sortPredicate
));
150 private static PropertyFilter
.Builder
toV4PropertyFilter(Query
.FilterPredicate predicate
) {
151 PropertyFilter
.Builder filter
= PropertyFilter
.newBuilder();
152 FilterOperator operator
= predicate
.getOperator();
153 Object value
= predicate
.getValue();
154 if (operator
== Query
.FilterOperator
.IN
) {
155 if (!(predicate
.getValue() instanceof Collection
<?
>)) {
156 throw new IllegalArgumentException("IN filter value is not a Collection.");
158 Collection
<?
> valueCollection
= (Collection
<?
>) value
;
159 if (valueCollection
.size() != 1) {
160 throw new IllegalArgumentException("This service only supports 1 object for IN.");
162 operator
= Query
.FilterOperator
.EQUAL
;
163 value
= valueCollection
.iterator().next();
165 filter
.setOperator(toV4PropertyFilterOperator(operator
));
166 filter
.getPropertyBuilder().setName(predicate
.getPropertyName());
167 filter
.setValue(DataTypeTranslator
.toV4Value(value
, true));
172 private static PropertyFilter
.Operator
toV4PropertyFilterOperator(FilterOperator operator
) {
175 return PropertyFilter
.Operator
.LESS_THAN
;
176 case LESS_THAN_OR_EQUAL
:
177 return PropertyFilter
.Operator
.LESS_THAN_OR_EQUAL
;
179 return PropertyFilter
.Operator
.GREATER_THAN
;
180 case GREATER_THAN_OR_EQUAL
:
181 return PropertyFilter
.Operator
.GREATER_THAN_OR_EQUAL
;
183 return PropertyFilter
.Operator
.EQUAL
;
185 throw new IllegalArgumentException("Can't convert: " + operator
);
189 private static PropertyOrder
.Builder
toV4PropertyOrder(Query
.SortPredicate predicate
) {
190 return PropertyOrder
.newBuilder()
191 .setProperty(PropertyReference
.newBuilder().setName(predicate
.getPropertyName()))
192 .setDirection(toV4PropertyOrderDirection(predicate
.getDirection()));
195 private static PropertyOrder
.Direction
toV4PropertyOrderDirection(Query
.SortDirection direction
) {
198 return PropertyOrder
.Direction
.ASCENDING
;
200 return PropertyOrder
.Direction
.DESCENDING
;
202 throw new IllegalArgumentException("direction: " + direction
);