1 // Copyright 2007 Google Inc. All rights reserved.
3 package com
.google
.appengine
.api
.datastore
;
5 import com
.google
.apphosting
.datastore
.DatastoreV3Pb
;
6 import com
.google
.apphosting
.datastore
.DatastoreV3Pb
.Query
.Filter
;
7 import com
.google
.apphosting
.datastore
.DatastoreV3Pb
.Query
.Filter
.Operator
;
8 import com
.google
.apphosting
.datastore
.DatastoreV3Pb
.Query
.Order
;
9 import com
.google
.apphosting
.datastore
.DatastoreV3Pb
.Query
.Order
.Direction
;
10 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
.Reference
;
12 import java
.util
.List
;
15 * {@code QueryTranslator} contains the logic to translate a {@code
16 * Query} into the protocol buffers that are used to pass it to the
17 * implementation of the API.
20 final class QueryTranslator
{
22 public static DatastoreV3Pb
.Query
convertToPb(Query query
, FetchOptions fetchOptions
) {
23 Key ancestor
= query
.getAncestor();
24 List
<Query
.FilterPredicate
> filterPredicates
= query
.getFilterPredicates();
25 List
<Query
.SortPredicate
> sortPredicates
= query
.getSortPredicates();
27 DatastoreV3Pb
.Query proto
= new DatastoreV3Pb
.Query();
29 if (query
.getKind() != null) {
30 proto
.setKind(query
.getKind());
33 proto
.setApp(query
.getAppIdNamespace().getAppId());
34 String nameSpace
= query
.getAppIdNamespace().getNamespace();
35 if (nameSpace
.length() != 0) {
36 proto
.setNameSpace(nameSpace
);
39 if (fetchOptions
.getOffset() != null) {
40 proto
.setOffset(fetchOptions
.getOffset());
43 if (fetchOptions
.getLimit() != null) {
44 proto
.setLimit(fetchOptions
.getLimit());
47 if (fetchOptions
.getPrefetchSize() != null) {
48 proto
.setCount(fetchOptions
.getPrefetchSize());
49 } else if (fetchOptions
.getChunkSize() != null) {
50 proto
.setCount(fetchOptions
.getChunkSize());
53 if (fetchOptions
.getStartCursor() != null) {
54 if (!proto
.getMutableCompiledCursor().parseFrom(
55 fetchOptions
.getStartCursor().toByteString())) {
56 throw new IllegalArgumentException("Invalid cursor");
60 if (fetchOptions
.getEndCursor() != null) {
61 if (!proto
.getMutableEndCompiledCursor().parseFrom(
62 fetchOptions
.getEndCursor().toByteString())) {
63 throw new IllegalArgumentException("Invalid cursor");
67 if (fetchOptions
.getCompile() != null) {
68 proto
.setCompile(fetchOptions
.getCompile());
71 if (ancestor
!= null) {
72 Reference ref
= KeyTranslator
.convertToPb(ancestor
);
73 if (!ref
.getApp().equals(proto
.getApp())) {
74 throw new IllegalArgumentException("Query and ancestor appid/namespace mismatch");
76 proto
.setAncestor(ref
);
79 if (query
.getDistinct()) {
80 if (query
.getProjections().isEmpty()) {
81 throw new IllegalArgumentException("Projected properties must be set to " +
82 "allow for distinct projections");
84 for (Projection projection
: query
.getProjections()) {
85 proto
.addGroupByPropertyName(projection
.getPropertyName());
89 proto
.setKeysOnly(query
.isKeysOnly());
91 for (Query
.FilterPredicate filterPredicate
: filterPredicates
) {
92 Filter filter
= proto
.addFilter();
93 filter
.copyFrom(convertFilterPredicateToPb(filterPredicate
));
96 for (Query
.SortPredicate sortPredicate
: sortPredicates
) {
97 Order order
= proto
.addOrder();
98 order
.copyFrom(convertSortPredicateToPb(sortPredicate
));
101 for (Projection projection
: query
.getProjections()) {
102 proto
.addPropertyName(projection
.getPropertyName());
105 if (query
.getFullTextSearch() != null) {
106 proto
.setSearchQuery(query
.getFullTextSearch());
112 static Order
convertSortPredicateToPb(Query
.SortPredicate predicate
) {
113 Order order
= new Order();
114 order
.setProperty(predicate
.getPropertyName());
115 order
.setDirection(getSortOp(predicate
.getDirection()));
119 private static Direction
getSortOp(Query
.SortDirection direction
) {
122 return Direction
.ASCENDING
;
124 return Direction
.DESCENDING
;
126 throw new UnsupportedOperationException("direction: " + direction
);
130 private static Filter
convertFilterPredicateToPb(
131 Query
.FilterPredicate predicate
) {
132 Filter filter
= new Filter();
133 filter
.setOp(getFilterOp(predicate
.getOperator()));
135 if (predicate
.getValue() instanceof Iterable
<?
>) {
136 if (predicate
.getOperator() != Query
.FilterOperator
.IN
) {
137 throw new IllegalArgumentException("Only the IN operator supports multiple values");
139 for (Object value
: (Iterable
<?
>) predicate
.getValue()) {
141 .setName(predicate
.getPropertyName())
142 .setValue(DataTypeTranslator
.toV3Value(value
));
146 .setName(predicate
.getPropertyName())
147 .setValue(DataTypeTranslator
.toV3Value(predicate
.getValue()));
153 private static Operator
getFilterOp(Query
.FilterOperator operator
) {
156 return Operator
.LESS_THAN
;
157 case LESS_THAN_OR_EQUAL
:
158 return Operator
.LESS_THAN_OR_EQUAL
;
160 return Operator
.GREATER_THAN
;
161 case GREATER_THAN_OR_EQUAL
:
162 return Operator
.GREATER_THAN_OR_EQUAL
;
164 return Operator
.EQUAL
;
168 throw new UnsupportedOperationException("operator: " + operator
);
172 private QueryTranslator() {