Skip to content

Commit 5ea5bd0

Browse files
authored
Remove some query restrictions and add integration tests (#4231)
* Remove multipl IN restriction and add an integration test. * Update the ValidationTest to reflect the relaxed requirements. * Remove obsolete test. * new or query validation. * Remove the word 'yet'. * Fix wording. * *Revert* the last commit (some new validation checks). * Relax query restrictions. * Skip Or query tests until supported in production.
1 parent 6071e9d commit 5ea5bd0

File tree

4 files changed

+174
-161
lines changed

4 files changed

+174
-161
lines changed

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

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,4 +1178,171 @@ public void testOrQueriesWithArrayMembership() {
11781178
"doc4",
11791179
"doc6");
11801180
}
1181+
1182+
@Ignore
1183+
@Test
1184+
public void testMultipleInOps() {
1185+
Map<String, Map<String, Object>> testDocs =
1186+
map(
1187+
"doc1", map("a", 1, "b", 0),
1188+
"doc2", map("b", 1),
1189+
"doc3", map("a", 3, "b", 2),
1190+
"doc4", map("a", 1, "b", 3),
1191+
"doc5", map("a", 1),
1192+
"doc6", map("a", 2));
1193+
CollectionReference collection = testCollectionWithDocs(testDocs);
1194+
1195+
// Two IN operations on different fields with disjunction.
1196+
Query query1 =
1197+
collection
1198+
.where(Filter.or(Filter.inArray("a", asList(2, 3)), Filter.inArray("b", asList(0, 2))))
1199+
.orderBy("a");
1200+
checkOnlineAndOfflineResultsMatch(query1, "doc1", "doc6", "doc3");
1201+
1202+
// Two IN operations on different fields with conjunction.
1203+
Query query2 =
1204+
collection
1205+
.where(Filter.and(Filter.inArray("a", asList(2, 3)), Filter.inArray("b", asList(0, 2))))
1206+
.orderBy("a");
1207+
checkOnlineAndOfflineResultsMatch(query2, "doc3");
1208+
1209+
// Two IN operations on the same field.
1210+
// a IN [1,2,3] && a IN [0,1,4] should result in "a==1".
1211+
Query query3 =
1212+
collection.where(
1213+
Filter.and(Filter.inArray("a", asList(1, 2, 3)), Filter.inArray("a", asList(0, 1, 4))));
1214+
checkOnlineAndOfflineResultsMatch(query3, "doc1", "doc4", "doc5");
1215+
1216+
// a IN [2,3] && a IN [0,1,4] is never true and so the result should be an empty set.
1217+
Query query4 =
1218+
collection.where(
1219+
Filter.and(Filter.inArray("a", asList(2, 3)), Filter.inArray("a", asList(0, 1, 4))));
1220+
checkOnlineAndOfflineResultsMatch(query4);
1221+
1222+
// a IN [0,3] || a IN [0,2] should union them (similar to: a IN [0,2,3]).
1223+
Query query5 =
1224+
collection.where(
1225+
Filter.or(Filter.inArray("a", asList(0, 3)), Filter.inArray("a", asList(0, 2))));
1226+
checkOnlineAndOfflineResultsMatch(query5, "doc3", "doc6");
1227+
1228+
// Nested composite filter on the same field.
1229+
Query query6 =
1230+
collection.where(
1231+
Filter.and(
1232+
Filter.inArray("a", asList(1, 3)),
1233+
Filter.or(
1234+
Filter.inArray("a", asList(0, 2)),
1235+
Filter.and(
1236+
Filter.greaterThanOrEqualTo("b", 1), Filter.inArray("a", asList(1, 3))))));
1237+
checkOnlineAndOfflineResultsMatch(query6, "doc3", "doc4");
1238+
1239+
// Nested composite filter on different fields.
1240+
Query query7 =
1241+
collection.where(
1242+
Filter.and(
1243+
Filter.inArray("b", asList(0, 3)),
1244+
Filter.or(
1245+
Filter.inArray("b", asList(1)),
1246+
Filter.and(
1247+
Filter.inArray("b", asList(2, 3)), Filter.inArray("a", asList(1, 3))))));
1248+
checkOnlineAndOfflineResultsMatch(query7, "doc4");
1249+
}
1250+
1251+
@Ignore
1252+
@Test
1253+
public void testUsingInWithArrayContainsAny() {
1254+
Map<String, Map<String, Object>> testDocs =
1255+
map(
1256+
"doc1", map("a", 1, "b", asList(0)),
1257+
"doc2", map("b", asList(1)),
1258+
"doc3", map("a", 3, "b", asList(2, 7), "c", 10),
1259+
"doc4", map("a", 1, "b", asList(3, 7)),
1260+
"doc5", map("a", 1),
1261+
"doc6", map("a", 2, "c", 20));
1262+
CollectionReference collection = testCollectionWithDocs(testDocs);
1263+
1264+
Query query1 =
1265+
collection.where(
1266+
Filter.or(
1267+
Filter.inArray("a", asList(2, 3)), Filter.arrayContainsAny("b", asList(0, 7))));
1268+
checkOnlineAndOfflineResultsMatch(query1, "doc1", "doc3", "doc4", "doc6");
1269+
1270+
Query query2 =
1271+
collection.where(
1272+
Filter.and(
1273+
Filter.inArray("a", asList(2, 3)), Filter.arrayContainsAny("b", asList(0, 7))));
1274+
checkOnlineAndOfflineResultsMatch(query2, "doc3");
1275+
1276+
Query query3 =
1277+
collection.where(
1278+
Filter.or(
1279+
Filter.and(Filter.inArray("a", asList(2, 3)), Filter.equalTo("c", 10)),
1280+
Filter.arrayContainsAny("b", asList(0, 7))));
1281+
checkOnlineAndOfflineResultsMatch(query3, "doc1", "doc3", "doc4");
1282+
1283+
Query query4 =
1284+
collection.where(
1285+
Filter.and(
1286+
Filter.inArray("a", asList(2, 3)),
1287+
Filter.or(Filter.arrayContainsAny("b", asList(0, 7)), Filter.equalTo("c", 20))));
1288+
checkOnlineAndOfflineResultsMatch(query4, "doc3", "doc6");
1289+
}
1290+
1291+
@Ignore
1292+
@Test
1293+
public void testUsingInWithArrayContains() {
1294+
Map<String, Map<String, Object>> testDocs =
1295+
map(
1296+
"doc1", map("a", 1, "b", asList(0)),
1297+
"doc2", map("b", asList(1)),
1298+
"doc3", map("a", 3, "b", asList(2, 7)),
1299+
"doc4", map("a", 1, "b", asList(3, 7)),
1300+
"doc5", map("a", 1),
1301+
"doc6", map("a", 2));
1302+
CollectionReference collection = testCollectionWithDocs(testDocs);
1303+
1304+
Query query1 =
1305+
collection.where(
1306+
Filter.or(Filter.inArray("a", asList(2, 3)), Filter.arrayContains("b", 3)));
1307+
checkOnlineAndOfflineResultsMatch(query1, "doc3", "doc4", "doc6");
1308+
1309+
Query query2 =
1310+
collection.where(
1311+
Filter.and(Filter.inArray("a", asList(2, 3)), Filter.arrayContains("b", 7)));
1312+
checkOnlineAndOfflineResultsMatch(query2, "doc3");
1313+
1314+
Query query3 =
1315+
collection.where(
1316+
Filter.or(
1317+
Filter.inArray("a", asList(2, 3)),
1318+
Filter.and(Filter.arrayContains("b", 3), Filter.equalTo("a", 1))));
1319+
checkOnlineAndOfflineResultsMatch(query3, "doc3", "doc4", "doc6");
1320+
1321+
Query query4 =
1322+
collection.where(
1323+
Filter.and(
1324+
Filter.inArray("a", asList(2, 3)),
1325+
Filter.or(Filter.arrayContains("b", 7), Filter.equalTo("a", 1))));
1326+
checkOnlineAndOfflineResultsMatch(query4, "doc3");
1327+
}
1328+
1329+
@Ignore
1330+
@Test
1331+
public void testOrderByEquality() {
1332+
Map<String, Map<String, Object>> testDocs =
1333+
map(
1334+
"doc1", map("a", 1, "b", asList(0)),
1335+
"doc2", map("b", asList(1)),
1336+
"doc3", map("a", 3, "b", asList(2, 7), "c", 10),
1337+
"doc4", map("a", 1, "b", asList(3, 7)),
1338+
"doc5", map("a", 1),
1339+
"doc6", map("a", 2, "c", 20));
1340+
CollectionReference collection = testCollectionWithDocs(testDocs);
1341+
1342+
Query query1 = collection.where(Filter.equalTo("a", 1)).orderBy("a");
1343+
checkOnlineAndOfflineResultsMatch(query1, "doc1", "doc4", "doc5");
1344+
1345+
Query query2 = collection.where(Filter.inArray("a", asList(2, 3))).orderBy("a");
1346+
checkOnlineAndOfflineResultsMatch(query2, "doc6", "doc3");
1347+
}
11811348
}

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

