PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/GqlQuery.java

https://gitlab.com/adam.lukaitis/gcloud-java
Java | 421 lines | 304 code | 60 blank | 57 comment | 33 complexity | f45aa33df47afc7e5c8e49cfdae24812 MD5 | raw file
  1. /*
  2. * Copyright 2015 Google Inc. All Rights Reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.google.gcloud.datastore;
  17. import static com.google.common.base.Preconditions.checkNotNull;
  18. import static com.google.gcloud.datastore.Validator.validateNamespace;
  19. import com.google.api.services.datastore.DatastoreV1;
  20. import com.google.common.base.MoreObjects;
  21. import com.google.common.collect.ImmutableList;
  22. import com.google.common.collect.ImmutableMap;
  23. import com.google.common.collect.ImmutableSortedMap;
  24. import com.google.common.primitives.Booleans;
  25. import com.google.common.primitives.Doubles;
  26. import com.google.common.primitives.Longs;
  27. import com.google.protobuf.InvalidProtocolBufferException;
  28. import java.util.ArrayList;
  29. import java.util.Arrays;
  30. import java.util.LinkedList;
  31. import java.util.List;
  32. import java.util.Map;
  33. import java.util.Objects;
  34. import java.util.TreeMap;
  35. /**
  36. * A Google Cloud Datastore GQL query.
  37. *
  38. * <h3>A usage example:</h3>
  39. *
  40. * <p>When the type of the results is known the preferred usage would be:
  41. * <pre>{@code
  42. * Query<Entity> query =
  43. * Query.gqlQueryBuilder(Query.ResultType.ENTITY, "select * from kind").build();
  44. * QueryResults<Entity> results = datastore.run(query);
  45. * while (results.hasNext()) {
  46. * Entity entity = results.next();
  47. * ...
  48. * }
  49. * } </pre>
  50. *
  51. * <p>When the type of the results is unknown you can use this approach:
  52. * <pre>{@code
  53. * Query<?> query = Query.gqlQueryBuilder("select __key__ from kind").build();
  54. * QueryResults<?> results = datastore.run(query);
  55. * if (Key.class.isAssignableFrom(results.resultClass())) {
  56. * QueryResults<Key> keys = (QueryResults<Key>) results;
  57. * while (keys.hasNext()) {
  58. * Key key = keys.next();
  59. * ...
  60. * }
  61. * }
  62. * } </pre>
  63. *
  64. * @param <V> the type of the result values this query will produce
  65. * @see <a href="https://cloud.google.com/datastore/docs/apis/gql/gql_reference">GQL Reference</a>
  66. */
  67. public final class GqlQuery<V> extends Query<V> {
  68. private static final long serialVersionUID = 5988280590929540569L;
  69. private final transient String queryString;
  70. private final transient boolean allowLiteral;
  71. private final transient ImmutableList<Binding> namedBindings;
  72. private final transient ImmutableList<Binding> positionalBindings;
  73. static final class Binding extends Serializable<DatastoreV1.GqlQueryArg> {
  74. private static final long serialVersionUID = 1976895435257636275L;
  75. private final transient String name;
  76. private final transient Cursor cursor;
  77. private final transient Value<?> value;
  78. Binding(String name, Cursor cursor) {
  79. this.name = name;
  80. this.cursor = checkNotNull(cursor);
  81. value = null;
  82. }
  83. Binding(String name, Value<?> value) {
  84. this.name = name;
  85. this.value = checkNotNull(value);
  86. cursor = null;
  87. }
  88. Object cursorOrValue() {
  89. return MoreObjects.firstNonNull(cursor, value);
  90. }
  91. String name() {
  92. return name;
  93. }
  94. @Override
  95. public int hashCode() {
  96. return Objects.hash(name, cursor, value);
  97. }
  98. @Override
  99. public boolean equals(Object obj) {
  100. if (obj == this) {
  101. return true;
  102. }
  103. if (!(obj instanceof Binding)) {
  104. return false;
  105. }
  106. Binding other = (Binding) obj;
  107. return Objects.equals(name, other.name)
  108. && Objects.equals(cursor, other.cursor)
  109. && Objects.equals(value, other.value);
  110. }
  111. @Override
  112. protected DatastoreV1.GqlQueryArg toPb() {
  113. DatastoreV1.GqlQueryArg.Builder argPb = DatastoreV1.GqlQueryArg.newBuilder();
  114. if (name != null) {
  115. argPb.setName(name);
  116. }
  117. if (cursor != null) {
  118. argPb.setCursor(cursor.byteString());
  119. }
  120. if (value != null) {
  121. argPb.setValue(value.toPb());
  122. }
  123. return argPb.build();
  124. }
  125. @Override
  126. protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException {
  127. return fromPb(DatastoreV1.GqlQueryArg.parseFrom(bytesPb));
  128. }
  129. static Binding fromPb(DatastoreV1.GqlQueryArg argPb) {
  130. String name = argPb.hasName() ? argPb.getName() : null;
  131. if (argPb.hasCursor()) {
  132. return new Binding(name, new Cursor(argPb.getCursor()));
  133. }
  134. return new Binding(name, Value.fromPb(argPb.getValue()));
  135. }
  136. }
  137. /**
  138. * A GQL query builder.
  139. */
  140. public static final class Builder<V> {
  141. private final ResultType<V> resultType;
  142. private String namespace;
  143. private String queryString;
  144. private boolean allowLiteral;
  145. private final Map<String, Binding> namedBindings = new TreeMap<>();
  146. private final List<Binding> positionalBindings = new LinkedList<>();
  147. Builder(ResultType<V> resultType, String query) {
  148. this.resultType = checkNotNull(resultType);
  149. queryString = checkNotNull(query);
  150. }
  151. public Builder<V> query(String query) {
  152. queryString = checkNotNull(query);
  153. return this;
  154. }
  155. public Builder<V> namespace(String namespace) {
  156. this.namespace = validateNamespace(namespace);
  157. return this;
  158. }
  159. public Builder<V> allowLiteral(boolean allowLiteral) {
  160. this.allowLiteral = allowLiteral;
  161. return this;
  162. }
  163. public Builder<V> clearBindings() {
  164. namedBindings.clear();
  165. positionalBindings.clear();
  166. return this;
  167. }
  168. public Builder<V> setBinding(String name, Cursor cursor) {
  169. namedBindings.put(name, new Binding(name, cursor));
  170. return this;
  171. }
  172. public Builder<V> setBinding(String name, String... value) {
  173. namedBindings.put(name, toBinding(name, StringValue.MARSHALLER, Arrays.asList(value)));
  174. return this;
  175. }
  176. public Builder<V> setBinding(String name, long... value) {
  177. namedBindings.put(name, toBinding(name, LongValue.MARSHALLER, Longs.asList(value)));
  178. return this;
  179. }
  180. public Builder<V> setBinding(String name, double... value) {
  181. namedBindings.put(name, toBinding(name, DoubleValue.MARSHALLER, Doubles.asList(value)));
  182. return this;
  183. }
  184. public Builder<V> setBinding(String name, boolean... value) {
  185. namedBindings.put(name, toBinding(name, BooleanValue.MARSHALLER, Booleans.asList(value)));
  186. return this;
  187. }
  188. public Builder<V> setBinding(String name, DateTime... value) {
  189. namedBindings.put(name, toBinding(name, DateTimeValue.MARSHALLER, Arrays.asList(value)));
  190. return this;
  191. }
  192. public Builder<V> setBinding(String name, Key... value) {
  193. namedBindings.put(name, toBinding(name, KeyValue.MARSHALLER, Arrays.asList(value)));
  194. return this;
  195. }
  196. public Builder<V> setBinding(String name, FullEntity<?>... value) {
  197. namedBindings.put(name, toBinding(name, EntityValue.MARSHALLER, Arrays.asList(value)));
  198. return this;
  199. }
  200. public Builder<V> setBinding(String name, Blob... value) {
  201. namedBindings.put(name, toBinding(name, BlobValue.MARSHALLER, Arrays.asList(value)));
  202. return this;
  203. }
  204. public Builder<V> addBinding(Cursor cursor) {
  205. positionalBindings.add(new Binding(null, cursor));
  206. return this;
  207. }
  208. public Builder<V> addBinding(String... value) {
  209. positionalBindings.add(toBinding(StringValue.MARSHALLER, Arrays.asList(value)));
  210. return this;
  211. }
  212. public Builder<V> addBinding(long... value) {
  213. positionalBindings.add(toBinding(LongValue.MARSHALLER, Longs.asList(value)));
  214. return this;
  215. }
  216. public Builder<V> addBinding(double... value) {
  217. positionalBindings.add(toBinding(DoubleValue.MARSHALLER, Doubles.asList(value)));
  218. return this;
  219. }
  220. public Builder<V> addBinding(boolean... value) {
  221. positionalBindings.add(toBinding(BooleanValue.MARSHALLER, Booleans.asList(value)));
  222. return this;
  223. }
  224. public Builder<V> addBinding(DateTime... value) {
  225. positionalBindings.add(toBinding(DateTimeValue.MARSHALLER, Arrays.asList(value)));
  226. return this;
  227. }
  228. public Builder<V> addBinding(Key... value) {
  229. positionalBindings.add(toBinding(KeyValue.MARSHALLER, Arrays.asList(value)));
  230. return this;
  231. }
  232. public Builder<V> addBinding(FullEntity<?>... value) {
  233. positionalBindings.add(toBinding(EntityValue.MARSHALLER, Arrays.asList(value)));
  234. return this;
  235. }
  236. public Builder<V> addBinding(Blob... value) {
  237. positionalBindings.add(toBinding(BlobValue.MARSHALLER, Arrays.asList(value)));
  238. return this;
  239. }
  240. public GqlQuery<V> build() {
  241. return new GqlQuery<>(this);
  242. }
  243. private static Binding toBinding(Value.BuilderFactory<?, ?, ?> builderFactory, List<?> values) {
  244. return toBinding(null, builderFactory, values);
  245. }
  246. private static <V> Binding toBinding(String name, Value.BuilderFactory<V, ?, ?> builderFactory,
  247. List<?> values) {
  248. List<Value<V>> list = new ArrayList<>(values.size());
  249. for (Object object : values) {
  250. @SuppressWarnings("unchecked")
  251. V v = (V) object;
  252. list.add(builderFactory.newBuilder(v).build());
  253. }
  254. Value<?> value;
  255. if (list.isEmpty()) {
  256. value = new NullValue();
  257. } else if (list.size() == 1) {
  258. value = list.get(0);
  259. } else {
  260. value = new ListValue(list);
  261. }
  262. return new Binding(name, value);
  263. }
  264. }
  265. private GqlQuery(Builder<V> builder) {
  266. super(builder.resultType, builder.namespace);
  267. queryString = builder.queryString;
  268. allowLiteral = builder.allowLiteral;
  269. namedBindings = ImmutableList.copyOf(builder.namedBindings.values());
  270. positionalBindings = ImmutableList.copyOf(builder.positionalBindings);
  271. }
  272. public String queryString() {
  273. return queryString;
  274. }
  275. public boolean allowLiteral() {
  276. return allowLiteral;
  277. }
  278. /**
  279. * Returns an immutable map of named bindings.
  280. */
  281. public Map<String, Object> namedBindings() {
  282. ImmutableMap.Builder<String, Object> builder = ImmutableSortedMap.naturalOrder();
  283. for (Binding binding : namedBindings) {
  284. builder.put(binding.name(), binding.cursorOrValue());
  285. }
  286. return builder.build();
  287. }
  288. /**
  289. * Returns an immutable list of positional bindings (using original order).
  290. */
  291. public List<Object> numberArgs() {
  292. ImmutableList.Builder<Object> builder = ImmutableList.builder();
  293. for (Binding binding : positionalBindings) {
  294. builder.add(binding.cursorOrValue());
  295. }
  296. return builder.build();
  297. }
  298. @Override
  299. public int hashCode() {
  300. return Objects.hash(namespace(), queryString, allowLiteral, namedBindings, positionalBindings);
  301. }
  302. @Override
  303. public boolean equals(Object obj) {
  304. if (obj == this) {
  305. return true;
  306. }
  307. if (!(obj instanceof GqlQuery)) {
  308. return false;
  309. }
  310. GqlQuery<?> other = (GqlQuery<?>) obj;
  311. return Objects.equals(namespace(), other.namespace())
  312. && Objects.equals(queryString, other.queryString)
  313. && allowLiteral == other.allowLiteral
  314. && Objects.equals(namedBindings, other.namedBindings)
  315. && Objects.equals(positionalBindings, other.positionalBindings);
  316. }
  317. @Override
  318. protected DatastoreV1.GqlQuery toPb() {
  319. DatastoreV1.GqlQuery.Builder queryPb = DatastoreV1.GqlQuery.newBuilder();
  320. queryPb.setQueryString(queryString);
  321. queryPb.setAllowLiteral(allowLiteral);
  322. for (Binding argument : namedBindings) {
  323. queryPb.addNameArg(argument.toPb());
  324. }
  325. for (Binding argument : positionalBindings) {
  326. queryPb.addNumberArg(argument.toPb());
  327. }
  328. return queryPb.build();
  329. }
  330. @Override
  331. protected void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb) {
  332. requestPb.setGqlQuery(toPb());
  333. }
  334. @Override
  335. protected GqlQuery<V> nextQuery(DatastoreV1.QueryResultBatch responsePb) {
  336. // See issue #17
  337. throw new UnsupportedOperationException("paging for this query is not implemented yet");
  338. }
  339. @Override
  340. protected Object fromPb(ResultType<V> resultType, String namespace, byte[] bytesPb)
  341. throws InvalidProtocolBufferException {
  342. return fromPb(resultType, namespace, DatastoreV1.GqlQuery.parseFrom(bytesPb));
  343. }
  344. private static <V> GqlQuery<V> fromPb(
  345. ResultType<V> resultType, String ns, DatastoreV1.GqlQuery queryPb) {
  346. Builder<V> builder = new Builder<>(resultType, queryPb.getQueryString());
  347. builder.namespace(ns);
  348. if (queryPb.hasAllowLiteral()) {
  349. builder.allowLiteral = queryPb.getAllowLiteral();
  350. }
  351. for (DatastoreV1.GqlQueryArg nameArg : queryPb.getNameArgList()) {
  352. Binding argument = Binding.fromPb(nameArg);
  353. builder.namedBindings.put(argument.name(), argument);
  354. }
  355. for (DatastoreV1.GqlQueryArg numberArg : queryPb.getNumberArgList()) {
  356. Binding argument = Binding.fromPb(numberArg);
  357. builder.positionalBindings.add(argument);
  358. }
  359. return builder.build();
  360. }
  361. }