diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/EntityQuery.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/EntityQuery.java
index ceabcc4fa6ff..6038cba91320 100644
--- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/EntityQuery.java
+++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/EntityQuery.java
@@ -16,6 +16,8 @@
package com.google.gcloud.datastore;
+import com.google.api.services.datastore.DatastoreV1;
+
/**
* An implementation of a Google Cloud Datastore entity query that can be constructed by providing
* all the specific query elements.
@@ -23,28 +25,100 @@
* @see Datastore
* queries
*/
-public class EntityQuery extends StructuredQuery {
+public final class EntityQuery extends StructuredQuery {
+
+ private static final long serialVersionUID = 2990565454831019471L;
/**
- * A StructuredQuery builder for queries that return Entity results.
+ * A {@code EntityQuery} builder for queries that return Entity results.
*/
- public static final class Builder extends StructuredQuery.BaseBuilder {
+ public static final class Builder extends StructuredQuery.BaseBuilder {
+
+ private StructuredQuery.BuilderImpl builder;
Builder() {
- super(ResultType.ENTITY);
+ builder = new StructuredQuery.BuilderImpl<>(ResultType.ENTITY);
}
Builder(EntityQuery query) {
- super(query);
+ builder = new StructuredQuery.BuilderImpl<>(query);
+ }
+
+ @Override
+ Builder mergeFrom(DatastoreV1.Query queryPb) {
+ builder.mergeFrom(queryPb);
+ builder.clearProjection();
+ builder.clearGroupBy();
+ return this;
+ }
+
+ @Override
+ public Builder namespace(String namespace) {
+ builder.namespace(namespace);
+ return this;
+ }
+
+ @Override
+ public Builder kind(String kind) {
+ builder.kind(kind);
+ return this;
+ }
+
+ @Override
+ public Builder startCursor(Cursor startCursor) {
+ builder.startCursor(startCursor);
+ return this;
+ }
+
+ @Override
+ public Builder endCursor(Cursor endCursor) {
+ builder.endCursor(endCursor);
+ return this;
+ }
+
+ @Override
+ public Builder offset(int offset) {
+ builder.offset(offset);
+ return this;
+ }
+
+ @Override
+ public Builder limit(Integer limit) {
+ builder.limit(limit);
+ return this;
+ }
+
+ @Override
+ public Builder filter(Filter filter) {
+ builder.filter(filter);
+ return this;
+ }
+
+ @Override
+ public Builder clearOrderBy() {
+ builder.clearOrderBy();
+ return this;
+ }
+
+ @Override
+ public Builder orderBy(OrderBy orderBy, OrderBy... others) {
+ builder.orderBy(orderBy, others);
+ return this;
+ }
+
+ @Override
+ public Builder addOrderBy(OrderBy orderBy, OrderBy... others) {
+ builder.addOrderBy(orderBy, others);
+ return this;
}
@Override
public EntityQuery build() {
- return new EntityQuery(this);
+ return new EntityQuery(builder);
}
}
- EntityQuery(Builder builder) {
+ EntityQuery(BuilderImpl builder) {
super(builder);
}
diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/KeyQuery.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/KeyQuery.java
index 57b5b68199ce..97bc52e50f21 100644
--- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/KeyQuery.java
+++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/KeyQuery.java
@@ -25,37 +25,101 @@
* @see Datastore
* queries
*/
-public class KeyQuery extends StructuredQuery {
+public final class KeyQuery extends StructuredQuery {
+
+ private static final long serialVersionUID = -746768461459070045L;
/**
- * A StructuredQuery builder for queries that return Key results.
+ * A {@code KeyQuery} builder for queries that return Key results.
*/
- public static final class Builder extends StructuredQuery.BaseBuilder {
+ public static final class Builder extends StructuredQuery.BaseBuilder {
+
+ private StructuredQuery.BuilderImpl builder;
Builder() {
- super(ResultType.KEY);
- projection(Projection.property(KEY_PROPERTY_NAME));
+ builder = new StructuredQuery.BuilderImpl<>(ResultType.KEY);
+ builder.projection(Projection.property(KEY_PROPERTY_NAME));
}
Builder(KeyQuery query) {
- super(query);
+ builder = new StructuredQuery.BuilderImpl<>(query);
}
@Override
Builder mergeFrom(DatastoreV1.Query queryPb) {
- super.mergeFrom(queryPb);
- projection(Projection.property(KEY_PROPERTY_NAME));
- clearGroupBy();
+ builder.mergeFrom(queryPb);
+ builder.projection(Projection.property(KEY_PROPERTY_NAME));
+ builder.clearGroupBy();
+ return this;
+ }
+
+ @Override
+ public Builder namespace(String namespace) {
+ builder.namespace(namespace);
+ return this;
+ }
+
+ @Override
+ public Builder kind(String kind) {
+ builder.kind(kind);
+ return this;
+ }
+
+ @Override
+ public Builder startCursor(Cursor startCursor) {
+ builder.startCursor(startCursor);
+ return this;
+ }
+
+ @Override
+ public Builder endCursor(Cursor endCursor) {
+ builder.endCursor(endCursor);
+ return this;
+ }
+
+ @Override
+ public Builder offset(int offset) {
+ builder.offset(offset);
+ return this;
+ }
+
+ @Override
+ public Builder limit(Integer limit) {
+ builder.limit(limit);
+ return this;
+ }
+
+ @Override
+ public Builder filter(Filter filter) {
+ builder.filter(filter);
+ return this;
+ }
+
+ @Override
+ public Builder clearOrderBy() {
+ builder.clearOrderBy();
+ return this;
+ }
+
+ @Override
+ public Builder orderBy(OrderBy orderBy, OrderBy... others) {
+ builder.orderBy(orderBy, others);
+ return this;
+ }
+
+ @Override
+ public Builder addOrderBy(OrderBy orderBy, OrderBy... others) {
+ builder.addOrderBy(orderBy, others);
return this;
}
@Override
public KeyQuery build() {
- return new KeyQuery(this);
+ return new KeyQuery(builder);
}
}
- KeyQuery(Builder builder) {
+ KeyQuery(BuilderImpl builder) {
super(builder);
}
diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ProjectionEntityQuery.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ProjectionEntityQuery.java
index cb61633c20ec..aa4e601583e2 100644
--- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ProjectionEntityQuery.java
+++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ProjectionEntityQuery.java
@@ -16,6 +16,8 @@
package com.google.gcloud.datastore;
+import com.google.api.services.datastore.DatastoreV1.Query;
+
/**
* An implementation of a Google Cloud Datastore projection entity query that can be constructed by
* providing all the specific query elements.
@@ -23,70 +25,140 @@
* @see Datastore
* queries
*/
-public class ProjectionEntityQuery extends StructuredQuery {
+public final class ProjectionEntityQuery extends StructuredQuery {
+
+ private static final long serialVersionUID = 5488451194542425391L;
/**
- * A StructuredQuery builder for queries that return Key results.
+ * A {@code ProjectionEntityQuery} builder for queries that return Key results.
*/
- public static final class Builder extends StructuredQuery.BaseBuilder {
+ public static final class Builder extends StructuredQuery.BaseBuilder {
+
+ private StructuredQuery.BuilderImpl builder;
Builder() {
- super(ResultType.PROJECTION_ENTITY);
+ builder = new StructuredQuery.BuilderImpl<>(ResultType.PROJECTION_ENTITY);
}
Builder(ProjectionEntityQuery query) {
- super(query);
+ builder = new StructuredQuery.BuilderImpl<>(query);
+ }
+
+ @Override
+ Builder mergeFrom(Query queryPb) {
+ builder.mergeFrom(queryPb);
+ return this;
+ }
+
+ @Override
+ public Builder namespace(String namespace) {
+ builder.namespace(namespace);
+ return this;
+ }
+
+ @Override
+ public Builder kind(String kind) {
+ builder.kind(kind);
+ return this;
+ }
+
+ @Override
+ public Builder startCursor(Cursor startCursor) {
+ builder.startCursor(startCursor);
+ return this;
+ }
+
+ @Override
+ public Builder endCursor(Cursor endCursor) {
+ builder.endCursor(endCursor);
+ return this;
+ }
+
+ @Override
+ public Builder offset(int offset) {
+ builder.offset(offset);
+ return this;
+ }
+
+ @Override
+ public Builder limit(Integer limit) {
+ builder.limit(limit);
+ return this;
+ }
+
+ @Override
+ public Builder filter(Filter filter) {
+ builder.filter(filter);
+ return this;
+ }
+
+ @Override
+ public Builder clearOrderBy() {
+ builder.clearOrderBy();
+ return this;
}
@Override
+ public Builder orderBy(OrderBy orderBy, OrderBy... others) {
+ builder.orderBy(orderBy, others);
+ return this;
+ }
+
+ @Override
+ public Builder addOrderBy(OrderBy orderBy, OrderBy... others) {
+ builder.addOrderBy(orderBy, others);
+ return this;
+ }
+
public Builder clearProjection() {
- return super.clearProjection();
+ builder.clearProjection();
+ return this;
}
/**
* Sets the query's projection clause (clearing any previously specified Projection settings).
*/
- @Override
public Builder projection(Projection projection, Projection... others) {
- return super.projection(projection, others);
+ builder.projection(projection, others);
+ return this;
}
/**
* Adds one or more projections to the existing projection clause.
*/
- @Override
public Builder addProjection(Projection projection, Projection... others) {
- return super.addProjection(projection, others);
+ builder.addProjection(projection, others);
+ return this;
}
- @Override
public Builder clearGroupBy() {
- return super.clearGroupBy();
+ builder.clearGroupBy();
+ return this;
}
/**
* Sets the query's group by clause (clearing any previously specified GroupBy settings).
*/
- @Override
public Builder groupBy(String property, String... others) {
- return super.groupBy(property, others);
+ builder.groupBy(property, others);
+ return this;
}
/**
* Adds one or more properties to the existing group by clause.
*/
- @Override
public Builder addGroupBy(String property, String... others) {
- return super.addGroupBy(property, others);
+ builder.addGroupBy(property, others);
+ return this;
}
@Override
public ProjectionEntityQuery build() {
- return new ProjectionEntityQuery(this);
+ return new ProjectionEntityQuery(builder);
}
}
- ProjectionEntityQuery(BaseBuilder builder) {
+ ProjectionEntityQuery(BuilderImpl builder) {
super(builder);
}
diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
index 1e3a0172b643..d2fc5e7fd501 100644
--- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
+++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
@@ -609,13 +609,82 @@ public static Projection first(String property) {
}
}
+ /**
+ * Interface for StructuredQuery builders.
+ *
+ * @param the type of result the query returns.
+ */
+ public interface Builder {
+ public Builder namespace(String namespace);
+
+ public Builder kind(String kind);
+
+ public Builder startCursor(Cursor startCursor);
+
+ public Builder endCursor(Cursor endCursor);
+
+ public Builder offset(int offset);
+
+ public Builder limit(Integer limit);
+
+ public Builder filter(Filter filter);
+
+ public Builder clearOrderBy();
+
+ /**
+ * Sets the query's order by clause (clearing any previously specified OrderBy settings).
+ */
+ public Builder orderBy(OrderBy orderBy, OrderBy... others);
+
+ /**
+ * Adds settings to the existing order by clause.
+ */
+ public Builder addOrderBy(OrderBy orderBy, OrderBy... others);
+
+ public abstract StructuredQuery build();
+ }
+
+ abstract static class BaseBuilder implements Builder {
+ @Override
+ abstract public BaseBuilder namespace(String namespace);
+
+ @Override
+ public abstract BaseBuilder kind(String kind);
+
+ @Override
+ public abstract BaseBuilder startCursor(Cursor startCursor);
+
+ @Override
+ public abstract BaseBuilder endCursor(Cursor endCursor);
+
+ @Override
+ public abstract BaseBuilder offset(int offset);
+
+ @Override
+ public abstract BaseBuilder limit(Integer limit);
+
+ @Override
+ public abstract BaseBuilder filter(Filter filter);
+
+ @Override
+ public abstract BaseBuilder clearOrderBy();
+
+ @Override
+ public abstract BaseBuilder orderBy(OrderBy orderBy, OrderBy... others);
+
+ @Override
+ public abstract BaseBuilder addOrderBy(OrderBy orderBy, OrderBy... others);
+
+ abstract BaseBuilder mergeFrom(DatastoreV1.Query queryPb);
+ }
+
/**
* Base class for StructuredQuery builders.
*
* @param the type of result the query returns.
* @param the query builder.
*/
- public abstract static class BaseBuilder> {
+ static class BuilderImpl> extends BaseBuilder {
private final ResultType resultType;
private String namespace;
@@ -629,11 +698,11 @@ public abstract static class BaseBuilder> {
private int offset;
private Integer limit;
- BaseBuilder(ResultType resultType) {
+ BuilderImpl(ResultType resultType) {
this.resultType = resultType;
}
- BaseBuilder(StructuredQuery query) {
+ BuilderImpl(StructuredQuery query) {
resultType = query.type();
namespace = query.namespace();
kind = query.kind;
@@ -652,60 +721,64 @@ B self() {
return (B) this;
}
+ @Override
public B namespace(String namespace) {
this.namespace = namespace;
return self();
}
+ @Override
public B kind(String kind) {
this.kind = kind;
return self();
}
+ @Override
public B startCursor(Cursor startCursor) {
this.startCursor = startCursor;
return self();
}
+ @Override
public B endCursor(Cursor endCursor) {
this.endCursor = endCursor;
return self();
}
+ @Override
public B offset(int offset) {
Preconditions.checkArgument(offset >= 0, "offset must not be negative");
this.offset = offset;
return self();
}
+ @Override
public B limit(Integer limit) {
Preconditions.checkArgument(limit == null || limit > 0, "limit must be positive");
this.limit = limit;
return self();
}
+ @Override
public B filter(Filter filter) {
this.filter = filter;
return self();
}
+ @Override
public B clearOrderBy() {
orderBy.clear();
return self();
}
- /**
- * Sets the query's order by clause (clearing any previously specified OrderBy settings).
- */
+ @Override
public B orderBy(OrderBy orderBy, OrderBy... others) {
clearOrderBy();
addOrderBy(orderBy, others);
return self();
}
- /**
- * Adds settings to the existing order by clause.
- */
+ @Override
public B addOrderBy(OrderBy orderBy, OrderBy... others) {
this.orderBy.add(orderBy);
Collections.addAll(this.orderBy, others);
@@ -746,6 +819,7 @@ B addGroupBy(String property, String... others) {
return self();
}
+ @Override
B mergeFrom(DatastoreV1.Query queryPb) {
if (queryPb.getKindCount() > 0) {
kind(queryPb.getKind(0).getName());
@@ -777,10 +851,13 @@ B mergeFrom(DatastoreV1.Query queryPb) {
return self();
}
- public abstract StructuredQuery build();
+ @Override
+ public StructuredQuery build() {
+ throw new IllegalStateException("This should be overridden.");
+ }
}
- StructuredQuery(BaseBuilder builder) {
+ StructuredQuery(BuilderImpl builder) {
super(builder.resultType, builder.namespace);
kind = builder.kind;
projection = ImmutableList.copyOf(builder.projection);
@@ -861,7 +938,7 @@ public Integer limit() {
return limit;
}
- public abstract BaseBuilder toBuilder();
+ public abstract Builder toBuilder();
@Override
void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb) {
@@ -870,7 +947,7 @@ void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb) {
@Override
StructuredQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) {
- BaseBuilder builder = toBuilder();
+ Builder builder = toBuilder();
builder.startCursor(new Cursor(responsePb.getEndCursor()));
if (offset > 0 && responsePb.getSkippedResults() < offset) {
builder.offset(offset - responsePb.getSkippedResults());
@@ -924,7 +1001,7 @@ Object fromPb(ResultType resultType, String namespace, byte[] bytesPb)
private static StructuredQuery> fromPb(ResultType> resultType, String namespace,
DatastoreV1.Query queryPb) {
- BaseBuilder, ?> builder;
+ BaseBuilder> builder;
if (resultType.equals(ResultType.ENTITY)) {
builder = new EntityQuery.Builder();
} else if (resultType.equals(ResultType.KEY)) {
diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/StructuredQueryTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/StructuredQueryTest.java
index 4b8c16e1b9d6..b8b13f273648 100644
--- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/StructuredQueryTest.java
+++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/StructuredQueryTest.java
@@ -19,12 +19,125 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import com.google.common.collect.ImmutableList;
+import com.google.gcloud.datastore.StructuredQuery.CompositeFilter;
+import com.google.gcloud.datastore.StructuredQuery.Filter;
+import com.google.gcloud.datastore.StructuredQuery.OrderBy;
import com.google.gcloud.datastore.StructuredQuery.Projection;
+import com.google.gcloud.datastore.StructuredQuery.PropertyFilter;
import org.junit.Test;
+import java.util.List;
+
public class StructuredQueryTest {
+ private static final String NAMESPACE = "ns";
+ private static final String KIND = "k";
+ private static final Cursor START_CURSOR = Cursor.copyFrom(new byte[] {1, 2});
+ private static final Cursor END_CURSOR = Cursor.copyFrom(new byte[] {10});
+ private static final int OFFSET = 42;
+ private static final Integer LIMIT = 43;
+ private static final Filter FILTER = CompositeFilter.and(PropertyFilter.gt("p1", 10), PropertyFilter.eq("a", "v"));
+ private static final OrderBy ORDER_BY_1 = OrderBy.asc("p2");
+ private static final OrderBy ORDER_BY_2 = OrderBy.desc("p3");
+ private static final List ORDER_BY = ImmutableList.of(ORDER_BY_1, ORDER_BY_2);
+ private static final Projection PROJECTION1 = Projection.property("p4");
+ private static final Projection PROJECTION2 = Projection.property("p5");
+ private static final List PROJECTION = ImmutableList.of(PROJECTION1, PROJECTION2);
+ private static final String GROUP_BY1 = "p6";
+ private static final String GROUP_BY2 = "p7";
+ private static final List GROUP_BY = ImmutableList.of(GROUP_BY1, GROUP_BY2);
+ private static final EntityQuery ENTITY_QUERY = Query.entityQueryBuilder()
+ .namespace(NAMESPACE)
+ .kind(KIND)
+ .startCursor(START_CURSOR)
+ .endCursor(END_CURSOR)
+ .offset(OFFSET)
+ .limit(LIMIT)
+ .filter(FILTER)
+ .orderBy(ORDER_BY_1, ORDER_BY_2)
+ .build();
+ private static final KeyQuery KEY_QUERY = Query.keyQueryBuilder()
+ .namespace(NAMESPACE)
+ .kind(KIND)
+ .startCursor(START_CURSOR)
+ .endCursor(END_CURSOR)
+ .offset(OFFSET)
+ .limit(LIMIT)
+ .filter(FILTER)
+ .orderBy(ORDER_BY_1, ORDER_BY_2)
+ .build();
+ private static final ProjectionEntityQuery PROJECTION_QUERY =
+ Query.projectionEntityQueryBuilder()
+ .namespace(NAMESPACE)
+ .kind(KIND)
+ .startCursor(START_CURSOR)
+ .endCursor(END_CURSOR)
+ .offset(OFFSET)
+ .limit(LIMIT)
+ .filter(FILTER)
+ .orderBy(ORDER_BY_1, ORDER_BY_2)
+ .projection(PROJECTION1, PROJECTION2)
+ .groupBy(GROUP_BY1, GROUP_BY2)
+ .build();
+
+ @Test
+ public void testEntityQueryBuilder() {
+ compareBaseBuilderFields(ENTITY_QUERY);
+ assertTrue(ENTITY_QUERY.projection().isEmpty());
+ assertTrue(ENTITY_QUERY.groupBy().isEmpty());
+ }
+
+ @Test
+ public void testKeyQueryBuilder() {
+ compareBaseBuilderFields(KEY_QUERY);
+ assertEquals(
+ ImmutableList.of(Projection.property(StructuredQuery.KEY_PROPERTY_NAME)),
+ KEY_QUERY.projection());
+ assertTrue(KEY_QUERY.groupBy().isEmpty());
+ }
+
+ @Test
+ public void testProjectionEntityQueryBuilder() {
+ compareBaseBuilderFields(PROJECTION_QUERY);
+ assertEquals(PROJECTION, PROJECTION_QUERY.projection());
+ assertEquals(GROUP_BY, PROJECTION_QUERY.groupBy());
+ }
+
+ private void compareBaseBuilderFields(StructuredQuery> query) {
+ assertEquals(NAMESPACE, query.namespace());
+ assertEquals(KIND, query.kind());
+ assertEquals(START_CURSOR, query.startCursor());
+ assertEquals(END_CURSOR, query.endCursor());
+ assertEquals(OFFSET, query.offset());
+ assertEquals(LIMIT, query.limit());
+ assertEquals(FILTER, query.filter());
+ assertEquals(ORDER_BY, query.orderBy());
+ }
+
+ @Test
+ public void mergeFrom() {
+ compareMergedQuery(
+ ENTITY_QUERY, new EntityQuery.Builder().mergeFrom(ENTITY_QUERY.toPb()).build());
+ compareMergedQuery(KEY_QUERY, new KeyQuery.Builder().mergeFrom(KEY_QUERY.toPb()).build());
+ compareMergedQuery(
+ PROJECTION_QUERY,
+ new ProjectionEntityQuery.Builder().mergeFrom(PROJECTION_QUERY.toPb()).build());
+ }
+
+ private void compareMergedQuery(StructuredQuery> expected, StructuredQuery> actual) {
+ assertEquals(expected.kind(), actual.kind());
+ assertEquals(expected.startCursor(), actual.startCursor());
+ assertEquals(expected.endCursor(), actual.endCursor());
+ assertEquals(expected.offset(), actual.offset());
+ assertEquals(expected.limit(), actual.limit());
+ assertEquals(expected.filter(), actual.filter());
+ assertEquals(expected.orderBy(), actual.orderBy());
+ assertEquals(expected.projection(), actual.projection());
+ assertEquals(expected.groupBy(), actual.groupBy());
+ }
+
@Test
public void testToBuilder() {
StructuredQuery entityQuery =