Lines changed: 0 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -557,31 +557,6 @@ public void queriesWithMultipleNotEqualAndInequalitiesFail() {
557557
+ "same field. But you have filters on 'x' and 'y'");
558558
}
559559

560-
@Test
561-
public void queriesWithMultipleArrayFiltersFail() {
562-
expectError(
563-
() -> testCollection().whereArrayContains("foo", 1).whereArrayContains("foo", 2),
564-
"Invalid Query. You cannot use more than one 'array_contains' filter.");
565-
566-
expectError(
567-
() ->
568-
testCollection()
569-
.whereArrayContains("foo", 1)
570-
.whereArrayContainsAny("foo", asList(1, 2)),
571-
"Invalid Query. You cannot use 'array_contains_any' filters with 'array_contains' filters.");
572-
573-
expectError(
574-
() ->
575-
testCollection()
576-
.whereArrayContainsAny("foo", asList(1, 2))
577-
.whereArrayContains("foo", 1),
578-
"Invalid Query. You cannot use 'array_contains' filters with 'array_contains_any' filters.");
579-
580-
expectError(
581-
() -> testCollection().whereNotIn("foo", asList(1, 2)).whereArrayContains("foo", 1),
582-
"Invalid Query. You cannot use 'array_contains' filters with 'not_in' filters.");
583-
}
584-
585560
@Test
586561
public void queriesWithNotEqualAndNotInFiltersFail() {
587562
expectError(
@@ -595,44 +570,12 @@ public void queriesWithNotEqualAndNotInFiltersFail() {
595570

596571
@Test
597572
public void queriesWithMultipleDisjunctiveFiltersFail() {
598-
expectError(
599-
() -> testCollection().whereIn("foo", asList(1, 2)).whereIn("bar", asList(1, 2)),
600-
"Invalid Query. You cannot use more than one 'in' filter.");
601-
602573
expectError(
603574
() -> testCollection().whereNotIn("foo", asList(1, 2)).whereNotIn("bar", asList(1, 2)),
604575
"All where filters with an inequality (notEqualTo, notIn, lessThan, "
605576
+ "lessThanOrEqualTo, greaterThan, or greaterThanOrEqualTo) must be on the "
606577
+ "same field. But you have filters on 'foo' and 'bar'");
607578

608-
expectError(
609-
() ->
610-
testCollection()
611-
.whereArrayContainsAny("foo", asList(1, 2))
612-
.whereArrayContainsAny("bar", asList(1, 2)),
613-
"Invalid Query. You cannot use more than one 'array_contains_any' filter.");
614-
615-
expectError(
616-
() ->
617-
testCollection()
618-
.whereArrayContainsAny("foo", asList(1, 2))
619-
.whereIn("bar", asList(1, 2)),
620-
"Invalid Query. You cannot use 'in' filters with 'array_contains_any' filters.");
621-
622-
expectError(
623-
() ->
624-
testCollection()
625-
.whereIn("bar", asList(1, 2))
626-
.whereArrayContainsAny("foo", asList(1, 2)),
627-
"Invalid Query. You cannot use 'array_contains_any' filters with 'in' filters.");
628-
629-
expectError(
630-
() ->
631-
testCollection()
632-
.whereArrayContainsAny("foo", asList(1, 2))
633-
.whereNotIn("bar", asList(1, 2)),
634-
"Invalid Query. You cannot use 'not_in' filters with 'array_contains_any' filters.");
635-
636579
expectError(
637580
() ->
638581
testCollection()
@@ -647,61 +590,6 @@ public void queriesWithMultipleDisjunctiveFiltersFail() {
647590
expectError(
648591
() -> testCollection().whereIn("bar", asList(1, 2)).whereNotIn("foo", asList(1, 2)),
649592
"Invalid Query. You cannot use 'not_in' filters with 'in' filters.");
650-
651-
// This is redundant with the above tests, but makes sure our validation doesn't get confused.
652-
expectError(
653-
() ->
654-
testCollection()
655-
.whereIn("bar", asList(1, 2))
656-
.whereArrayContains("foo", 1)
657-
.whereArrayContainsAny("foo", asList(1, 2)),
658-
"Invalid Query. You cannot use 'array_contains_any' filters with 'in' filters.");
659-
660-
expectError(
661-
() ->
662-
testCollection()
663-
.whereArrayContains("foo", 1)
664-
.whereIn("bar", asList(1, 2))
665-
.whereArrayContainsAny("foo", asList(1, 2)),
666-
"Invalid Query. You cannot use 'array_contains_any' filters with 'array_contains' filters.");
667-
668-
expectError(
669-
() ->
670-
testCollection()
671-
.whereNotIn("bar", asList(1, 2))
672-
.whereArrayContains("foo", 1)
673-
.whereArrayContainsAny("foo", asList(1, 2)),
674-
"Invalid Query. You cannot use 'array_contains' filters with 'not_in' filters.");
675-
676-
expectError(
677-
() ->
678-
testCollection()
679-
.whereArrayContains("foo", 1)
680-
.whereIn("foo", asList(1, 2))
681-
.whereNotIn("bar", asList(1, 2)),
682-
"Invalid Query. You cannot use 'not_in' filters with 'array_contains' filters.");
683-
}
684-
685-
@Test
686-
public void queriesCanUseInWithArrayContains() {
687-
testCollection().whereArrayContains("foo", 1).whereIn("bar", asList(1, 2));
688-
testCollection().whereIn("bar", asList(1, 2)).whereArrayContains("foo", 1);
689-
690-
expectError(
691-
() ->
692-
testCollection()
693-
.whereIn("bar", asList(1, 2))
694-
.whereArrayContains("foo", 1)
695-
.whereArrayContains("foo", 1),
696-
"Invalid Query. You cannot use more than one 'array_contains' filter.");
697-
698-
expectError(
699-
() ->
700-
testCollection()
701-
.whereArrayContains("foo", 1)
702-
.whereIn("bar", asList(1, 2))
703-
.whereIn("bar", asList(1, 2)),
704-
"Invalid Query. You cannot use more than one 'in' filter.");
705593
}
706594

707595
@Test
@@ -717,22 +605,6 @@ public void queriesInAndArrayContainsAnyArrayRules() {
717605
expectError(
718606
() -> testCollection().whereArrayContainsAny("bar", asList()),
719607
"Invalid Query. A non-empty array is required for 'array_contains_any' filters.");
720-
721-
expectError(
722-
// The 10 element max includes duplicates.
723-
() -> testCollection().whereIn("bar", asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9)),
724-
"Invalid Query. 'in' filters support a maximum of 10 elements in the value array.");
725-
726-
expectError(
727-
// The 10 element max includes duplicates.
728-
() -> testCollection().whereNotIn("bar", asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9)),
729-
"Invalid Query. 'not_in' filters support a maximum of 10 elements in the value array.");
730-
731-
expectError(
732-
// The 10 element max includes duplicates.
733-
() ->
734-
testCollection().whereArrayContainsAny("bar", asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9)),
735-
"Invalid Query. 'array_contains_any' filters support a maximum of 10 elements in the value array.");
736608
}
737609

738610
@Test

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

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -540,12 +540,6 @@ private void validateDisjunctiveFilterElements(Object value, Operator op) {
540540
throw new IllegalArgumentException(
541541
"Invalid Query. A non-empty array is required for '" + op.toString() + "' filters.");
542542
}
543-
if (((List) value).size() > 10) {
544-
throw new IllegalArgumentException(
545-
"Invalid Query. '"
546-
+ op.toString()
547-
+ "' filters support a maximum of 10 elements in the value array.");
548-
}
549543
}
550544

551545
private void validateOrderByFieldMatchesInequality(
@@ -566,36 +560,26 @@ private void validateOrderByFieldMatchesInequality(
566560
/**
567561
* Given an operator, returns the set of operators that cannot be used with it.
568562
*
563+
* <p>This is not a comprehensive check, and this function should be removed in the long term.
564+
* Validations should occur in the Firestore backend.
565+
*
569566
* <p>Operators in a query must adhere to the following set of rules:
570567
*
571568
* <ol>
572-
* <li>Only one array operator is allowed.
573-
* <li>Only one disjunctive operator is allowed.
574-
* <li>NOT_EQUAL cannot be used with another NOT_EQUAL operator.
569+
* <li>Only one inequality per query.
575570
* <li>NOT_IN cannot be used with array, disjunctive, or NOT_EQUAL operators.
576571
* </ol>
577-
*
578-
* <p>Array operators: ARRAY_CONTAINS, ARRAY_CONTAINS_ANY Disjunctive operators: IN,
579-
* ARRAY_CONTAINS_ANY, NOT_IN
580572
*/
581573
private List<Operator> conflictingOps(Operator op) {
582574
switch (op) {
583575
case NOT_EQUAL:
584576
return Arrays.asList(Operator.NOT_EQUAL, Operator.NOT_IN);
585-
case ARRAY_CONTAINS:
586-
return Arrays.asList(Operator.ARRAY_CONTAINS, Operator.ARRAY_CONTAINS_ANY, Operator.NOT_IN);
587-
case IN:
588-
return Arrays.asList(Operator.ARRAY_CONTAINS_ANY, Operator.IN, Operator.NOT_IN);
589577
case ARRAY_CONTAINS_ANY:
590-
return Arrays.asList(
591-
Operator.ARRAY_CONTAINS, Operator.ARRAY_CONTAINS_ANY, Operator.IN, Operator.NOT_IN);
578+
case IN:
579+
return Arrays.asList(Operator.NOT_IN);
592580
case NOT_IN:
593581
return Arrays.asList(
594-
Operator.ARRAY_CONTAINS,
595-
Operator.ARRAY_CONTAINS_ANY,
596-
Operator.IN,
597-
Operator.NOT_IN,
598-
Operator.NOT_EQUAL);
582+
Operator.ARRAY_CONTAINS_ANY, Operator.IN, Operator.NOT_IN, Operator.NOT_EQUAL);
599583
default:
600584
return new ArrayList<>();
601585
}

0 commit comments

Comments
 (0)