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 =