Skip to content

Remove some query restrictions and add integration tests #4231

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

Merged
merged 12 commits into from
Feb 15, 2023
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -1178,4 +1178,167 @@ public void testOrQueriesWithArrayMembership() {
"doc4",
"doc6");
}

@Test
public void testMultipleInOps() {
Map<String, Map<String, Object>> testDocs =
map(
"doc1", map("a", 1, "b", 0),
"doc2", map("b", 1),
"doc3", map("a", 3, "b", 2),
"doc4", map("a", 1, "b", 3),
"doc5", map("a", 1),
"doc6", map("a", 2));
CollectionReference collection = testCollectionWithDocs(testDocs);

// Two IN operations on different fields with disjunction.
Query query1 =
collection
.where(Filter.or(Filter.inArray("a", asList(2, 3)), Filter.inArray("b", asList(0, 2))))
.orderBy("a");
checkOnlineAndOfflineResultsMatch(query1, "doc1", "doc6", "doc3");

// Two IN operations on different fields with conjunction.
Query query2 =
collection
.where(Filter.and(Filter.inArray("a", asList(2, 3)), Filter.inArray("b", asList(0, 2))))
.orderBy("a");
checkOnlineAndOfflineResultsMatch(query2, "doc3");

// Two IN operations on the same field.
// a IN [1,2,3] && a IN [0,1,4] should result in "a==1".
Query query3 =
collection.where(
Filter.and(Filter.inArray("a", asList(1, 2, 3)), Filter.inArray("a", asList(0, 1, 4))));
checkOnlineAndOfflineResultsMatch(query3, "doc1", "doc4", "doc5");

// a IN [2,3] && a IN [0,1,4] is never true and so the result should be an empty set.
Query query4 =
collection.where(
Filter.and(Filter.inArray("a", asList(2, 3)), Filter.inArray("a", asList(0, 1, 4))));
checkOnlineAndOfflineResultsMatch(query4);

// a IN [0,3] || a IN [0,2] should union them (similar to: a IN [0,2,3]).
Query query5 =
collection.where(
Filter.or(Filter.inArray("a", asList(0, 3)), Filter.inArray("a", asList(0, 2))));
checkOnlineAndOfflineResultsMatch(query5, "doc3", "doc6");

// Nested composite filter on the same field.
Query query6 =
collection.where(
Filter.and(
Filter.inArray("a", asList(1, 3)),
Filter.or(
Filter.inArray("a", asList(0, 2)),
Filter.and(
Filter.greaterThanOrEqualTo("b", 1), Filter.inArray("a", asList(1, 3))))));
checkOnlineAndOfflineResultsMatch(query6, "doc3", "doc4");

// Nested composite filter on different fields.
Query query7 =
collection.where(
Filter.and(
Filter.inArray("b", asList(0, 3)),
Filter.or(
Filter.inArray("b", asList(1)),
Filter.and(
Filter.inArray("b", asList(2, 3)), Filter.inArray("a", asList(1, 3))))));
checkOnlineAndOfflineResultsMatch(query7, "doc4");
}

@Test
public void testUsingInWithArrayContainsAny() {
Map<String, Map<String, Object>> testDocs =
map(
"doc1", map("a", 1, "b", asList(0)),
"doc2", map("b", asList(1)),
"doc3", map("a", 3, "b", asList(2, 7), "c", 10),
"doc4", map("a", 1, "b", asList(3, 7)),
"doc5", map("a", 1),
"doc6", map("a", 2, "c", 20));
CollectionReference collection = testCollectionWithDocs(testDocs);

Query query1 =
collection.where(
Filter.or(
Filter.inArray("a", asList(2, 3)), Filter.arrayContainsAny("b", asList(0, 7))));
checkOnlineAndOfflineResultsMatch(query1, "doc1", "doc3", "doc4", "doc6");

Query query2 =
collection.where(
Filter.and(
Filter.inArray("a", asList(2, 3)), Filter.arrayContainsAny("b", asList(0, 7))));
checkOnlineAndOfflineResultsMatch(query2, "doc3");

Query query3 =
collection.where(
Filter.or(
Filter.and(Filter.inArray("a", asList(2, 3)), Filter.equalTo("c", 10)),
Filter.arrayContainsAny("b", asList(0, 7))));
checkOnlineAndOfflineResultsMatch(query3, "doc1", "doc3", "doc4");

Query query4 =
collection.where(
Filter.and(
Filter.inArray("a", asList(2, 3)),
Filter.or(Filter.arrayContainsAny("b", asList(0, 7)), Filter.equalTo("c", 20))));
checkOnlineAndOfflineResultsMatch(query4, "doc3", "doc6");
}

