Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / datastore / QueryRunnerV4.java
blob2a9a49c75a340c17454209de38df8957802e2bca
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;
21 import java.util.Set;
22 import java.util.concurrent.Future;
24 /**
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;
38 @Override
39 public QueryResultsSource runQuery(FetchOptions fetchOptions, Query query, Transaction txn) {
41 RunQueryRequest.Builder queryBldr = toV4Query(query, fetchOptions);
42 if (txn != null) {
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);
56 } else {
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));
157 return requestBldr;
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));
179 return filter;
182 private static PropertyFilter.Operator toV4PropertyFilterOperator(FilterOperator operator) {
183 switch (operator) {
184 case LESS_THAN:
185 return PropertyFilter.Operator.LESS_THAN;
186 case LESS_THAN_OR_EQUAL:
187 return PropertyFilter.Operator.LESS_THAN_OR_EQUAL;
188 case GREATER_THAN:
189 return PropertyFilter.Operator.GREATER_THAN;
190 case GREATER_THAN_OR_EQUAL:
191 return PropertyFilter.Operator.GREATER_THAN_OR_EQUAL;
192 case EQUAL:
193 return PropertyFilter.Operator.EQUAL;
194 default:
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) {
206 switch (direction) {
207 case ASCENDING:
208 return PropertyOrder.Direction.ASCENDING;
209 case DESCENDING:
210 return PropertyOrder.Direction.DESCENDING;
211 default:
212 throw new IllegalArgumentException("direction: " + direction);