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
;
18 import java
.util
.concurrent
.Future
;
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
;
36 public QueryResultsSource
runQuery(FetchOptions fetchOptions
, Query query
, Transaction txn
) {
37 RunQueryRequest
.Builder queryBldr
= toV1Query(query
, fetchOptions
);
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
));
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));
172 private static PropertyFilter
.Operator
toV1PropertyFilterOperator(FilterOperator operator
) {
175 return PropertyFilter
.Operator
.LESS_THAN
;
176 case LESS_THAN_OR_EQUAL
:
177 return PropertyFilter
.Operator
.LESS_THAN_OR_EQUAL
;
179 return PropertyFilter
.Operator
.GREATER_THAN
;
180 case GREATER_THAN_OR_EQUAL
:
181 return PropertyFilter
.Operator
.GREATER_THAN_OR_EQUAL
;
183 return PropertyFilter
.Operator
.EQUAL
;
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
) {
198 return PropertyOrder
.Direction
.ASCENDING
;
200 return PropertyOrder
.Direction
.DESCENDING
;
202 throw new IllegalArgumentException("direction: " + direction
);