@Test
public void testUsingInWithArrayContains() {
Map<String, Map<String, Object>> testDocs =
map(
"doc1", map("a", 1, "b", asList(0)),
"doc2", map("b", asList(1)),
"doc3", map("a", 3, "b", asList(2, 7)),
"doc4", map("a", 1, "b", asList(3, 7)),
"doc5", map("a", 1),
"doc6", map("a", 2));
CollectionReference collection = testCollectionWithDocs(testDocs);

Query query1 =
collection.where(
Filter.or(Filter.inArray("a", asList(2, 3)), Filter.arrayContains("b", 3)));
checkOnlineAndOfflineResultsMatch(query1, "doc3", "doc4", "doc6");

Query query2 =
collection.where(
Filter.and(Filter.inArray("a", asList(2, 3)), Filter.arrayContains("b", 7)));
checkOnlineAndOfflineResultsMatch(query2, "doc3");

Query query3 =
collection.where(
Filter.or(
Filter.inArray("a", asList(2, 3)),
Filter.and(Filter.arrayContains("b", 3), Filter.equalTo("a", 1))));
checkOnlineAndOfflineResultsMatch(query3, "doc3", "doc4", "doc6");

Query query4 =
collection.where(
Filter.and(
Filter.inArray("a", asList(2, 3)),
Filter.or(Filter.arrayContains("b", 7), Filter.equalTo("a", 1))));
checkOnlineAndOfflineResultsMatch(query4, "doc3");
}

@Test
public void testOrderByEquality() {
Map<String, Map<String, Object>> testDocs =
map(
"doc1", map("a", 1, "b", asList(0)),
"doc2", map("b", asList(1)),
"doc3", map("a", 3, "b", asList(2, 7), "c", 10),
"doc4", map("a", 1, "b", asList(3, 7)),
"doc5", map("a", 1),
"doc6", map("a", 2, "c", 20));
CollectionReference collection = testCollectionWithDocs(testDocs);

Query query1 = collection.where(Filter.equalTo("a", 1)).orderBy("a");
checkOnlineAndOfflineResultsMatch(query1, "doc1", "doc4", "doc5");

Query query2 = collection.where(Filter.inArray("a", asList(2, 3))).orderBy("a");
checkOnlineAndOfflineResultsMatch(query2, "doc6", "doc3");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static com.google.firebase.firestore.Filter.equalTo;
import static com.google.firebase.firestore.Filter.greaterThan;
import static com.google.firebase.firestore.Filter.inArray;
import static com.google.firebase.firestore.Filter.notEqualTo;
import static com.google.firebase.firestore.Filter.notInArray;
import static com.google.firebase.firestore.Filter.or;
import static com.google.firebase.firestore.testutil.Assert.assertThrows;
Expand Down Expand Up @@ -595,10 +596,6 @@ public void queriesWithNotEqualAndNotInFiltersFail() {

@Test
public void queriesWithMultipleDisjunctiveFiltersFail() {
expectError(
() -> testCollection().whereIn("foo", asList(1, 2)).whereIn("bar", asList(1, 2)),
"Invalid Query. You cannot use more than one 'in' filter.");

expectError(
() -> testCollection().whereNotIn("foo", asList(1, 2)).whereNotIn("bar", asList(1, 2)),
"All where filters with an inequality (notEqualTo, notIn, lessThan, "
Expand All @@ -612,20 +609,6 @@ public void queriesWithMultipleDisjunctiveFiltersFail() {
.whereArrayContainsAny("bar", asList(1, 2)),
"Invalid Query. You cannot use more than one 'array_contains_any' filter.");

expectError(
() ->
testCollection()
.whereArrayContainsAny("foo", asList(1, 2))
.whereIn("bar", asList(1, 2)),
"Invalid Query. You cannot use 'in' filters with 'array_contains_any' filters.");

expectError(
() ->
testCollection()
.whereIn("bar", asList(1, 2))
.whereArrayContainsAny("foo", asList(1, 2)),
"Invalid Query. You cannot use 'array_contains_any' filters with 'in' filters.");

expectError(
() ->
testCollection()
Expand Down Expand Up @@ -655,7 +638,7 @@ public void queriesWithMultipleDisjunctiveFiltersFail() {
.whereIn("bar", asList(1, 2))
.whereArrayContains("foo", 1)
.whereArrayContainsAny("foo", asList(1, 2)),
"Invalid Query. You cannot use 'array_contains_any' filters with 'in' filters.");
"Invalid Query. You cannot use 'array_contains_any' filters with 'array_contains' filters.");

expectError(
() ->
Expand Down Expand Up @@ -694,14 +677,6 @@ public void queriesCanUseInWithArrayContains() {
.whereArrayContains("foo", 1)
.whereArrayContains("foo", 1),
"Invalid Query. You cannot use more than one 'array_contains' filter.");

expectError(
() ->
testCollection()
.whereArrayContains("foo", 1)
.whereIn("bar", asList(1, 2))
.whereIn("bar", asList(1, 2)),
"Invalid Query. You cannot use more than one 'in' filter.");
}

@Test
Expand All @@ -719,20 +694,31 @@ public void queriesInAndArrayContainsAnyArrayRules() {
"Invalid Query. A non-empty array is required for 'array_contains_any' filters.");

expectError(
// The 10 element max includes duplicates.
() -> testCollection().whereIn("bar", asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9)),
"Invalid Query. 'in' filters support a maximum of 10 elements in the value array.");
// The 30 element max includes duplicates.
() ->
testCollection()
.whereIn(
"bar",
asList(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31)),
"Invalid Query. 'in' filters support a maximum of 30 elements in the value array.");

expectError(
// The 10 element max includes duplicates.
() -> testCollection().whereNotIn("bar", asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9)),
// The 30 element max includes duplicates.
() -> testCollection().whereNotIn("bar", asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)),
"Invalid Query. 'not_in' filters support a maximum of 10 elements in the value array.");

