diff --git a/Firestore/Example/Tests/Integration/API/FIRCompositeIndexQueryTests.mm b/Firestore/Example/Tests/Integration/API/FIRCompositeIndexQueryTests.mm index 1358d0975ec..769fc935885 100644 --- a/Firestore/Example/Tests/Integration/API/FIRCompositeIndexQueryTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRCompositeIndexQueryTests.mm @@ -122,9 +122,16 @@ - (NSString *)toHashedId:(NSString *)docId { // Asserts that the result of running the query while online (against the backend/emulator) is // the same as running it while offline. The expected document Ids are hashed to match the // actual document IDs created by the test helper. -- (void)assertOnlineAndOfflineResultsMatch:(FIRQuery *)query +- (void)assertOnlineAndOfflineResultsMatch:(FIRCollectionReference *)collection + withQuery:(FIRQuery *)query expectedDocs:(NSArray *)expectedDocs { - [self checkOnlineAndOfflineQuery:query matchesResult:[self toHashedIds:expectedDocs]]; + // `checkOnlineAndOfflineCollection` first makes sure all documents needed for + // `query` are in the cache. It does so making a `get` on the first argument. + // Since *all* composite index tests use the same collection, this is very inefficient to do. + // Therefore, we should only do so for tests where `TEST_ID_FIELD` matches the current test. + [self checkOnlineAndOfflineCollection:[self compositeIndexQuery:collection] + query:query + matchesResult:[self toHashedIds:expectedDocs]]; } // Asserts that the IDs in the query snapshot matches the expected Ids. The expected document @@ -219,7 +226,8 @@ - (void)testOrQueriesWithCompositeIndexes { [FIRFilter filterWhereField:@"a" isGreaterThan:@2], [FIRFilter filterWhereField:@"b" isEqualTo:@1] ]]]; - [self assertOnlineAndOfflineResultsMatch:[self compositeIndexQuery:query1] + [self assertOnlineAndOfflineResultsMatch:collRef + withQuery:[self compositeIndexQuery:query1] expectedDocs:@[ @"doc5", @"doc2", @"doc3" ]]; // Test with limits (implicit order by ASC): (a==1) || (b > 0) LIMIT 2 @@ -228,7 +236,8 @@ - (void)testOrQueriesWithCompositeIndexes { [FIRFilter filterWhereField:@"a" isEqualTo:@1], [FIRFilter filterWhereField:@"b" isGreaterThan:@0] ]]]; - [self assertOnlineAndOfflineResultsMatch:[[self compositeIndexQuery:query2] queryLimitedTo:2] + [self assertOnlineAndOfflineResultsMatch:collRef + withQuery:[[self compositeIndexQuery:query2] queryLimitedTo:2] expectedDocs:@[ @"doc1", @"doc2" ]]; // Test with limits (explicit order by): (a==1) || (b > 0) LIMIT_TO_LAST 2 @@ -238,7 +247,8 @@ - (void)testOrQueriesWithCompositeIndexes { [FIRFilter filterWhereField:@"a" isEqualTo:@1], [FIRFilter filterWhereField:@"b" isGreaterThan:@0] ]]]; - [self assertOnlineAndOfflineResultsMatch:[[[self compositeIndexQuery:query3] queryLimitedToLast:2] + [self assertOnlineAndOfflineResultsMatch:collRef + withQuery:[[[self compositeIndexQuery:query3] queryLimitedToLast:2] queryOrderedByField:@"b"] expectedDocs:@[ @"doc3", @"doc4" ]]; @@ -248,7 +258,8 @@ - (void)testOrQueriesWithCompositeIndexes { [FIRFilter filterWhereField:@"a" isEqualTo:@2], [FIRFilter filterWhereField:@"b" isEqualTo:@1] ]]]; - [self assertOnlineAndOfflineResultsMatch:[[[self compositeIndexQuery:query4] queryLimitedTo:1] + [self assertOnlineAndOfflineResultsMatch:collRef + withQuery:[[[self compositeIndexQuery:query4] queryLimitedTo:1] queryOrderedByField:@"a"] expectedDocs:@[ @"doc5" ]]; @@ -258,7 +269,8 @@ - (void)testOrQueriesWithCompositeIndexes { [FIRFilter filterWhereField:@"a" isEqualTo:@2], [FIRFilter filterWhereField:@"b" isEqualTo:@1] ]]]; - [self assertOnlineAndOfflineResultsMatch:[[[self compositeIndexQuery:query5] queryLimitedToLast:1] + [self assertOnlineAndOfflineResultsMatch:collRef + withQuery:[[[self compositeIndexQuery:query5] queryLimitedToLast:1] queryOrderedByField:@"a"] expectedDocs:@[ @"doc2" ]]; } @@ -886,7 +898,8 @@ - (void)testMultipleInequalityFromCacheAndFromServer { // implicit AND: a != 1 && b < 2 FIRQuery *query = [[collRef queryWhereField:@"a" isNotEqualTo:@1] queryWhereField:@"b" isLessThan:@2]; - [self assertOnlineAndOfflineResultsMatch:[self compositeIndexQuery:query] + [self assertOnlineAndOfflineResultsMatch:collRef + withQuery:[self compositeIndexQuery:query] expectedDocs:@[ @"doc2" ]]; // explicit AND: a != 1 && b < 2 @@ -895,7 +908,8 @@ - (void)testMultipleInequalityFromCacheAndFromServer { [FIRFilter filterWhereField:@"a" isNotEqualTo:@1], [FIRFilter filterWhereField:@"b" isLessThan:@2] ]]]; - [self assertOnlineAndOfflineResultsMatch:[self compositeIndexQuery:query] + [self assertOnlineAndOfflineResultsMatch:collRef + withQuery:[self compositeIndexQuery:query] expectedDocs:@[ @"doc2" ]]; // explicit AND: a < 3 && b not-in [2, 3] @@ -905,14 +919,16 @@ - (void)testMultipleInequalityFromCacheAndFromServer { [FIRFilter filterWhereField:@"a" isLessThan:@3], [FIRFilter filterWhereField:@"b" notIn:@[ @2, @3 ]] ]]]; - [self assertOnlineAndOfflineResultsMatch:[self compositeIndexQuery:query] + [self assertOnlineAndOfflineResultsMatch:collRef + withQuery:[self compositeIndexQuery:query] expectedDocs:@[ @"doc1", @"doc5", @"doc2" ]]; // a <3 && b != 0, ordered by: b desc, a desc, __name__ desc query = [[[[collRef queryWhereField:@"a" isLessThan:@3] queryWhereField:@"b" isNotEqualTo:@0] queryOrderedByField:@"b" descending:YES] queryLimitedTo:2]; - [self assertOnlineAndOfflineResultsMatch:[self compositeIndexQuery:query] + [self assertOnlineAndOfflineResultsMatch:collRef + withQuery:[self compositeIndexQuery:query] expectedDocs:@[ @"doc4", @"doc2" ]]; // explicit OR: a>2 || b<1. @@ -921,7 +937,8 @@ - (void)testMultipleInequalityFromCacheAndFromServer { [FIRFilter filterWhereField:@"a" isGreaterThan:@2], [FIRFilter filterWhereField:@"b" isLessThan:@1] ]]]; - [self assertOnlineAndOfflineResultsMatch:[self compositeIndexQuery:query] + [self assertOnlineAndOfflineResultsMatch:collRef + withQuery:[self compositeIndexQuery:query] expectedDocs:@[ @"doc1", @"doc3" ]]; } diff --git a/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm b/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm index 71e1172190d..c8835d53e64 100644 --- a/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm @@ -555,15 +555,18 @@ - (void)testSDKUsesNotEqualFiltersSameAsServer { // populate cache with all documents first to ensure getDocsFromCache() scans all docs [self readDocumentSetForRef:collection]; - [self checkOnlineAndOfflineQuery:[collection queryWhereField:@"zip" isNotEqualTo:@98101] - matchesResult:@[ @"a", @"b", @"d", @"e", @"f", @"g", @"h" ]]; - - [self checkOnlineAndOfflineQuery:[collection queryWhereField:@"zip" isNotEqualTo:@(NAN)] - matchesResult:@[ @"b", @"c", @"d", @"e", @"f", @"g", @"h" ]]; - - [self checkOnlineAndOfflineQuery:[collection queryWhereField:@"zip" - isNotEqualTo:@[ [NSNull null] ]] - matchesResult:@[ @"a", @"b", @"c", @"d", @"e", @"f", @"g", @"h" ]]; + [self checkOnlineAndOfflineCollection:collection + query:[collection queryWhereField:@"zip" isNotEqualTo:@98101] + matchesResult:@[ @"a", @"b", @"d", @"e", @"f", @"g", @"h" ]]; + + [self checkOnlineAndOfflineCollection:collection + query:[collection queryWhereField:@"zip" isNotEqualTo:@(NAN)] + matchesResult:@[ @"b", @"c", @"d", @"e", @"f", @"g", @"h" ]]; + + [self checkOnlineAndOfflineCollection:collection + query:[collection queryWhereField:@"zip" + isNotEqualTo:@[ [NSNull null] ]] + matchesResult:@[ @"a", @"b", @"c", @"d", @"e", @"f", @"g", @"h" ]]; } - (void)testQueriesCanUseArrayContainsFilters { @@ -741,13 +744,16 @@ - (void)testSDKUsesNotInFiltersSameAsServer { // populate cache with all documents first to ensure getDocsFromCache() scans all docs [self readDocumentSetForRef:collection]; - [self checkOnlineAndOfflineQuery:[collection - queryWhereField:@"zip" - notIn:@[ @98101, @98103, @[ @98101, @98102 ] ]] - matchesResult:@[ @"a", @"b", @"d", @"e", @"g", @"h" ]]; + [self + checkOnlineAndOfflineCollection:collection + query:[collection + queryWhereField:@"zip" + notIn:@[ @98101, @98103, @[ @98101, @98102 ] ]] + matchesResult:@[ @"a", @"b", @"d", @"e", @"g", @"h" ]]; - [self checkOnlineAndOfflineQuery:[collection queryWhereField:@"zip" notIn:@[ [NSNull null] ]] - matchesResult:@[]]; + [self checkOnlineAndOfflineCollection:collection + query:[collection queryWhereField:@"zip" notIn:@[ [NSNull null] ]] + matchesResult:@[]]; } - (void)testQueriesCanUseArrayContainsAnyFilters { @@ -944,12 +950,13 @@ - (void)testSdkOrdersQueryByDocumentIdTheSameWayOnlineAndOffline { @"__id-9223372036854775808__" : @{@"a" : @1}, }]; - [self checkOnlineAndOfflineQuery:[collRef queryOrderedByFieldPath:[FIRFieldPath documentID]] - matchesResult:@[ - @"__id-9223372036854775808__", @"__id-2__", @"__id7__", @"__id12__", - @"__id9223372036854775807__", @"12", @"7", @"A", @"Aa", @"__id", @"__id1_", - @"_id1__", @"a" - ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryOrderedByFieldPath:[FIRFieldPath documentID]] + matchesResult:@[ + @"__id-9223372036854775808__", @"__id-2__", @"__id7__", @"__id12__", + @"__id9223372036854775807__", @"12", @"7", @"A", @"Aa", @"__id", + @"__id1_", @"_id1__", @"a" + ]]; } - (void)testSnapshotListenerSortsUnicodeStringsInTheSameOrderAsServer { @@ -976,7 +983,7 @@ - (void)testSnapshotListenerSortsUnicodeStringsInTheSameOrderAsServer { [registration remove]; - [self checkOnlineAndOfflineQuery:query matchesResult:expectedDocs]; + [self checkOnlineAndOfflineCollection:collRef query:query matchesResult:expectedDocs]; } - (void)testSnapshotListenerSortsUnicodeStringsInArrayInTheSameOrderAsServer { @@ -1003,7 +1010,7 @@ - (void)testSnapshotListenerSortsUnicodeStringsInArrayInTheSameOrderAsServer { [registration remove]; - [self checkOnlineAndOfflineQuery:query matchesResult:expectedDocs]; + [self checkOnlineAndOfflineCollection:collRef query:query matchesResult:expectedDocs]; } - (void)testSnapshotListenerSortsUnicodeStringsInMapInTheSameOrderAsServer { @@ -1030,7 +1037,7 @@ - (void)testSnapshotListenerSortsUnicodeStringsInMapInTheSameOrderAsServer { [registration remove]; - [self checkOnlineAndOfflineQuery:query matchesResult:expectedDocs]; + [self checkOnlineAndOfflineCollection:collRef query:query matchesResult:expectedDocs]; } - (void)testSnapshotListenerSortsUnicodeStringsInMapKeyInTheSameOrderAsServer { @@ -1057,7 +1064,7 @@ - (void)testSnapshotListenerSortsUnicodeStringsInMapKeyInTheSameOrderAsServer { [registration remove]; - [self checkOnlineAndOfflineQuery:query matchesResult:expectedDocs]; + [self checkOnlineAndOfflineCollection:collRef query:query matchesResult:expectedDocs]; } - (void)testSnapshotListenerSortsUnicodeStringsInDocumentKeyInTheSameOrderAsServer { @@ -1085,7 +1092,7 @@ - (void)testSnapshotListenerSortsUnicodeStringsInDocumentKeyInTheSameOrderAsServ [registration remove]; - [self checkOnlineAndOfflineQuery:query matchesResult:expectedDocs]; + [self checkOnlineAndOfflineCollection:collRef query:query matchesResult:expectedDocs]; } - (void)testCollectionGroupQueriesWithWhereFiltersOnArbitraryDocumentIDs { @@ -1133,8 +1140,9 @@ - (void)testOrQueries { FIRFilter *filter1 = [FIRFilter orFilterWithFilters:@[ [FIRFilter filterWhereField:@"a" isEqualTo:@1], [FIRFilter filterWhereField:@"b" isEqualTo:@1] ]]; - [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter1] - matchesResult:@[ @"doc1", @"doc2", @"doc4", @"doc5" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryWhereFilter:filter1] + matchesResult:@[ @"doc1", @"doc2", @"doc4", @"doc5" ]]; // (a==1 && b==0) || (a==3 && b==2) FIRFilter *filter2 = [FIRFilter orFilterWithFilters:@[ @@ -1145,8 +1153,9 @@ - (void)testOrQueries { [FIRFilter filterWhereField:@"a" isEqualTo:@3], [FIRFilter filterWhereField:@"b" isEqualTo:@2] ]] ]]; - [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter2] - matchesResult:@[ @"doc1", @"doc3" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryWhereFilter:filter2] + matchesResult:@[ @"doc1", @"doc3" ]]; // a==1 && (b==0 || b==3). FIRFilter *filter3 = [FIRFilter andFilterWithFilters:@[ @@ -1154,8 +1163,9 @@ - (void)testOrQueries { [FIRFilter filterWhereField:@"b" isEqualTo:@0], [FIRFilter filterWhereField:@"b" isEqualTo:@3] ]] ]]; - [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter3] - matchesResult:@[ @"doc1", @"doc4" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryWhereFilter:filter3] + matchesResult:@[ @"doc1", @"doc4" ]]; // (a==2 || b==2) && (a==3 || b==3) FIRFilter *filter4 = [FIRFilter andFilterWithFilters:@[ @@ -1166,14 +1176,17 @@ - (void)testOrQueries { [FIRFilter filterWhereField:@"a" isEqualTo:@3], [FIRFilter filterWhereField:@"b" isEqualTo:@3] ]] ]]; - [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter4] matchesResult:@[ @"doc3" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryWhereFilter:filter4] + matchesResult:@[ @"doc3" ]]; // Test with limits without orderBy (the __name__ ordering is the tie breaker). FIRFilter *filter5 = [FIRFilter orFilterWithFilters:@[ [FIRFilter filterWhereField:@"a" isEqualTo:@2], [FIRFilter filterWhereField:@"b" isEqualTo:@1] ]]; - [self checkOnlineAndOfflineQuery:[[collRef queryWhereFilter:filter5] queryLimitedTo:1] - matchesResult:@[ @"doc2" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[[collRef queryWhereFilter:filter5] queryLimitedTo:1] + matchesResult:@[ @"doc2" ]]; } - (void)testOrQueriesWithIn { @@ -1190,8 +1203,9 @@ - (void)testOrQueriesWithIn { FIRFilter *filter = [FIRFilter orFilterWithFilters:@[ [FIRFilter filterWhereField:@"a" isEqualTo:@2], [FIRFilter filterWhereField:@"b" in:@[ @2, @3 ]] ]]; - [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter] - matchesResult:@[ @"doc3", @"doc4", @"doc6" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryWhereFilter:filter] + matchesResult:@[ @"doc3", @"doc4", @"doc6" ]]; } - (void)testOrQueriesWithArrayMembership { @@ -1209,16 +1223,18 @@ - (void)testOrQueriesWithArrayMembership { [FIRFilter filterWhereField:@"a" isEqualTo:@2], [FIRFilter filterWhereField:@"b" arrayContains:@7] ]]; - [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter1] - matchesResult:@[ @"doc3", @"doc4", @"doc6" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryWhereFilter:filter1] + matchesResult:@[ @"doc3", @"doc4", @"doc6" ]]; // a==2 || b array-contains-any [0, 3] FIRFilter *filter2 = [FIRFilter orFilterWithFilters:@[ [FIRFilter filterWhereField:@"a" isEqualTo:@2], [FIRFilter filterWhereField:@"b" arrayContainsAny:@[ @0, @3 ]] ]]; - [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter2] - matchesResult:@[ @"doc1", @"doc4", @"doc6" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryWhereFilter:filter2] + matchesResult:@[ @"doc1", @"doc4", @"doc6" ]]; } - (void)testMultipleInOps { @@ -1236,8 +1252,9 @@ - (void)testMultipleInOps { [FIRFilter filterWhereField:@"a" in:@[ @2, @3 ]], [FIRFilter filterWhereField:@"b" in:@[ @0, @2 ]] ]]; - [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter1] - matchesResult:@[ @"doc1", @"doc3", @"doc6" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryWhereFilter:filter1] + matchesResult:@[ @"doc1", @"doc3", @"doc6" ]]; // Two IN operations on the same field with disjunction. // a IN [0,3] || a IN [0,2] should union them (similar to: a IN [0,2,3]). @@ -1245,8 +1262,9 @@ - (void)testMultipleInOps { [FIRFilter filterWhereField:@"a" in:@[ @0, @3 ]], [FIRFilter filterWhereField:@"a" in:@[ @0, @2 ]] ]]; - [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter2] - matchesResult:@[ @"doc3", @"doc6" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryWhereFilter:filter2] + matchesResult:@[ @"doc3", @"doc6" ]]; } - (void)testUsingInWithArrayContainsAny { @@ -1263,8 +1281,9 @@ - (void)testUsingInWithArrayContainsAny { [FIRFilter filterWhereField:@"a" in:@[ @2, @3 ]], [FIRFilter filterWhereField:@"b" arrayContainsAny:@[ @0, @7 ]] ]]; - [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter1] - matchesResult:@[ @"doc1", @"doc3", @"doc4", @"doc6" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryWhereFilter:filter1] + matchesResult:@[ @"doc1", @"doc3", @"doc4", @"doc6" ]]; FIRFilter *filter2 = [FIRFilter orFilterWithFilters:@[ [FIRFilter andFilterWithFilters:@[ @@ -1273,8 +1292,9 @@ - (void)testUsingInWithArrayContainsAny { ]], [FIRFilter filterWhereField:@"b" arrayContainsAny:@[ @0, @7 ]] ]]; - [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter2] - matchesResult:@[ @"doc1", @"doc3", @"doc4" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryWhereFilter:filter2] + matchesResult:@[ @"doc1", @"doc3", @"doc4" ]]; } - (void)testUseInWithArrayContains { @@ -1291,14 +1311,17 @@ - (void)testUseInWithArrayContains { [FIRFilter filterWhereField:@"a" in:@[ @2, @3 ]], [FIRFilter filterWhereField:@"b" arrayContainsAny:@[ @3 ]] ]]; - [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter1] - matchesResult:@[ @"doc3", @"doc4", @"doc6" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryWhereFilter:filter1] + matchesResult:@[ @"doc3", @"doc4", @"doc6" ]]; FIRFilter *filter2 = [FIRFilter andFilterWithFilters:@[ [FIRFilter filterWhereField:@"a" in:@[ @2, @3 ]], [FIRFilter filterWhereField:@"b" arrayContains:@7] ]]; - [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter2] matchesResult:@[ @"doc3" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryWhereFilter:filter2] + matchesResult:@[ @"doc3" ]]; FIRFilter *filter3 = [FIRFilter orFilterWithFilters:@[ [FIRFilter filterWhereField:@"a" in:@[ @2, @3 ]], [FIRFilter andFilterWithFilters:@[ @@ -1306,8 +1329,9 @@ - (void)testUseInWithArrayContains { isEqualTo:@1] ]] ]]; - [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter3] - matchesResult:@[ @"doc3", @"doc4", @"doc6" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryWhereFilter:filter3] + matchesResult:@[ @"doc3", @"doc4", @"doc6" ]]; FIRFilter *filter4 = [FIRFilter andFilterWithFilters:@[ [FIRFilter filterWhereField:@"a" in:@[ @2, @3 ]], [FIRFilter orFilterWithFilters:@[ @@ -1315,7 +1339,9 @@ - (void)testUseInWithArrayContains { isEqualTo:@1] ]] ]]; - [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter4] matchesResult:@[ @"doc3" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[collRef queryWhereFilter:filter4] + matchesResult:@[ @"doc3" ]]; } - (void)testOrderByEquality { @@ -1333,16 +1359,19 @@ - (void)testOrderByEquality { @"doc6" : @{@"a" : @2, @"c" : @20} }]; - [self checkOnlineAndOfflineQuery:[[collRef queryWhereFilter:[FIRFilter filterWhereField:@"a" - isEqualTo:@1]] - queryOrderedByField:@"a"] - matchesResult:@[ @"doc1", @"doc4", @"doc5" ]]; - - [self checkOnlineAndOfflineQuery:[[collRef - queryWhereFilter:[FIRFilter filterWhereField:@"a" - in:@[ @2, @3 ]]] - queryOrderedByField:@"a"] - matchesResult:@[ @"doc6", @"doc3" ]]; + [self checkOnlineAndOfflineCollection:collRef + query:[[collRef queryWhereFilter:[FIRFilter filterWhereField:@"a" + isEqualTo:@1]] + queryOrderedByField:@"a"] + matchesResult:@[ @"doc1", @"doc4", @"doc5" ]]; + + [self + checkOnlineAndOfflineCollection:collRef + query:[[collRef + queryWhereFilter:[FIRFilter filterWhereField:@"a" + in:@[ @2, @3 ]]] + queryOrderedByField:@"a"] + matchesResult:@[ @"doc6", @"doc3" ]]; } - (void)testResumingAQueryShouldUseBloomFilterToAvoidFullRequery { diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h index a63fd66cd1b..faca06dee8c 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h @@ -130,7 +130,9 @@ extern "C" { - (void)enableNetwork; -- (void)checkOnlineAndOfflineQuery:(FIRQuery *)query matchesResult:(NSArray *)expectedDocs; +- (void)checkOnlineAndOfflineCollection:(FIRQuery *)collection + query:(FIRQuery *)query + matchesResult:(NSArray *)expectedDocs; /** * "Blocks" the current thread/run loop until the block returns YES. diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm index 4b7c7b9f034..c64b1e80706 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm @@ -623,21 +623,44 @@ - (void)enableNetwork { /** * Checks that running the query while online (against the backend/emulator) results in the same - * documents as running the query while offline. It also checks that both online and offline - * query result is equal to the expected documents. + * documents as running the query while offline. If `expectedDocs` is provided, it also checks + * that both online and offline query result is equal to the expected documents. * - * @param query The query to check. - * @param expectedDocs Array of document keys that are expected to match the query. + * This function first performs a "get" for the entire COLLECTION from the server. + * It then performs the QUERY from CACHE which, results in `executeFullCollectionScan()` + * It then performs the QUERY from SERVER. + * It then performs the QUERY from CACHE again, which results in `performQueryUsingRemoteKeys()`. + * It then ensure that all the above QUERY results are the same. + * + * @param collection The collection on which the query is performed. + * @param query The query to check + * @param expectedDocs Ordered list of document keys that are expected to match the query */ -- (void)checkOnlineAndOfflineQuery:(FIRQuery *)query matchesResult:(NSArray *)expectedDocs { +- (void)checkOnlineAndOfflineCollection:(FIRQuery *)collection + query:(FIRQuery *)query + matchesResult:(NSArray *)expectedDocs { + // Note: Order matters. The following has to be done in the specific order: + + // 1- Pre-populate the cache with the entire collection. + [self readDocumentSetForRef:collection source:FIRFirestoreSourceServer]; + + // 2- This performs the query against the cache using full collection scan. + FIRQuerySnapshot *docsFromCacheFullCollectionScan = + [self readDocumentSetForRef:query source:FIRFirestoreSourceCache]; + + // 3- This goes to the server (backend/emulator). FIRQuerySnapshot *docsFromServer = [self readDocumentSetForRef:query source:FIRFirestoreSourceServer]; - FIRQuerySnapshot *docsFromCache = [self readDocumentSetForRef:query - source:FIRFirestoreSourceCache]; + // 4- This performs the query against the cache using remote keys. + FIRQuerySnapshot *docsFromCacheUsingRemoteKeys = + [self readDocumentSetForRef:query source:FIRFirestoreSourceCache]; + + XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(docsFromServer), + FIRQuerySnapshotGetIDs(docsFromCacheFullCollectionScan)); XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(docsFromServer), - FIRQuerySnapshotGetIDs(docsFromCache)); - XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(docsFromCache), expectedDocs); + FIRQuerySnapshotGetIDs(docsFromCacheUsingRemoteKeys)); + XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(docsFromServer), expectedDocs); } - (const std::shared_ptr &)queueForFirestore:(FIRFirestore *)firestore { diff --git a/Firestore/Swift/Tests/Integration/QueryIntegrationTests.swift b/Firestore/Swift/Tests/Integration/QueryIntegrationTests.swift index 3829b320ac2..bc71699774c 100644 --- a/Firestore/Swift/Tests/Integration/QueryIntegrationTests.swift +++ b/Firestore/Swift/Tests/Integration/QueryIntegrationTests.swift @@ -18,28 +18,6 @@ import FirebaseFirestore import Foundation class QueryIntegrationTests: FSTIntegrationTestCase { - /** - * Checks that running the query while online (against the backend/emulator) results in the same - * documents as running the query while offline. If expectedDocs is provided, it also checks - * that both online and offline query result is equal to the expected documents. - * - * @param query The query to check. - * @param expectedDocs Ordered list of document keys that are expected to match the query. - */ - private func checkOnlineAndOfflineQuery(_ query: Query, matchesResult expectedDocs: [String]?) { - let docsFromServer = readDocumentSet(forRef: query, - source: FirestoreSource.server) - - let docsFromCache = readDocumentSet(forRef: query, - source: FirestoreSource.cache) - - XCTAssertEqual(FIRQuerySnapshotGetIDs(docsFromServer), - FIRQuerySnapshotGetIDs(docsFromCache)) - if expectedDocs != nil { - XCTAssertEqual(FIRQuerySnapshotGetIDs(docsFromCache), expectedDocs) - } - } - func testOrQueries() throws { let collRef = collectionRef( withDocuments: ["doc1": ["a": 1, "b": 0], @@ -54,8 +32,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase { [Filter.whereField("a", isEqualTo: 1), Filter.whereField("b", isEqualTo: 1)] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter1), - matchesResult: ["doc1", "doc2", "doc4", "doc5"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1), + matchesResult: ["doc1", "doc2", "doc4", "doc5"]) // (a==1 && b==0) || (a==3 && b==2) let filter2 = Filter.orFilter( @@ -68,8 +46,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase { Filter.whereField("b", isEqualTo: 2)] )] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter2), - matchesResult: ["doc1", "doc3"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2), + matchesResult: ["doc1", "doc3"]) // a==1 && (b==0 || b==3). let filter3 = Filter.andFilter( @@ -79,8 +57,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase { Filter.whereField("b", isEqualTo: 3)] )] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter3), - matchesResult: ["doc1", "doc4"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter3), + matchesResult: ["doc1", "doc4"]) // (a==2 || b==2) && (a==3 || b==3) let filter4 = Filter.andFilter( @@ -93,16 +71,16 @@ class QueryIntegrationTests: FSTIntegrationTestCase { Filter.whereField("b", isEqualTo: 3)] )] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter4), - matchesResult: ["doc3"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter4), + matchesResult: ["doc3"]) // Test with limits without orderBy (the __name__ ordering is the tie breaker). let filter5 = Filter.orFilter( [Filter.whereField("a", isEqualTo: 2), Filter.whereField("b", isEqualTo: 1)] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter5).limit(to: 1), - matchesResult: ["doc2"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter5).limit(to: 1), + matchesResult: ["doc2"]) } func testOrQueriesWithCompositeIndexes() throws { @@ -124,16 +102,16 @@ class QueryIntegrationTests: FSTIntegrationTestCase { [Filter.whereField("a", isGreaterThan: 2), Filter.whereField("b", isEqualTo: 1)] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter1), - matchesResult: ["doc5", "doc2", "doc3"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1), + matchesResult: ["doc5", "doc2", "doc3"]) // Test with limits (implicit order by ASC): (a==1) || (b > 0) LIMIT 2 let filter2 = Filter.orFilter( [Filter.whereField("a", isEqualTo: 1), Filter.whereField("b", isGreaterThan: 0)] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter2).limit(to: 2), - matchesResult: ["doc1", "doc2"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2).limit(to: 2), + matchesResult: ["doc1", "doc2"]) // Test with limits (explicit order by): (a==1) || (b > 0) LIMIT_TO_LAST 2 // Note: The public query API does not allow implicit ordering when limitToLast is used. @@ -141,7 +119,7 @@ class QueryIntegrationTests: FSTIntegrationTestCase { [Filter.whereField("a", isEqualTo: 1), Filter.whereField("b", isGreaterThan: 0)] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter3) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter3) .limit(toLast: 2) .order(by: "b"), matchesResult: ["doc3", "doc4"]) @@ -151,7 +129,7 @@ class QueryIntegrationTests: FSTIntegrationTestCase { [Filter.whereField("a", isEqualTo: 2), Filter.whereField("b", isEqualTo: 1)] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter4).limit(to: 1) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter4).limit(to: 1) .order(by: "a"), matchesResult: ["doc5"]) @@ -160,7 +138,7 @@ class QueryIntegrationTests: FSTIntegrationTestCase { [Filter.whereField("a", isEqualTo: 2), Filter.whereField("b", isEqualTo: 1)] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter5).limit(toLast: 1) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter5).limit(toLast: 1) .order(by: "a"), matchesResult: ["doc2"]) } @@ -180,8 +158,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase { [Filter.whereField("a", isEqualTo: 2), Filter.whereField("b", in: [2, 3])] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter), - matchesResult: ["doc3", "doc4", "doc6"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter), + matchesResult: ["doc3", "doc4", "doc6"]) } func testOrQueriesWithArrayMembership() throws { @@ -199,16 +177,16 @@ class QueryIntegrationTests: FSTIntegrationTestCase { [Filter.whereField("a", isEqualTo: 2), Filter.whereField("b", arrayContains: 7)] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter1), - matchesResult: ["doc3", "doc4", "doc6"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1), + matchesResult: ["doc3", "doc4", "doc6"]) // a==2 || b array-contains-any [0, 3] let filter2 = Filter.orFilter( [Filter.whereField("a", isEqualTo: 2), Filter.whereField("b", arrayContainsAny: [0, 3])] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter2), - matchesResult: ["doc1", "doc4", "doc6"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2), + matchesResult: ["doc1", "doc4", "doc6"]) } func testMultipleInOps() throws { @@ -226,8 +204,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase { [Filter.whereField("a", in: [2, 3]), Filter.whereField("b", in: [0, 2])] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter1).order(by: "a"), - matchesResult: ["doc1", "doc6", "doc3"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1).order(by: "a"), + matchesResult: ["doc1", "doc6", "doc3"]) // Two IN operations on same fields with disjunction. // a IN [0,3] || a IN [0,2] should union them (similar to: a IN [0,2,3]). @@ -235,8 +213,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase { [Filter.whereField("a", in: [0, 3]), Filter.whereField("a", in: [0, 2])] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter2), - matchesResult: ["doc3", "doc6"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2), + matchesResult: ["doc3", "doc6"]) } func testUsingInWithArrayContainsAny() throws { @@ -253,8 +231,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase { [Filter.whereField("a", in: [2, 3]), Filter.whereField("b", arrayContainsAny: [0, 7])] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter1), - matchesResult: ["doc1", "doc3", "doc4", "doc6"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1), + matchesResult: ["doc1", "doc3", "doc4", "doc6"]) let filter2 = Filter.orFilter( [Filter.andFilter( @@ -263,8 +241,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase { ), Filter.whereField("b", arrayContainsAny: [0, 7])] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter2), - matchesResult: ["doc1", "doc3", "doc4"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2), + matchesResult: ["doc1", "doc3", "doc4"]) } func testUseInWithArrayContains() throws { @@ -281,15 +259,15 @@ class QueryIntegrationTests: FSTIntegrationTestCase { [Filter.whereField("a", in: [2, 3]), Filter.whereField("b", arrayContainsAny: [3])] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter1), - matchesResult: ["doc3", "doc4", "doc6"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1), + matchesResult: ["doc3", "doc4", "doc6"]) let filter2 = Filter.andFilter( [Filter.whereField("a", in: [2, 3]), Filter.whereField("b", arrayContains: 7)] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter2), - matchesResult: ["doc3"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2), + matchesResult: ["doc3"]) let filter3 = Filter.orFilter( [Filter.whereField("a", in: [2, 3]), @@ -298,8 +276,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase { Filter.whereField("a", isEqualTo: 1)] )] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter3), - matchesResult: ["doc3", "doc4", "doc6"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter3), + matchesResult: ["doc3", "doc4", "doc6"]) let filter4 = Filter.andFilter( [Filter.whereField("a", in: [2, 3]), @@ -308,8 +286,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase { Filter.whereField("a", isEqualTo: 1)] )] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(filter4), - matchesResult: ["doc3"]) + checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter4), + matchesResult: ["doc3"]) } func testOrderByEquality() throws { @@ -326,11 +304,15 @@ class QueryIntegrationTests: FSTIntegrationTestCase { "doc6": ["a": 2, "c": 20]] ) - checkOnlineAndOfflineQuery(collRef.whereFilter(Filter.whereField("a", isEqualTo: 1)), - matchesResult: ["doc1", "doc4", "doc5"]) + checkOnlineAndOfflineCollection( + collRef, + query: collRef.whereFilter(Filter.whereField("a", isEqualTo: 1)), + matchesResult: ["doc1", "doc4", "doc5"] + ) - checkOnlineAndOfflineQuery( - collRef.whereFilter(Filter.whereField("a", in: [2, 3])).order(by: "a"), + checkOnlineAndOfflineCollection( + collRef, + query: collRef.whereFilter(Filter.whereField("a", in: [2, 3])).order(by: "a"), matchesResult: ["doc6", "doc3"] ) } diff --git a/Firestore/Swift/Tests/Integration/VectorIntegrationTests.swift b/Firestore/Swift/Tests/Integration/VectorIntegrationTests.swift index 069a71a6541..f7203ab495a 100644 --- a/Firestore/Swift/Tests/Integration/VectorIntegrationTests.swift +++ b/Firestore/Swift/Tests/Integration/VectorIntegrationTests.swift @@ -146,7 +146,11 @@ class VectorIntegrationTests: FSTIntegrationTestCase { docIds.append(docRef.documentID) } - checkOnlineAndOfflineQuery(collection.order(by: "embedding"), matchesResult: docIds) + checkOnlineAndOfflineCollection( + collection, + query: collection.order(by: "embedding"), + matchesResult: docIds + ) } func testSdkFiltersVectorFieldSameWayOnlineAndOffline() async throws { @@ -176,16 +180,14 @@ class VectorIntegrationTests: FSTIntegrationTestCase { docIds.append(docRef.documentID) } - checkOnlineAndOfflineQuery( + checkOnlineAndOfflineCollection(collection, query: collection.order(by: "embedding") .whereField("embedding", isLessThan: FieldValue.vector([1, 2, 100, 4, 4.0])), - matchesResult: Array(docIds[2 ... 10]) - ) - checkOnlineAndOfflineQuery( + matchesResult: Array(docIds[2 ... 10])) + checkOnlineAndOfflineCollection(collection, query: collection.order(by: "embedding") .whereField("embedding", isGreaterThanOrEqualTo: FieldValue.vector([1, 2, 100, 4, 4.0])), - matchesResult: Array(docIds[11 ... 12]) - ) + matchesResult: Array(docIds[11 ... 12])) } func testQueryVectorValueWrittenByCodable() async throws {