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 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
));
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));
163 private static PropertyFilter
.Operator
toV1PropertyFilterOperator(FilterOperator operator
) {
166 return PropertyFilter
.Operator
.LESS_THAN
;
167 case LESS_THAN_OR_EQUAL
:
168 return PropertyFilter
.Operator
.LESS_THAN_OR_EQUAL
;
170 return PropertyFilter
.Operator
.GREATER_THAN
;
171 case GREATER_THAN_OR_EQUAL
:
172 return PropertyFilter
.Operator
.GREATER_THAN_OR_EQUAL
;
174 return PropertyFilter
.Operator
.EQUAL
;
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
) {
189 return PropertyOrder
.Direction
.ASCENDING
;
191 return PropertyOrder
.Direction
.DESCENDING
;
193 throw new IllegalArgumentException("direction: " + direction
);