expectError(
// The 10 element max includes duplicates.
() ->
testCollection().whereArrayContainsAny("bar", asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9)),
"Invalid Query. 'array_contains_any' filters support a maximum of 10 elements in the value array.");
testCollection()
.whereArrayContainsAny(
"bar",
asList(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31)),
"Invalid Query. 'array_contains_any' filters support a maximum of 30 elements in the value array.");
}

@Test
Expand Down Expand Up @@ -890,6 +876,54 @@ public void testInvalidQueryFilters() {
reason);
}

@Test
public void testDisjunctionWithNotInFilter() {
CollectionReference collection = testCollection();
String reason = "The 'notIn' filter should not be used in a query that contains a disjunction.";
expectError(
() -> collection.where(or(equalTo("a", "b"), notInArray("c", asList(1, 2)))), reason);
expectError(
() ->
collection.where(
and(equalTo("x", "y"), or(equalTo("a", "b"), notInArray("c", asList(1, 2))))),
reason);
expectError(
() ->
collection.where(
or(equalTo("x", "y"), and(equalTo("a", "b"), notInArray("c", asList(1, 2))))),
reason);
}

@Test
public void testOrderByEqualityWithDisjunctions() {
CollectionReference collection = testCollection();
String reason =
"Performing an equality ('in' and '==') and an inequality (notEqualTo, notIn, "
+ "lessThan, lessThanOrEqualTo, greaterThan, or greaterThanOrEqualTo) on the same field "
+ "is not yet allowed in disjunctions.";
// Using ==
expectError(() -> collection.where(or(equalTo("a", "b"), notEqualTo("a", "c"))), reason);
expectError(
() -> collection.where(and(greaterThan("a", 5), or(equalTo("a", 6), equalTo("b", "c")))),
reason);
expectError(
() -> collection.where(or(greaterThan("a", 5), and(equalTo("a", 6), equalTo("b", "c")))),
reason);

// Using 'in'
expectError(() -> collection.where(or(inArray("a", asList(1, 2)), notEqualTo("a", 5))), reason);
expectError(
() ->
collection.where(
and(greaterThan("a", 5), or(inArray("a", asList(1, 2)), equalTo("b", "c")))),
reason);
expectError(
() ->
collection.where(
or(greaterThan("a", 5), and(inArray("a", asList(1, 2)), equalTo("b", "c")))),
reason);
}

// Helpers

/** Performs a write using each write API and makes sure it succeeds. */
Expand Down
Loading