Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: OR query support #993

Merged
merged 8 commits into from
Mar 8, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.api.core.ApiFunction;
import com.google.api.core.BetaApi;
import com.google.api.core.InternalApi;
import com.google.cloud.StringEnumType;
import com.google.cloud.StringEnumValue;
Expand Down Expand Up @@ -151,6 +152,8 @@ public Operator apply(String constant) {

static final Operator AND = type.createAndRegister("AND");

static final Operator OR = type.createAndRegister("OR");

com.google.datastore.v1.CompositeFilter.Operator toPb() {
return com.google.datastore.v1.CompositeFilter.Operator.valueOf(name());
}
Expand Down Expand Up @@ -231,6 +234,11 @@ public static CompositeFilter and(Filter first, Filter... other) {
return new CompositeFilter(Operator.AND, first, other);
}

@BetaApi
public static CompositeFilter or(Filter first, Filter... other) {
return new CompositeFilter(Operator.OR, first, other);
}

@Override
com.google.datastore.v1.Filter toPb() {
com.google.datastore.v1.Filter.Builder filterPb = com.google.datastore.v1.Filter.newBuilder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,19 @@ public class SerializationTest extends BaseSerializationTest {
.addDistinctOn("p")
.addOrderBy(OrderBy.asc("p"))
.build();
private static final Query<ProjectionEntity> QUERY4 =
Query.newProjectionEntityQueryBuilder()
.setKind("k")
.setNamespace("ns1")
.addProjection("p")
.setLimit(100)
.setOffset(5)
.setStartCursor(CURSOR1)
.setEndCursor(CURSOR2)
.setFilter(CompositeFilter.or(PropertyFilter.gt("p1", 10), PropertyFilter.eq("a", "v")))
.addDistinctOn("p")
.addOrderBy(OrderBy.asc("p"))
.build();
private static final KeyValue KEY_VALUE = KeyValue.of(KEY1);
private static final NullValue NULL_VALUE =
NullValue.newBuilder().setExcludeFromIndexes(true).build();
Expand Down Expand Up @@ -136,6 +149,7 @@ protected java.io.Serializable[] serializableObjects() {
QUERY1,
QUERY2,
QUERY3,
QUERY4,
NULL_VALUE,
KEY_VALUE,
STRING_VALUE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ public class StructuredQueryTest {
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 =
private static final Filter AND_FILTER =
CompositeFilter.and(PropertyFilter.gt("p1", 10), PropertyFilter.eq("a", "v"));
private static final Filter OR_FILTER =
CompositeFilter.or(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<OrderBy> ORDER_BY = ImmutableList.of(ORDER_BY_1, ORDER_BY_2);
Expand All @@ -56,7 +58,7 @@ public class StructuredQueryTest {
.setEndCursor(END_CURSOR)
.setOffset(OFFSET)
.setLimit(LIMIT)
.setFilter(FILTER)
.setFilter(AND_FILTER)
.setOrderBy(ORDER_BY_1, ORDER_BY_2)
.build();
private static final KeyQuery KEY_QUERY =
Expand All @@ -67,7 +69,7 @@ public class StructuredQueryTest {
.setEndCursor(END_CURSOR)
.setOffset(OFFSET)
.setLimit(LIMIT)
.setFilter(FILTER)
.setFilter(OR_FILTER)
.setOrderBy(ORDER_BY_1, ORDER_BY_2)
.build();
private static final ProjectionEntityQuery PROJECTION_QUERY =
Expand All @@ -78,7 +80,7 @@ public class StructuredQueryTest {
.setEndCursor(END_CURSOR)
.setOffset(OFFSET)
.setLimit(LIMIT)
.setFilter(FILTER)
.setFilter(AND_FILTER)
.setOrderBy(ORDER_BY_1, ORDER_BY_2)
.setProjection(PROJECTION1, PROJECTION2)
.setDistinctOn(DISTINCT_ON1, DISTINCT_ON2)
Expand All @@ -93,7 +95,14 @@ public void testEntityQueryBuilder() {

@Test
public void testKeyQueryBuilder() {
compareBaseBuilderFields(KEY_QUERY);
assertEquals(NAMESPACE, KEY_QUERY.getNamespace());
assertEquals(KIND, KEY_QUERY.getKind());
assertEquals(START_CURSOR, KEY_QUERY.getStartCursor());
assertEquals(END_CURSOR, KEY_QUERY.getEndCursor());
assertEquals(OFFSET, KEY_QUERY.getOffset());
assertEquals(LIMIT, KEY_QUERY.getLimit());
assertEquals(OR_FILTER, KEY_QUERY.getFilter());
assertEquals(ORDER_BY, KEY_QUERY.getOrderBy());
assertEquals(ImmutableList.of(StructuredQuery.KEY_PROPERTY_NAME), KEY_QUERY.getProjection());
assertTrue(KEY_QUERY.getDistinctOn().isEmpty());
}
Expand All @@ -112,7 +121,7 @@ private void compareBaseBuilderFields(StructuredQuery<?> query) {
assertEquals(END_CURSOR, query.getEndCursor());
assertEquals(OFFSET, query.getOffset());
assertEquals(LIMIT, query.getLimit());
assertEquals(FILTER, query.getFilter());
assertEquals(AND_FILTER, query.getFilter());
assertEquals(ORDER_BY, query.getOrderBy());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import com.google.cloud.datastore.ReadOption;
import com.google.cloud.datastore.StringValue;
import com.google.cloud.datastore.StructuredQuery;
import com.google.cloud.datastore.StructuredQuery.CompositeFilter;
import com.google.cloud.datastore.StructuredQuery.OrderBy;
import com.google.cloud.datastore.StructuredQuery.PropertyFilter;
import com.google.cloud.datastore.TimestampValue;
Expand Down Expand Up @@ -223,6 +224,89 @@ private <T> List<T> makeResultsCopy(QueryResults<T> scResults) {
return results;
}

@Test
public void orQuery() {
Key key = Key.newBuilder(KEY1, KIND2, 2).build();
Entity entity3 =
Entity.newBuilder(ENTITY1)
.setKey(key)
.remove("str")
.set("name", "Dan")
.setNull("null")
.set("age", 19)
.build();
try {
DATASTORE.put(entity3);

// age == 19 || age == 20
CompositeFilter orFilter =
CompositeFilter.or(PropertyFilter.eq("age", 19), PropertyFilter.eq("age", 20));
Query<Entity> simpleOrQuery =
Query.newEntityQueryBuilder()
.setNamespace(NAMESPACE)
.setKind(KIND2)
.setFilter(orFilter)
.build();
QueryResults<Entity> results = DATASTORE.run(simpleOrQuery);
assertTrue(results.hasNext());
assertEquals(ENTITY2, results.next());
assertTrue(results.hasNext());
assertEquals(entity3, results.next());
assertFalse(results.hasNext());

// age == 19 || age == 20 with limit of 1
Query<Entity> simpleOrQueryLimit =
Query.newEntityQueryBuilder()
.setNamespace(NAMESPACE)
.setKind(KIND2)
.setFilter(orFilter)
.setLimit(1)
.build();
QueryResults<Entity> results2 = DATASTORE.run(simpleOrQueryLimit);
assertTrue(results2.hasNext());
assertEquals(ENTITY2, results2.next());
assertFalse(results2.hasNext());

// (age == 18 && name == Dan) || (age == 20 && name == Dan)
CompositeFilter nestedOr =
CompositeFilter.or(
CompositeFilter.and(PropertyFilter.eq("age", 18), PropertyFilter.eq("name", "Dan")),
CompositeFilter.and(PropertyFilter.eq("age", 20), PropertyFilter.eq("name", "Dan")));
CompositeFilter compositeFilter =
CompositeFilter.and(PropertyFilter.hasAncestor(ROOT_KEY), nestedOr);
Query<Entity> orQueryNested =
Query.newEntityQueryBuilder()
.setNamespace(NAMESPACE)
.setKind(KIND2)
.setFilter(compositeFilter)
.build();
QueryResults<Entity> results3 = DATASTORE.run(orQueryNested);
assertTrue(results3.hasNext());
assertEquals(ENTITY2, results3.next());
assertFalse(results3.hasNext());

// age == 20 && (name == Bob || name == Dan)
CompositeFilter nestedOr2 =
CompositeFilter.or(PropertyFilter.eq("name", "Dan"), PropertyFilter.eq("name", "Bob"));
CompositeFilter andFilter = CompositeFilter.and(PropertyFilter.eq("age", 20), nestedOr2);
CompositeFilter ancestorAndFilter =
kolea2 marked this conversation as resolved.
Show resolved Hide resolved
CompositeFilter.and(PropertyFilter.hasAncestor(ROOT_KEY), andFilter);
Query<Entity> orQueryNested2 =
Query.newEntityQueryBuilder()
.setNamespace(NAMESPACE)
.setKind(KIND2)
.setFilter(ancestorAndFilter)
.setLimit(1)
.build();
QueryResults<Entity> results4 = DATASTORE.run(orQueryNested2);
assertTrue(results4.hasNext());
assertEquals(ENTITY2, results4.next());
assertFalse(results4.hasNext());
} finally {
DATASTORE.delete(entity3.getKey());
}
jainsahab marked this conversation as resolved.
Show resolved Hide resolved
}

@Test
public void testNewTransactionCommit() {
Transaction transaction = DATASTORE.newTransaction();
Expand Down Expand Up @@ -946,6 +1030,21 @@ public void testInNotInNeqFilters() throws InterruptedException {
assertEquals(e2, resultNeq.next());
assertFalse(resultNeq.hasNext());

Query<Entity> scQueryInEqOr =
Query.newEntityQueryBuilder()
.setKind(KIND1)
.setFilter(
CompositeFilter.or(
PropertyFilter.in("v_int", ListValue.of(10, 50000)),
PropertyFilter.eq("v_int", 10000)))
.build();

QueryResults<Entity> run = DATASTORE.run(scQueryInEqOr);

assertTrue(run.hasNext());
assertEquals(e1, run.next());
assertFalse(run.hasNext());

DATASTORE.delete(e1.getKey());
DATASTORE.delete(e2.getKey());
}
Expand Down