Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / datastore / QueryTranslator.java
blobbc03a841773350d6db4f3291c92c0e360699b1df
1 package com.google.appengine.api.datastore;
3 import static com.google.common.base.Preconditions.checkArgument;
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.PropertyValue;
11 import com.google.storage.onestore.v3.OnestoreEntity.Reference;
13 import java.util.List;
15 /**
16 * {@code QueryTranslator} contains the logic to translate a {@code
17 * Query} into the protocol buffers that are used to pass it to the
18 * 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.SortPredicate> sortPredicates = query.getSortPredicates();
26 DatastoreV3Pb.Query proto = new DatastoreV3Pb.Query();
28 if (query.getKind() != null) {
29 proto.setKind(query.getKind());
32 proto.setApp(query.getAppIdNamespace().getAppId());
33 String nameSpace = query.getAppIdNamespace().getNamespace();
34 if (nameSpace.length() != 0) {
35 proto.setNameSpace(nameSpace);
38 if (fetchOptions.getOffset() != null) {
39 proto.setOffset(fetchOptions.getOffset());
42 if (fetchOptions.getLimit() != null) {
43 proto.setLimit(fetchOptions.getLimit());
46 if (fetchOptions.getPrefetchSize() != null) {
47 proto.setCount(fetchOptions.getPrefetchSize());
48 } else if (fetchOptions.getChunkSize() != null) {
49 proto.setCount(fetchOptions.getChunkSize());
52 if (fetchOptions.getStartCursor() != null) {
53 if (!proto.getMutableCompiledCursor().parseFrom(
54 fetchOptions.getStartCursor().toByteString())) {
55 throw new IllegalArgumentException("Invalid cursor");
59 if (fetchOptions.getEndCursor() != null) {
60 if (!proto.getMutableEndCompiledCursor().parseFrom(
61 fetchOptions.getEndCursor().toByteString())) {
62 throw new IllegalArgumentException("Invalid cursor");
66 if (fetchOptions.getCompile() != null) {
67 proto.setCompile(fetchOptions.getCompile());
70 if (ancestor != null) {
71 Reference ref = KeyTranslator.convertToPb(ancestor);
72 if (!ref.getApp().equals(proto.getApp())) {
73 throw new IllegalArgumentException("Query and ancestor appid/namespace mismatch");
75 proto.setAncestor(ref);
78 if (query.getDistinct()) {
79 if (query.getProjections().isEmpty()) {
80 throw new IllegalArgumentException("Projected properties must be set to " +
81 "allow for distinct projections");
83 for (Projection projection : query.getProjections()) {
84 proto.addGroupByPropertyName(projection.getPropertyName());
88 proto.setKeysOnly(query.isKeysOnly());
90 Query.Filter filter = query.getFilter();
91 if (filter != null) {
92 copyGeoFilterToPb(filter, proto);
93 } else {
94 for (Query.FilterPredicate filterPredicate : query.getFilterPredicates()) {
95 Filter filterPb = proto.addFilter();
96 filterPb.copyFrom(convertFilterPredicateToPb(filterPredicate));
100 for (Query.SortPredicate sortPredicate : sortPredicates) {
101 Order order = proto.addOrder();
102 order.copyFrom(convertSortPredicateToPb(sortPredicate));
105 for (Projection projection : query.getProjections()) {
106 proto.addPropertyName(projection.getPropertyName());
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);
131 * Converts the filter from a geo-spatial query into proto-buf form.
132 * Should only be called when the filter indeed has a geo-spatial
133 * term; but the filter as a whole has not yet been entirely
134 * validated, so we complete the validation here.
136 private static void copyGeoFilterToPb(Query.Filter filter, DatastoreV3Pb.Query proto) {
137 if (filter instanceof Query.CompositeFilter) {
138 Query.CompositeFilter conjunction = (Query.CompositeFilter) filter;
139 checkArgument(conjunction.getOperator() == Query.CompositeFilterOperator.AND,
140 "Geo-spatial filters may only be composed with CompositeFilterOperator.AND");
141 for (Query.Filter f : conjunction.getSubFilters()) {
142 copyGeoFilterToPb(f, proto);
144 } else if (filter instanceof Query.StContainsFilter) {
145 Query.StContainsFilter containmentFilter = (Query.StContainsFilter) filter;
146 Filter f = proto.addFilter();
147 f.setOp(Operator.CONTAINED_IN_REGION);
148 f.setGeoRegion(convertGeoRegionToPb(containmentFilter.getRegion()));
149 f.addProperty()
150 .setName(containmentFilter.getPropertyName())
151 .setMultiple(false)
152 .setValue(new PropertyValue());
153 } else {
154 checkArgument(filter instanceof Query.FilterPredicate);
155 Query.FilterPredicate predicate = (Query.FilterPredicate) filter;
156 checkArgument(predicate.getOperator() == Query.FilterOperator.EQUAL,
157 "Geo-spatial filters may only be combined with equality comparisons");
158 Filter f = proto.addFilter();
159 f.copyFrom(convertFilterPredicateToPb(predicate));
163 private static Filter convertFilterPredicateToPb(Query.FilterPredicate predicate) {
164 Filter filterPb = new Filter();
165 filterPb.setOp(getFilterOp(predicate.getOperator()));
167 if (predicate.getValue() instanceof Iterable<?>) {
168 if (predicate.getOperator() != Query.FilterOperator.IN) {
169 throw new IllegalArgumentException("Only the IN operator supports multiple values");
171 for (Object value : (Iterable<?>) predicate.getValue()) {
172 filterPb.addProperty()
173 .setName(predicate.getPropertyName())
174 .setValue(DataTypeTranslator.toV3Value(value));
176 } else {
177 filterPb.addProperty()
178 .setName(predicate.getPropertyName())
179 .setValue(DataTypeTranslator.toV3Value(predicate.getValue()));
182 return filterPb;
185 private static DatastoreV3Pb.GeoRegion convertGeoRegionToPb(Query.GeoRegion region) {
186 DatastoreV3Pb.GeoRegion geoRegion = new DatastoreV3Pb.GeoRegion();
187 if (region instanceof Query.GeoRegion.Circle) {
188 Query.GeoRegion.Circle circle = (Query.GeoRegion.Circle) region;
189 DatastoreV3Pb.CircleRegion circlePb = new DatastoreV3Pb.CircleRegion();
190 circlePb.setCenter(convertGeoPtToPb(circle.getCenter()));
191 circlePb.setRadiusMeters(circle.getRadius());
192 geoRegion.setCircle(circlePb);
193 } else if (region instanceof Query.GeoRegion.Rectangle) {
194 Query.GeoRegion.Rectangle rect = (Query.GeoRegion.Rectangle) region;
195 DatastoreV3Pb.RectangleRegion rectPb = new DatastoreV3Pb.RectangleRegion();
196 rectPb.setSouthwest(convertGeoPtToPb(rect.getSouthwest()));
197 rectPb.setNortheast(convertGeoPtToPb(rect.getNortheast()));
198 geoRegion.setRectangle(rectPb);
199 } else {
200 throw new IllegalArgumentException("missing or unknown-type region in StContainsFilter");
202 return geoRegion;
205 private static DatastoreV3Pb.RegionPoint convertGeoPtToPb(GeoPt point) {
206 DatastoreV3Pb.RegionPoint pointPb = new DatastoreV3Pb.RegionPoint();
207 pointPb.setLatitude(point.getLatitude());
208 pointPb.setLongitude(point.getLongitude());
209 return pointPb;
212 private static Operator getFilterOp(Query.FilterOperator operator) {
213 switch (operator) {
214 case LESS_THAN:
215 return Operator.LESS_THAN;
216 case LESS_THAN_OR_EQUAL:
217 return Operator.LESS_THAN_OR_EQUAL;
218 case GREATER_THAN:
219 return Operator.GREATER_THAN;
220 case GREATER_THAN_OR_EQUAL:
221 return Operator.GREATER_THAN_OR_EQUAL;
222 case EQUAL:
223 return Operator.EQUAL;
224 case IN:
225 return Operator.IN;
226 default:
227 throw new UnsupportedOperationException("operator: " + operator);
231 private QueryTranslator() {