Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / datastore / QueryRunnerCloudDatastoreV1.java
blob44607442267991be1e239799edf4acf715e9fd86
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.common.collect.Sets;
6 import com.google.datastore.v1beta3.CompositeFilter;
7 import com.google.datastore.v1beta3.PartitionId;
8 import com.google.datastore.v1beta3.PropertyFilter;
9 import com.google.datastore.v1beta3.PropertyOrder;
10 import com.google.datastore.v1beta3.PropertyReference;
11 import com.google.datastore.v1beta3.ReadOptions.ReadConsistency;
12 import com.google.datastore.v1beta3.RunQueryRequest;
13 import com.google.datastore.v1beta3.RunQueryResponse;
14 import com.google.datastore.v1beta3.Value;
16 import java.util.Collection;
17 import java.util.Set;
18 import java.util.concurrent.Future;
20 /**
21 * Cloud Datastore v1-service specific code for constructing and sending queries.
22 * This class is threadsafe and has no state.
24 final class QueryRunnerCloudDatastoreV1 implements QueryRunner {
26 private final DatastoreServiceConfig datastoreServiceConfig;
27 private final CloudDatastoreV1Proxy datastoreProxy;
29 QueryRunnerCloudDatastoreV1(DatastoreServiceConfig datastoreServiceConfig,
30 CloudDatastoreV1Proxy datastoreProxy) {
31 this.datastoreServiceConfig = datastoreServiceConfig;
32 this.datastoreProxy = datastoreProxy;
35 @Override
36 public QueryResultsSource runQuery(FetchOptions fetchOptions, Query query, Transaction txn) {
37 RunQueryRequest.Builder queryBldr = toV1Query(query, fetchOptions);
38 if (txn != null) {
39 TransactionImpl.ensureTxnActive(txn);
40 queryBldr.getReadOptionsBuilder()
41 .setTransaction(InternalTransactionCloudDatastoreV1.getById(txn.getId()).getHandle());
42 } else if (datastoreServiceConfig.getReadPolicy().getConsistency() == Consistency.EVENTUAL) {
43 queryBldr.getReadOptionsBuilder().setReadConsistency(ReadConsistency.EVENTUAL);
46 RunQueryRequest request = queryBldr.build();
47 Future<RunQueryResponse> result = datastoreProxy.runQuery(request);
49 return new QueryResultsSourceCloudDatastoreV1(datastoreServiceConfig.getDatastoreCallbacks(),
50 fetchOptions, txn, query, request, result, datastoreProxy);
53 static RunQueryRequest.Builder toV1Query(Query query, FetchOptions fetchOptions) {
55 if (query.getFilter() != null) {
56 throw new IllegalArgumentException(
57 "Geo-spatial queries are not supported in the v1 protocol.");
60 RunQueryRequest.Builder requestBldr = RunQueryRequest.newBuilder();
62 PartitionId.Builder partitionId = requestBldr.getPartitionIdBuilder()
63 .setProjectId(DatastoreApiHelper.toProjectId(query.getAppId()));
64 if (!query.getNamespace().isEmpty()) {
65 partitionId.setNamespaceId(query.getNamespace());
68 com.google.datastore.v1beta3.Query.Builder queryBldr = requestBldr.getQueryBuilder();
70 if (query.getKind() != null) {
71 queryBldr.addKindBuilder().setName(query.getKind());
74 if (fetchOptions.getOffset() != null) {
75 queryBldr.setOffset(fetchOptions.getOffset());
78 if (fetchOptions.getLimit() != null) {
79 queryBldr.getLimitBuilder().setValue(fetchOptions.getLimit());
82 if (fetchOptions.getStartCursor() != null) {
83 queryBldr.setStartCursor(fetchOptions.getStartCursor().toByteString());
86 if (fetchOptions.getEndCursor() != null) {
87 queryBldr.setEndCursor(fetchOptions.getEndCursor().toByteString());
90 Set<String> groupByProperties = Sets.newHashSet();
91 if (query.getDistinct()) {
92 if (query.getProjections().isEmpty()) {
93 throw new IllegalArgumentException(
94 "Projected properties must be set to allow for distinct projections");
96 for (Projection projection : query.getProjections()) {
97 String name = projection.getPropertyName();
98 groupByProperties.add(name);
99 queryBldr.addDistinctOnBuilder().setName(name);
103 if (query.isKeysOnly() && !query.getProjections().isEmpty()) {
104 throw new IllegalArgumentException(
105 "A query cannot have both projections and keys-only set.");
108 for (Projection projection : query.getProjections()) {
109 String name = projection.getPropertyName();
110 if (Entity.KEY_RESERVED_PROPERTY.equals(name)) {
111 throw new IllegalArgumentException(
112 "projections are not supported for the property: __key__");
114 com.google.datastore.v1beta3.Projection.Builder projBuilder =
115 queryBldr.addProjectionBuilder();
116 projBuilder.getPropertyBuilder().setName(name);
119 if (query.isKeysOnly()) {
120 com.google.datastore.v1beta3.Projection.Builder projBuilder =
121 queryBldr.addProjectionBuilder();
122 projBuilder.getPropertyBuilder().setName(Entity.KEY_RESERVED_PROPERTY);
125 CompositeFilter.Builder compositeFilter = CompositeFilter.newBuilder();
126 if (query.getAncestor() != null) {
127 compositeFilter.addFiltersBuilder().getPropertyFilterBuilder()
128 .setOp(PropertyFilter.Operator.HAS_ANCESTOR)
129 .setProperty(PropertyReference.newBuilder().setName(Entity.KEY_RESERVED_PROPERTY))
130 .setValue(Value.newBuilder()
131 .setKeyValue(DataTypeTranslator.toV1Key(query.getAncestor())));
133 for (Query.FilterPredicate filterPredicate : query.getFilterPredicates()) {
134 compositeFilter.addFiltersBuilder().setPropertyFilter(toV1PropertyFilter(filterPredicate));
136 if (compositeFilter.getFiltersCount() == 1) {
137 queryBldr.setFilter(compositeFilter.getFilters(0));
138 } else if (compositeFilter.getFiltersCount() > 1) {
139 queryBldr.getFilterBuilder()
140 .setCompositeFilter(compositeFilter.setOp(CompositeFilter.Operator.AND));
143 for (Query.SortPredicate sortPredicate : query.getSortPredicates()) {
144 queryBldr.addOrder(toV1PropertyOrder(sortPredicate));
147 return requestBldr;
150 private static PropertyFilter.Builder toV1PropertyFilter(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.setOp(toV1PropertyFilterOperator(operator));
166 filter.getPropertyBuilder().setName(predicate.getPropertyName());
167 filter.setValue(DataTypeTranslator.toV1Value(value, true, true));
169 return filter;
172 private static PropertyFilter.Operator toV1PropertyFilterOperator(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 toV1PropertyOrder(Query.SortPredicate predicate) {
190 return PropertyOrder.newBuilder()
191 .setProperty(PropertyReference.newBuilder().setName(predicate.getPropertyName()))
192 .setDirection(toV1PropertyOrderDirection(predicate.getDirection()));
195 private static PropertyOrder.Direction toV1PropertyOrderDirection(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);