Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / datastore / QueryTranslator.java
blob5476617ed4cb184f1cc7ce689e7d8f2cba8a6bb3
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;
14 /**
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());
109 return proto;
112 static Order convertSortPredicateToPb(Query.SortPredicate predicate) {
113 Order order = new Order();
114 order.setProperty(predicate.getPropertyName());
115 order.setDirection(getSortOp(predicate.getDirection()));
116 return order;
119 private static Direction getSortOp(Query.SortDirection direction) {
120 switch (direction) {
121 case ASCENDING:
122 return Direction.ASCENDING;
123 case DESCENDING:
124 return Direction.DESCENDING;
125 default:
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()) {
140 filter.addProperty()
141 .setName(predicate.getPropertyName())
142 .setValue(DataTypeTranslator.toV3Value(value));
144 } else {
145 filter.addProperty()
146 .setName(predicate.getPropertyName())
147 .setValue(DataTypeTranslator.toV3Value(predicate.getValue()));
150 return filter;
153 private static Operator getFilterOp(Query.FilterOperator operator) {
154 switch (operator) {
155 case LESS_THAN:
156 return Operator.LESS_THAN;
157 case LESS_THAN_OR_EQUAL:
158 return Operator.LESS_THAN_OR_EQUAL;
159 case GREATER_THAN:
160 return Operator.GREATER_THAN;
161 case GREATER_THAN_OR_EQUAL:
162 return Operator.GREATER_THAN_OR_EQUAL;
163 case EQUAL:
164 return Operator.EQUAL;
165 case IN:
166 return Operator.IN;
167 default:
168 throw new UnsupportedOperationException("operator: " + operator);
172 private QueryTranslator() {