1 package com
.google
.appengine
.api
.datastore
;
3 import com
.google
.appengine
.api
.datastore
.DatastoreServiceConfig
.ApiVersion
;
4 import com
.google
.appengine
.api
.datastore
.Query
.FilterOperator
;
5 import com
.google
.appengine
.api
.datastore
.ReadPolicy
.Consistency
;
6 import com
.google
.apphosting
.datastore
.DatastoreV4
;
7 import com
.google
.apphosting
.datastore
.DatastoreV4
.CompositeFilter
;
8 import com
.google
.apphosting
.datastore
.DatastoreV4
.PropertyExpression
;
9 import com
.google
.apphosting
.datastore
.DatastoreV4
.PropertyFilter
;
10 import com
.google
.apphosting
.datastore
.DatastoreV4
.PropertyOrder
;
11 import com
.google
.apphosting
.datastore
.DatastoreV4
.PropertyReference
;
12 import com
.google
.apphosting
.datastore
.DatastoreV4
.ReadOptions
.ReadConsistency
;
13 import com
.google
.apphosting
.datastore
.DatastoreV4
.RunQueryRequest
;
14 import com
.google
.apphosting
.datastore
.DatastoreV4
.RunQueryResponse
;
15 import com
.google
.apphosting
.datastore
.EntityV4
;
16 import com
.google
.apphosting
.datastore
.EntityV4
.PartitionId
;
17 import com
.google
.common
.base
.Preconditions
;
18 import com
.google
.common
.collect
.Sets
;
20 import java
.util
.Collection
;
22 import java
.util
.concurrent
.Future
;
25 * V4 service specific code for constructing and sending queries.
26 * This class is threadsafe and has no state.
28 final class QueryRunnerV4
implements QueryRunner
{
30 private final DatastoreServiceConfig datastoreServiceConfig
;
31 private final DatastoreV4Proxy datastoreProxy
;
33 QueryRunnerV4(DatastoreServiceConfig datastoreServiceConfig
, DatastoreV4Proxy datastoreProxy
) {
34 this.datastoreServiceConfig
= datastoreServiceConfig
;
35 this.datastoreProxy
= datastoreProxy
;
39 public QueryResultsSource
runQuery(FetchOptions fetchOptions
, Query query
, Transaction txn
) {
41 RunQueryRequest
.Builder queryBldr
= toV4Query(query
, fetchOptions
);
43 TransactionImpl
.ensureTxnActive(txn
);
44 queryBldr
.getReadOptionsBuilder()
45 .setTransaction(InternalTransactionV4
.getById(txn
.getId()).getHandle());
46 } else if (datastoreServiceConfig
.getReadPolicy().getConsistency() == Consistency
.EVENTUAL
) {
47 queryBldr
.getReadOptionsBuilder().setReadConsistency(ReadConsistency
.EVENTUAL
);
50 RunQueryRequest request
= queryBldr
.build();
51 Future
<RunQueryResponse
> result
= datastoreProxy
.runQuery(request
);
53 if (datastoreServiceConfig
.getApiVersion() == ApiVersion
.CLOUD_DATASTORE
) {
54 return new QueryResultsSourceCloudDatastore(datastoreServiceConfig
.getDatastoreCallbacks(),
55 fetchOptions
, txn
, query
, request
, result
, datastoreProxy
);
57 return new QueryResultsSourceV4(datastoreServiceConfig
.getDatastoreCallbacks(),
58 fetchOptions
, txn
, query
, result
, datastoreProxy
);
62 static RunQueryRequest
.Builder
toV4Query(Query query
, FetchOptions fetchOptions
) {
64 Preconditions
.checkArgument(query
.getFullTextSearch() == null, "full-text search unsupported");
66 Preconditions
.checkArgument(query
.getFilter() == null);
68 RunQueryRequest
.Builder requestBldr
= RunQueryRequest
.newBuilder();
70 if (fetchOptions
.getChunkSize() != null) {
71 requestBldr
.setSuggestedBatchSize(fetchOptions
.getChunkSize());
72 } else if (fetchOptions
.getPrefetchSize() != null) {
73 requestBldr
.setSuggestedBatchSize(fetchOptions
.getPrefetchSize());
76 PartitionId
.Builder partitionId
= requestBldr
.getPartitionIdBuilder()
77 .setDatasetId(query
.getAppId());
78 if (!query
.getNamespace().isEmpty()) {
79 partitionId
.setNamespace(query
.getNamespace());
82 DatastoreV4
.Query
.Builder queryBldr
= requestBldr
.getQueryBuilder();
84 if (query
.getKind() != null) {
85 queryBldr
.addKindBuilder().setName(query
.getKind());
88 if (fetchOptions
.getOffset() != null) {
89 queryBldr
.setOffset(fetchOptions
.getOffset());
92 if (fetchOptions
.getLimit() != null) {
93 queryBldr
.setLimit(fetchOptions
.getLimit());
96 if (fetchOptions
.getStartCursor() != null) {
97 queryBldr
.setStartCursor(fetchOptions
.getStartCursor().toByteString());
100 if (fetchOptions
.getEndCursor() != null) {
101 queryBldr
.setEndCursor(fetchOptions
.getEndCursor().toByteString());
104 Set
<String
> groupByProperties
= Sets
.newHashSet();
105 if (query
.getDistinct()) {
106 if (query
.getProjections().isEmpty()) {
107 throw new IllegalArgumentException(
108 "Projected properties must be set to allow for distinct projections");
110 for (Projection projection
: query
.getProjections()) {
111 String name
= projection
.getPropertyName();
112 groupByProperties
.add(name
);
113 queryBldr
.addGroupByBuilder().setName(name
);
117 for (Projection projection
: query
.getProjections()) {
118 String name
= projection
.getPropertyName();
119 PropertyExpression
.Builder projBuilder
= queryBldr
.addProjectionBuilder();
120 projBuilder
.getPropertyBuilder().setName(name
);
121 if (!groupByProperties
.isEmpty() && !groupByProperties
.contains(name
)) {
122 projBuilder
.setAggregationFunction(PropertyExpression
.AggregationFunction
.FIRST
);
126 if (query
.isKeysOnly()) {
127 PropertyExpression
.Builder projBuilder
= queryBldr
.addProjectionBuilder();
128 projBuilder
.getPropertyBuilder().setName(Entity
.KEY_RESERVED_PROPERTY
);
129 if (!groupByProperties
.isEmpty()
130 && !groupByProperties
.contains(Entity
.KEY_RESERVED_PROPERTY
)) {
131 projBuilder
.setAggregationFunction(PropertyExpression
.AggregationFunction
.FIRST
);
135 CompositeFilter
.Builder compositeFilter
= CompositeFilter
.newBuilder();
136 if (query
.getAncestor() != null) {
137 compositeFilter
.addFilterBuilder().getPropertyFilterBuilder()
138 .setOperator(PropertyFilter
.Operator
.HAS_ANCESTOR
)
139 .setProperty(PropertyReference
.newBuilder().setName(Entity
.KEY_RESERVED_PROPERTY
))
140 .setValue(EntityV4
.Value
.newBuilder()
141 .setKeyValue(DataTypeTranslator
.toV4Key(query
.getAncestor())));
143 for (Query
.FilterPredicate filterPredicate
: query
.getFilterPredicates()) {
144 compositeFilter
.addFilterBuilder().setPropertyFilter(toV4PropertyFilter(filterPredicate
));
146 if (compositeFilter
.getFilterCount() == 1) {
147 queryBldr
.setFilter(compositeFilter
.getFilter(0));
148 } else if (compositeFilter
.getFilterCount() > 1) {
149 queryBldr
.getFilterBuilder()
150 .setCompositeFilter(compositeFilter
.setOperator(CompositeFilter
.Operator
.AND
));
153 for (Query
.SortPredicate sortPredicate
: query
.getSortPredicates()) {
154 queryBldr
.addOrder(toV4PropertyOrder(sortPredicate
));
160 private static PropertyFilter
.Builder
toV4PropertyFilter(Query
.FilterPredicate predicate
) {
161 PropertyFilter
.Builder filter
= PropertyFilter
.newBuilder();
162 FilterOperator operator
= predicate
.getOperator();
163 Object value
= predicate
.getValue();
164 if (operator
== Query
.FilterOperator
.IN
) {
165 if (!(predicate
.getValue() instanceof Collection
<?
>)) {
166 throw new IllegalArgumentException("IN filter value is not a Collection.");
168 Collection
<?
> valueCollection
= (Collection
<?
>) value
;
169 if (valueCollection
.size() != 1) {
170 throw new IllegalArgumentException("This service only supports 1 object for IN.");
172 operator
= Query
.FilterOperator
.EQUAL
;
173 value
= valueCollection
.iterator().next();
175 filter
.setOperator(toV4PropertyFilterOperator(operator
));
176 filter
.getPropertyBuilder().setName(predicate
.getPropertyName());
177 filter
.setValue(DataTypeTranslator
.toV4Value(value
, true));
182 private static PropertyFilter
.Operator
toV4PropertyFilterOperator(FilterOperator operator
) {
185 return PropertyFilter
.Operator
.LESS_THAN
;
186 case LESS_THAN_OR_EQUAL
:
187 return PropertyFilter
.Operator
.LESS_THAN_OR_EQUAL
;
189 return PropertyFilter
.Operator
.GREATER_THAN
;
190 case GREATER_THAN_OR_EQUAL
:
191 return PropertyFilter
.Operator
.GREATER_THAN_OR_EQUAL
;
193 return PropertyFilter
.Operator
.EQUAL
;
195 throw new IllegalArgumentException("Can't convert: " + operator
);
199 private static PropertyOrder
.Builder
toV4PropertyOrder(Query
.SortPredicate predicate
) {
200 return PropertyOrder
.newBuilder()
201 .setProperty(PropertyReference
.newBuilder().setName(predicate
.getPropertyName()))
202 .setDirection(toV4PropertyOrderDirection(predicate
.getDirection()));
205 private static PropertyOrder
.Direction
toV4PropertyOrderDirection(Query
.SortDirection direction
) {
208 return PropertyOrder
.Direction
.ASCENDING
;
210 return PropertyOrder
.Direction
.DESCENDING
;
212 throw new IllegalArgumentException("direction: " + direction
);