Skip to content

Commit

Permalink
feat: Enable OR Query support. (#1007)
Browse files Browse the repository at this point in the history
* Remove TODOs and add tests for OR Query support.

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* Address comments and backport a new test.

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* Target backend for integration tests.

* Fix: uninitialized 'firestore' variable.

* Add the docs.

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* Add test dependency.

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* Remove tests that require composite index and unsupported cases.

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* Remove the 'hide' annotation.

* Rearrange/add tests.

* remove duplicate test which is run only against emulator.

---------

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
Co-authored-by: meredithslota <meredithslota@google.com>
  • Loading branch information
3 people committed Mar 6, 2023
1 parent 8b6ecd9 commit e502cd4
Show file tree
Hide file tree
Showing 7 changed files with 624 additions and 12 deletions.
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -56,13 +56,13 @@ implementation 'com.google.cloud:google-cloud-firestore'
If you are using Gradle without BOM, add this to your dependencies:

```Groovy
implementation 'com.google.cloud:google-cloud-firestore:3.8.1'
implementation 'com.google.cloud:google-cloud-firestore:3.8.2'
```

If you are using SBT, add this to your dependencies:

```Scala
libraryDependencies += "com.google.cloud" % "google-cloud-firestore" % "3.8.1"
libraryDependencies += "com.google.cloud" % "google-cloud-firestore" % "3.8.2"
```

## Authentication
Expand Down
5 changes: 5 additions & 0 deletions google-cloud-firestore/pom.xml
Expand Up @@ -177,6 +177,11 @@
<version>3.12.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<reporting>
Expand Down
Expand Up @@ -23,7 +23,6 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/** @hide */
public class Filter {
static class UnaryFilter extends Filter {
private final FieldPath field;
Expand Down Expand Up @@ -69,113 +68,269 @@ public StructuredQuery.CompositeFilter.Operator getOperator() {
}
}

/**
* Creates a new filter for checking that the given field is equal to the given value.
*
* @param field The field used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter equalTo(@Nonnull String field, @Nullable Object value) {
return equalTo(FieldPath.fromDotSeparatedString(field), value);
}

/**
* Creates a new filter for checking that the given field is equal to the given value.
*
* @param fieldPath The field path used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter equalTo(@Nonnull FieldPath fieldPath, @Nullable Object value) {
return new UnaryFilter(fieldPath, Operator.EQUAL, value);
}

/**
* Creates a new filter for checking that the given field is not equal to the given value.
*
* @param field The field used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter notEqualTo(@Nonnull String field, @Nullable Object value) {
return notEqualTo(FieldPath.fromDotSeparatedString(field), value);
}

/**
* Creates a new filter for checking that the given field is not equal to the given value.
*
* @param fieldPath The field path used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter notEqualTo(@Nonnull FieldPath fieldPath, @Nullable Object value) {
return new UnaryFilter(fieldPath, Operator.NOT_EQUAL, value);
}

/**
* Creates a new filter for checking that the given field is greater than the given value.
*
* @param field The field used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter greaterThan(@Nonnull String field, @Nullable Object value) {
return greaterThan(FieldPath.fromDotSeparatedString(field), value);
}

/**
* Creates a new filter for checking that the given field is greater than the given value.
*
* @param fieldPath The field path used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter greaterThan(@Nonnull FieldPath fieldPath, @Nullable Object value) {
return new UnaryFilter(fieldPath, Operator.GREATER_THAN, value);
}

/**
* Creates a new filter for checking that the given field is greater than or equal to the given
* value.
*
* @param field The field used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter greaterThanOrEqualTo(@Nonnull String field, @Nullable Object value) {
return greaterThanOrEqualTo(FieldPath.fromDotSeparatedString(field), value);
}

/**
* Creates a new filter for checking that the given field is greater than or equal to the given
* value.
*
* @param fieldPath The field path used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter greaterThanOrEqualTo(@Nonnull FieldPath fieldPath, @Nullable Object value) {
return new UnaryFilter(fieldPath, Operator.GREATER_THAN_OR_EQUAL, value);
}

/**
* Creates a new filter for checking that the given field is less than the given value.
*
* @param field The field used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter lessThan(@Nonnull String field, @Nullable Object value) {
return lessThan(FieldPath.fromDotSeparatedString(field), value);
}

/**
* Creates a new filter for checking that the given field is less than the given value.
*
* @param fieldPath The field path used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter lessThan(@Nonnull FieldPath fieldPath, @Nullable Object value) {
return new UnaryFilter(fieldPath, Operator.LESS_THAN, value);
}

/**
* Creates a new filter for checking that the given field is less than or equal to the given
* value.
*
* @param field The field used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter lessThanOrEqualTo(@Nonnull String field, @Nullable Object value) {
return lessThanOrEqualTo(FieldPath.fromDotSeparatedString(field), value);
}

/**
* Creates a new filter for checking that the given field is less than or equal to the given
* value.
*
* @param fieldPath The field path used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter lessThanOrEqualTo(@Nonnull FieldPath fieldPath, @Nullable Object value) {
return new UnaryFilter(fieldPath, Operator.LESS_THAN_OR_EQUAL, value);
}

/**
* Creates a new filter for checking that the given array field contains the given value.
*
* @param field The field used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter arrayContains(@Nonnull String field, @Nullable Object value) {
return arrayContains(FieldPath.fromDotSeparatedString(field), value);
}

/**
* Creates a new filter for checking that the given array field contains the given value.
*
* @param fieldPath The field path used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter arrayContains(@Nonnull FieldPath fieldPath, @Nullable Object value) {
return new UnaryFilter(fieldPath, Operator.ARRAY_CONTAINS, value);
}

/**
* Creates a new filter for checking that the given array field contains any of the given values.
*
* @param field The field used for the filter.
* @param value The list of values used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter arrayContainsAny(@Nonnull String field, @Nullable Object value) {
return arrayContainsAny(FieldPath.fromDotSeparatedString(field), value);
}

/**
* Creates a new filter for checking that the given array field contains any of the given values.
*
* @param fieldPath The field path used for the filter.
* @param value The list of values used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter arrayContainsAny(@Nonnull FieldPath fieldPath, @Nullable Object value) {
return new UnaryFilter(fieldPath, Operator.ARRAY_CONTAINS_ANY, value);
}

/**
* Creates a new filter for checking that the given field equals any of the given values.
*
* @param field The field used for the filter.
* @param value The list of values used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter inArray(@Nonnull String field, @Nullable Object value) {
return inArray(FieldPath.fromDotSeparatedString(field), value);
}

/**
* Creates a new filter for checking that the given field equals any of the given values.
*
* @param fieldPath The field path used for the filter.
* @param value The list of values used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter inArray(@Nonnull FieldPath fieldPath, @Nullable Object value) {
return new UnaryFilter(fieldPath, Operator.IN, value);
}

/**
* Creates a new filter for checking that the given field does not equal any of the given values.
*
* @param field The field path used for the filter.
* @param value The list of values used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter notInArray(@Nonnull String field, @Nullable Object value) {
return notInArray(FieldPath.fromDotSeparatedString(field), value);
}

/**
* Creates a new filter for checking that the given field does not equal any of the given values.
*
* @param fieldPath The field path used for the filter.
* @param value The list of values used for the filter.
* @return The newly created filter.
*/
@Nonnull
public static Filter notInArray(@Nonnull FieldPath fieldPath, @Nullable Object value) {
return new UnaryFilter(fieldPath, Operator.NOT_IN, value);
}

/**
* Creates a new filter that is a disjunction of the given filters. A disjunction filter includes
* a document if it satisfies <em>any</em> of the given filters.
*
* @param filters The list of filters to perform a disjunction for.
* @return The newly created filter.
*/
@Nonnull
public static Filter or(Filter... filters) {
// TODO(orquery): Change this to Operator.OR once it is available.
return new CompositeFilter(
Arrays.asList(filters), StructuredQuery.CompositeFilter.Operator.OPERATOR_UNSPECIFIED);
return new CompositeFilter(Arrays.asList(filters), StructuredQuery.CompositeFilter.Operator.OR);
}

/**
* Creates a new filter that is a conjunction of the given filters. A conjunction filter includes
* a document if it satisfies <em>all</em> of the given filters.
*
* @param filters The list of filters to perform a conjunction for.
* @return The newly created filter.
*/
@Nonnull
public static Filter and(Filter... filters) {
return new CompositeFilter(
Expand Down
Expand Up @@ -888,8 +888,13 @@ public Query whereNotIn(@Nonnull FieldPath fieldPath, @Nonnull List<? extends Ob
return where(new com.google.cloud.firestore.Filter.UnaryFilter(fieldPath, NOT_IN, values));
}

// TODO(orquery): This method will become public API. Change visibility and add documentation.
Query where(com.google.cloud.firestore.Filter filter) {
/**
* Creates and returns a new Query with the additional filter.
*
* @param filter The new filter to apply to the existing query.
* @return The newly created Query.
*/
public Query where(com.google.cloud.firestore.Filter filter) {
Preconditions.checkState(
options.getStartCursor() == null && options.getEndCursor() == null,
"Cannot call a where() clause after defining a boundary with startAt(), "
Expand Down
Expand Up @@ -651,8 +651,7 @@ public static StructuredQuery.Filter andFilters(StructuredQuery.Filter... filter
}

public static StructuredQuery.Filter orFilters(StructuredQuery.Filter... filters) {
// TODO(orquery): Replace this with Operator.OR once it's available.
return compositeFilter(CompositeFilter.Operator.OPERATOR_UNSPECIFIED, Arrays.asList(filters));
return compositeFilter(CompositeFilter.Operator.OR, Arrays.asList(filters));
}

private static StructuredQuery.Filter compositeFilter(
Expand Down
Expand Up @@ -1331,12 +1331,12 @@ public void serializationTestWithSingleFilterCompositeFilters() {
}

@Test
public void serializationTestWithNestedCompositeFilters() {
public void serializationTestWithNestedCompositeFiltersOuterAnd() {
assertSerialization(query);
// a IN [1,2]
query.where(inArray("a", Arrays.asList(1, 2)));
assertSerialization(query);
// a IN [1,2] && (b==20 || c==30 || (d==40 && e>50)) || f==60
// a IN [1,2] && (b==20 || c==30 || (d==40 && e>50) || f==60)
query.where(
or(
equalTo("b", 20),
Expand All @@ -1361,6 +1361,36 @@ public void serializationTestWithNestedCompositeFilters() {
assertSerialization(query);
}

@Test
public void serializationTestWithNestedCompositeFiltersOuterOr() {
assertSerialization(query);
// a IN [1,2] || (b==20 && c==30 && (d==40 || e>50) && f==60)
query.where(
or(
inArray("a", Arrays.asList(1, 2)),
and(
equalTo("b", 20),
equalTo("c", 30),
or(equalTo("d", 40), greaterThan("e", 50)),
and(equalTo("f", 60)),
or(and()))));
assertSerialization(query);
query = query.orderBy("l");
assertSerialization(query);
query = query.startAt("o");
assertSerialization(query);
query = query.startAfter("p");
assertSerialization(query);
query = query.endBefore("q");
assertSerialization(query);
query = query.endAt("r");
assertSerialization(query);
query = query.limit(8);
assertSerialization(query);
query = query.offset(9);
assertSerialization(query);
}

private void assertSerialization(Query query) {
RunQueryRequest runQueryRequest = query.toProto();
Query deserializedQuery = Query.fromProto(firestoreMock, runQueryRequest);
Expand Down

0 comments on commit e502cd4

Please sign in to comment.