Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / datastore / QueryRunnerCloudDatastoreV1.java
bloba8a54705887d7f3a29393cdbb56f68d9ac7052cc
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 for (Projection projection : query.getProjections()) {
104 String name = projection.getPropertyName();
105 com.google.datastore.v1beta3.Projection.Builder projBuilder =
106 queryBldr.addProjectionBuilder();
107 projBuilder.getPropertyBuilder().setName(name);
110 if (query.isKeysOnly()) {
111 com.google.datastore.v1beta3.Projection.Builder projBuilder =
112 queryBldr.addProjectionBuilder();
113 projBuilder.getPropertyBuilder().setName(Entity.KEY_RESERVED_PROPERTY);
116 CompositeFilter.Builder compositeFilter = CompositeFilter.newBuilder();
117 if (query.getAncestor() != null) {
118 compositeFilter.addFiltersBuilder().getPropertyFilterBuilder()
119 .setOp(PropertyFilter.Operator.HAS_ANCESTOR)
120 .setProperty(PropertyReference.newBuilder().setName(Entity.KEY_RESERVED_PROPERTY))
121 .setValue(Value.newBuilder()
122 .setKeyValue(DataTypeTranslator.toV1Key(query.getAncestor())));
124 for (Query.FilterPredicate filterPredicate : query.getFilterPredicates()) {
125 compositeFilter.addFiltersBuilder().setPropertyFilter(toV1PropertyFilter(filterPredicate));
127 if (compositeFilter.getFiltersCount() == 1) {
128 queryBldr.setFilter(compositeFilter.getFilters(0));
129 } else if (compositeFilter.getFiltersCount() > 1) {
130 queryBldr.getFilterBuilder()
131 .setCompositeFilter(compositeFilter.setOp(CompositeFilter.Operator.AND));
134 for (Query.SortPredicate sortPredicate : query.getSortPredicates()) {
135 queryBldr.addOrder(toV1PropertyOrder(sortPredicate));
138 return requestBldr;
141 private static PropertyFilter.Builder toV1PropertyFilter(Query.FilterPredicate predicate) {
142 PropertyFilter.Builder filter = PropertyFilter.newBuilder();
143 FilterOperator operator = predicate.getOperator();
144 Object value = predicate.getValue();
145 if (operator == Query.FilterOperator.IN) {
146 if (!(predicate.getValue() instanceof Collection<?>)) {
147 throw new IllegalArgumentException("IN filter value is not a Collection.");
149 Collection<?> valueCollection = (Collection<?>) value;
150 if (valueCollection.size() != 1) {
151 throw new IllegalArgumentException("This service only supports 1 object for IN.");
153 operator = Query.FilterOperator.EQUAL;
154 value = valueCollection.iterator().next();
156 filter.setOp(toV1PropertyFilterOperator(operator));
157 filter.getPropertyBuilder().setName(predicate.getPropertyName());
158 filter.setValue(DataTypeTranslator.toV1Value(value, true));
160 return filter;
163 private static PropertyFilter.Operator toV1PropertyFilterOperator(FilterOperator operator) {
164 switch (operator) {
165 case LESS_THAN:
166 return PropertyFilter.Operator.LESS_THAN;
167 case LESS_THAN_OR_EQUAL:
168 return PropertyFilter.Operator.LESS_THAN_OR_EQUAL;
169 case GREATER_THAN:
170 return PropertyFilter.Operator.GREATER_THAN;
171 case GREATER_THAN_OR_EQUAL:
172 return PropertyFilter.Operator.GREATER_THAN_OR_EQUAL;
173 case EQUAL:
174 return PropertyFilter.Operator.EQUAL;
175 default:
176 throw new IllegalArgumentException("Can't convert: " + operator);
180 private static PropertyOrder.Builder toV1PropertyOrder(Query.SortPredicate predicate) {
181 return PropertyOrder.newBuilder()
182 .setProperty(PropertyReference.newBuilder().setName(predicate.getPropertyName()))
183 .setDirection(toV1PropertyOrderDirection(predicate.getDirection()));
186 private static PropertyOrder.Direction toV1PropertyOrderDirection(Query.SortDirection direction) {
187 switch (direction) {
188 case ASCENDING:
189 return PropertyOrder.Direction.ASCENDING;
190 case DESCENDING:
191 return PropertyOrder.Direction.DESCENDING;
192 default:
193 throw new IllegalArgumentException("direction: " + direction);