App Engine Java SDK version 1.9.8
[gae.git] / java / src / main / com / google / appengine / api / datastore / QueryRunnerV4.java
blobc0d416293ec82200919acc98d3f5a273573cfbbd
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;
22 import java.util.Set;
23 import java.util.concurrent.Future;
25 /**
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 {
31 @Override
32 public QueryResultsSource runQuery(ApiConfig apiConfig,
33 DatastoreServiceConfig datastoreServiceConfig, FetchOptions fetchOptions, Query query,
34 Transaction txn) {
36 RunQueryRequest.Builder queryBldr = toV4Query(query, fetchOptions);
37 if (txn != null) {
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));
147 return requestBldr;
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));
169 return filter;
172 private static PropertyFilter.Operator toV4PropertyFilterOperator(FilterOperator operator) {
173 switch (operator) {
174 case LESS_THAN:
175 return PropertyFilter.Operator.LESS_THAN;
176 case LESS_THAN_OR_EQUAL:
177 return PropertyFilter.Operator.LESS_THAN_OR_EQUAL;
178 case GREATER_THAN:
179 return PropertyFilter.Operator.GREATER_THAN;
180 case GREATER_THAN_OR_EQUAL:
181 return PropertyFilter.Operator.GREATER_THAN_OR_EQUAL;
182 case EQUAL:
183 return PropertyFilter.Operator.EQUAL;
184 default:
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) {
196 switch (direction) {
197 case ASCENDING:
198 return PropertyOrder.Direction.ASCENDING;
199 case DESCENDING:
200 return PropertyOrder.Direction.DESCENDING;
201 default:
202 throw new IllegalArgumentException("direction: " + direction);