Skip to content

Commit 21d351e

Browse files
authored
Fix validation bug for composite filters. (#3609)
* Fix validation bug for composite filters. * Address feedback. * Remove unused import. * Address feedback.
1 parent 8193f13 commit 21d351e

File tree

3 files changed

+90
-10
lines changed

3 files changed

+90
-10
lines changed

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/ValidationTest.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
package com.google.firebase.firestore;
1616

1717
import static com.google.common.truth.Truth.assertThat;
18+
import static com.google.firebase.firestore.Filter.and;
19+
import static com.google.firebase.firestore.Filter.equalTo;
20+
import static com.google.firebase.firestore.Filter.greaterThan;
21+
import static com.google.firebase.firestore.Filter.inArray;
22+
import static com.google.firebase.firestore.Filter.notInArray;
23+
import static com.google.firebase.firestore.Filter.or;
1824
import static com.google.firebase.firestore.testutil.Assert.assertThrows;
1925
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.testAlternateFirestore;
2026
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.testCollection;
@@ -39,6 +45,7 @@
3945
import com.google.firebase.firestore.Transaction.Function;
4046
import com.google.firebase.firestore.testutil.IntegrationTestUtil;
4147
import com.google.firebase.firestore.util.Consumer;
48+
import java.util.Arrays;
4249
import java.util.Date;
4350
import java.util.List;
4451
import java.util.Map;
@@ -815,6 +822,74 @@ public void queriesUsingInAndDocumentIdMustHaveProperDocumentReferencesInArray()
815822
reason);
816823
}
817824

825+
@Test
826+
public void testInvalidQueryFilters() {
827+
CollectionReference collection = testCollection();
828+
829+
// Multiple inequalities, one of which is inside a nested composite filter.
830+
String reason =
831+
"All where filters with an inequality (notEqualTo, notIn, lessThan, lessThanOrEqualTo, greaterThan, or greaterThanOrEqualTo) must be on the same field. But you have filters on 'c' and 'r'";
832+
expectError(
833+
() ->
834+
collection
835+
.where(
836+
or(
837+
and(equalTo("a", "b"), greaterThan("c", "d")),
838+
and(equalTo("e", "f"), equalTo("g", "h"))))
839+
.where(greaterThan("r", "s")),
840+
reason);
841+
842+
// OrderBy and inequality on different fields. Inequality inside a nested composite filter.
843+
reason =
844+
"Invalid query. You have an inequality where filter (whereLessThan(), whereGreaterThan(), etc.) on field 'c' and so you must also have 'c' as your first orderBy() field, but your first orderBy() is currently on field 'r' instead.";
845+
expectError(
846+
() ->
847+
collection
848+
.where(
849+
or(
850+
and(equalTo("a", "b"), greaterThan("c", "d")),
851+
and(equalTo("e", "f"), equalTo("g", "h"))))
852+
.orderBy("r"),
853+
reason);
854+
855+
// Conflicting operations within a composite filter.
856+
reason = "Invalid Query. You cannot use 'not_in' filters with 'in' filters.";
857+
expectError(
858+
() ->
859+
collection.where(
860+
or(
861+
and(equalTo("a", "b"), inArray("c", Arrays.asList("d", "e"))),
862+
and(equalTo("e", "f"), notInArray("c", Arrays.asList("f", "g"))))),
863+
reason);
864+
865+
// Conflicting operations between a field filter and a composite filter.
866+
reason = "Invalid Query. You cannot use 'not_in' filters with 'in' filters.";
867+
expectError(
868+
() ->
869+
collection
870+
.where(
871+
or(
872+
and(equalTo("a", "b"), inArray("c", Arrays.asList("d", "e"))),
873+
and(equalTo("e", "f"), equalTo("g", "h"))))
874+
.where(notInArray("i", Arrays.asList("j", "k"))),
875+
reason);
876+
877+
// Conflicting operations between two composite filters.
878+
reason = "Invalid Query. You cannot use 'not_in' filters with 'in' filters.";
879+
expectError(
880+
() ->
881+
collection
882+
.where(
883+
or(
884+
and(equalTo("a", "b"), inArray("c", Arrays.asList("d", "e"))),
885+
and(equalTo("e", "f"), equalTo("g", "h"))))
886+
.where(
887+
or(
888+
and(equalTo("i", "j"), notInArray("l", Arrays.asList("m", "n"))),
889+
and(equalTo("o", "p"), equalTo("q", "r")))),
890+
reason);
891+
}
892+
818893
// Helpers
819894

820895
/** Performs a write using each write API and makes sure it succeeds. */

firebase-firestore/src/main/java/com/google/firebase/firestore/Query.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ private com.google.firebase.firestore.core.Filter parseFilter(Filter filter) {
468468
}
469469

470470
// TODO(orquery): This method will become public API. Change visibility and add documentation.
471-
private Query where(Filter filter) {
471+
Query where(Filter filter) {
472472
com.google.firebase.firestore.core.Filter parsedFilter = parseFilter(filter);
473473
if (parsedFilter.getFilters().isEmpty()) {
474474
// Return the existing query if not adding any more filters (e.g. an empty composite filter).
@@ -639,7 +639,7 @@ private void validateNewFilter(com.google.firebase.firestore.core.Filter filter)
639639
com.google.firebase.firestore.core.Query testQuery = query;
640640
for (FieldFilter subfilter : filter.getFlattenedFilters()) {
641641
validateNewFieldFilter(testQuery, subfilter);
642-
testQuery = query.filter(subfilter);
642+
testQuery = testQuery.filter(subfilter);
643643
}
644644
}
645645

@@ -651,10 +651,9 @@ private void validateNewFilter(com.google.firebase.firestore.core.Filter filter)
651651
private Operator findFilterWithOperator(
652652
List<com.google.firebase.firestore.core.Filter> filters, List<Operator> operators) {
653653
for (com.google.firebase.firestore.core.Filter filter : filters) {
654-
if (filter instanceof FieldFilter) {
655-
Operator filterOp = ((FieldFilter) filter).getOperator();
656-
if (operators.contains(filterOp)) {
657-
return filterOp;
654+
for (FieldFilter fieldFilter : filter.getFlattenedFilters()) {
655+
if (operators.contains(fieldFilter.getOperator())) {
656+
return fieldFilter.getOperator();
658657
}
659658
}
660659
}

firebase-firestore/src/main/java/com/google/firebase/firestore/core/CompositeFilter.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ public class CompositeFilter extends Filter {
2828
private final List<Filter> filters;
2929
private final Operator operator;
3030

31+
// Memoized list of all field filters that can be found by traversing the tree of filters
32+
// contained in this composite filter.
33+
private List<FieldFilter> memoizedFlattenedFilters;
34+
3135
public CompositeFilter(List<Filter> filters, Operator operator) {
3236
this.filters = filters;
3337
this.operator = operator;
@@ -44,12 +48,14 @@ public Operator getOperator() {
4448

4549
@Override
4650
public List<FieldFilter> getFlattenedFilters() {
47-
// TODO(orquery): memoize this result if this method is used more than once.
48-
List<FieldFilter> result = new ArrayList<>();
51+
if (memoizedFlattenedFilters != null) {
52+
return memoizedFlattenedFilters;
53+
}
54+
memoizedFlattenedFilters = new ArrayList<>();
4955
for (Filter subfilter : filters) {
50-
result.addAll(subfilter.getFlattenedFilters());
56+
memoizedFlattenedFilters.addAll(subfilter.getFlattenedFilters());
5157
}
52-
return result;
58+
return memoizedFlattenedFilters;
5359
}
5460

5561
/**

0 commit comments

Comments
 (0)