From 25f8691970a9f765a87ab3125776598c92e02744 Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Tue, 10 Jul 2018 16:58:56 -0700 Subject: Expose array transforms and array contains queries. (#1514) Also remove test code that was combining multiple array contains queries since those were disallowed in https://github.com/firebase/firebase-ios-sdk/commit/0ec836f9ca71b27fa54a11ae9e07e60b8c5cc002 --- Firestore/CHANGELOG.md | 4 +++ Firestore/Example/SwiftBuildTest/main.swift | 10 +++--- .../Example/Tests/Integration/API/FIRQueryTests.mm | 21 +----------- .../Tests/Integration/API/FIRValidationTests.mm | 1 - Firestore/Source/API/FIRFieldValue+Internal.h | 28 ---------------- Firestore/Source/API/FIRQuery+Internal.h | 37 ---------------------- Firestore/Source/Public/FIRFieldValue.h | 23 ++++++++++++++ Firestore/Source/Public/FIRQuery.h | 32 +++++++++++++++++++ 8 files changed, 64 insertions(+), 92 deletions(-) diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index c7b745d..8bb3e60 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -1,4 +1,8 @@ # Unreleased +- [feature] Added `FieldValue.arrayUnion()` and `FieldValue.arrayRemove()` to + atomically add and remove elements from an array field in a document. +- [feature] Added `whereField(arrayContains:)` query filter to find + documents where an array field contains a specific element. # v0.12.5 - [changed] Internal improvements. diff --git a/Firestore/Example/SwiftBuildTest/main.swift b/Firestore/Example/SwiftBuildTest/main.swift index c5034da..6773511 100644 --- a/Firestore/Example/SwiftBuildTest/main.swift +++ b/Firestore/Example/SwiftBuildTest/main.swift @@ -83,9 +83,8 @@ func makeRefs(database db: Firestore) -> (CollectionReference, DocumentReference func makeQuery(collection collectionRef: CollectionReference) -> Query { let query = collectionRef.whereField(FieldPath(["name"]), isEqualTo: "Fred") .whereField("age", isGreaterThanOrEqualTo: 24) - // TODO(array-features): Uncomment when API is publicly exposed. - // .whereField("tags", arrayContains:"active") - // .whereField(FieldPath(["tags"]), arrayContains:"active") + .whereField("tags", arrayContains: "active") + .whereField(FieldPath(["tags"]), arrayContains: "active") .whereField(FieldPath.documentID(), isEqualTo: "fred") .order(by: FieldPath(["age"])) .order(by: "name", descending: true) @@ -106,9 +105,8 @@ func writeDocument(at docRef: DocumentReference) { "bar.baz": 42, FieldPath(["foobar"]): 42, "server_timestamp": FieldValue.serverTimestamp(), - // TODO(array-features): Uncomment once we add these to the public API - // "array_union": FieldValue.arrayUnion(["a", "b"]), - // "array_remove": FieldValue.arrayRemove(["a", "b"]), + "array_union": FieldValue.arrayUnion(["a", "b"]), + "array_remove": FieldValue.arrayRemove(["a", "b"]), "field_delete": FieldValue.delete(), ] as [AnyHashable: Any] diff --git a/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm b/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm index bdd3df1..e634d7f 100644 --- a/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm @@ -20,7 +20,6 @@ #import "Firestore/Example/Tests/Util/FSTEventAccumulator.h" #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" -#import "Firestore/Source/API/FIRQuery+Internal.h" @interface FIRQueryTests : FSTIntegrationTestCase @end @@ -293,8 +292,7 @@ ])); } -// TODO(array-features): Enable once backend support lands. -- (void)xtestArrayContainsQueries { +- (void)testArrayContainsQueries { NSDictionary *testDocs = @{ @"a" : @{@"array" : @[ @42 ]}, @"b" : @{@"array" : @[ @"a", @42, @"c" ]}, @@ -314,23 +312,6 @@ @"array2" : @[ @"bingo" ] } ])); - // Search for "array" to contain both @42 and "a". - snapshot = [self readDocumentSetForRef:[[collection queryWhereField:@"array" arrayContains:@42] - queryWhereField:@"array" - arrayContains:@"a"]]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[ - @{ @"array" : @[ @"a", @42, @"c" ] }, - ])); - - // Search two different array fields ("array" contains 42 and "array2" contains "bingo"). - snapshot = [self readDocumentSetForRef:[[collection queryWhereField:@"array" arrayContains:@42] - queryWhereField:@"array2" - arrayContains:@"bingo"]]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[ - @{ @"array" : @[ @42 ], - @"array2" : @[ @"bingo" ] } - ])); - // NOTE: The backend doesn't currently support null, NaN, objects, or arrays, so there isn't much // of anything else interesting to test. } diff --git a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm index 599f1b2..fd37e5b 100644 --- a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm @@ -19,7 +19,6 @@ #import #import "Firestore/Source/API/FIRFieldValue+Internal.h" -#import "Firestore/Source/API/FIRQuery+Internal.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" diff --git a/Firestore/Source/API/FIRFieldValue+Internal.h b/Firestore/Source/API/FIRFieldValue+Internal.h index 883a307..1618cd4 100644 --- a/Firestore/Source/API/FIRFieldValue+Internal.h +++ b/Firestore/Source/API/FIRFieldValue+Internal.h @@ -54,32 +54,4 @@ NS_ASSUME_NONNULL_BEGIN @property(strong, nonatomic, readonly) NSArray *elements; @end -// TODO(array-features): Move to FIRFieldValue.h once backend support lands. -@interface FIRFieldValue () - -/** - * Returns a special value that can be used with setData() or updateData() that tells the server to - * union the given elements with any array value that already exists on the server. Each - * specified element that doesn't already exist in the array will be added to the end. If the - * field being modified is not already an array it will be overwritten with an array containing - * exactly the specified elements. - * - * @param elements The elements to union into the array. - * @return The FieldValue sentinel for use in a call to setData() or updateData(). - */ -+ (instancetype)fieldValueForArrayUnion:(NSArray *)elements NS_SWIFT_NAME(arrayUnion(_:)); - -/** - * Returns a special value that can be used with setData() or updateData() that tells the server to - * remove the given elements from any array value that already exists on the server. All - * instances of each element specified will be removed from the array. If the field being - * modified is not already an array it will be overwritten with an empty array. - * - * @param elements The elements to remove from the array. - * @return The FieldValue sentinel for use in a call to setData() or updateData(). - */ -+ (instancetype)fieldValueForArrayRemove:(NSArray *)elements NS_SWIFT_NAME(arrayRemove(_:)); - -@end - NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/API/FIRQuery+Internal.h b/Firestore/Source/API/FIRQuery+Internal.h index e207837..fa6c415 100644 --- a/Firestore/Source/API/FIRQuery+Internal.h +++ b/Firestore/Source/API/FIRQuery+Internal.h @@ -28,41 +28,4 @@ NS_ASSUME_NONNULL_BEGIN @end -// TODO(array-features): Move to FIRQuery.h once backend support is available. -@interface FIRQuery () - -/** - * Creates and returns a new `FIRQuery` with the additional filter that documents must contain - * the specified field, it must be an array, and the array must contain the provided value. - * - * A query can have only one arrayContains filter. - * - * @param field The name of the field containing an array to search - * @param value The value that must be contained in the array - * - * @return The created `FIRQuery`. - */ -// clang-format off -- (FIRQuery *)queryWhereField:(NSString *)field - arrayContains:(id)value NS_SWIFT_NAME(whereField(_:arrayContains:)); -// clang-format on - -/** - * Creates and returns a new `FIRQuery` with the additional filter that documents must contain - * the specified field, it must be an array, and the array must contain the provided value. - * - * A query can have only one arrayContains filter. - * - * @param path The path of the field containing an array to search - * @param value The value that must be contained in the array - * - * @return The created `FIRQuery`. - */ -// clang-format off -- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path - arrayContains:(id)value NS_SWIFT_NAME(whereField(_:arrayContains:)); -// clang-format on - -@end - NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Public/FIRFieldValue.h b/Firestore/Source/Public/FIRFieldValue.h index 11a0da0..d896587 100644 --- a/Firestore/Source/Public/FIRFieldValue.h +++ b/Firestore/Source/Public/FIRFieldValue.h @@ -38,6 +38,29 @@ NS_SWIFT_NAME(FieldValue) */ + (instancetype)fieldValueForServerTimestamp NS_SWIFT_NAME(serverTimestamp()); +/** + * Returns a special value that can be used with setData() or updateData() that tells the server to + * union the given elements with any array value that already exists on the server. Each + * specified element that doesn't already exist in the array will be added to the end. If the + * field being modified is not already an array it will be overwritten with an array containing + * exactly the specified elements. + * + * @param elements The elements to union into the array. + * @return The FieldValue sentinel for use in a call to setData() or updateData(). + */ ++ (instancetype)fieldValueForArrayUnion:(NSArray *)elements NS_SWIFT_NAME(arrayUnion(_:)); + +/** + * Returns a special value that can be used with setData() or updateData() that tells the server to + * remove the given elements from any array value that already exists on the server. All + * instances of each element specified will be removed from the array. If the field being + * modified is not already an array it will be overwritten with an empty array. + * + * @param elements The elements to remove from the array. + * @return The FieldValue sentinel for use in a call to setData() or updateData(). + */ ++ (instancetype)fieldValueForArrayRemove:(NSArray *)elements NS_SWIFT_NAME(arrayRemove(_:)); + @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Public/FIRQuery.h b/Firestore/Source/Public/FIRQuery.h index 799abcc..2b02a3c 100644 --- a/Firestore/Source/Public/FIRQuery.h +++ b/Firestore/Source/Public/FIRQuery.h @@ -238,6 +238,38 @@ addSnapshotListenerWithIncludeMetadataChanges:(BOOL)includeMetadataChanges isGreaterThanOrEqualTo:(id)value NS_SWIFT_NAME(whereField(_:isGreaterThanOrEqualTo:)); // clang-format on +/** + * Creates and returns a new `FIRQuery` with the additional filter that documents must contain + * the specified field, it must be an array, and the array must contain the provided value. + * + * A query can have only one arrayContains filter. + * + * @param field The name of the field containing an array to search + * @param value The value that must be contained in the array + * + * @return The created `FIRQuery`. + */ +// clang-format off +- (FIRQuery *)queryWhereField:(NSString *)field + arrayContains:(id)value NS_SWIFT_NAME(whereField(_:arrayContains:)); +// clang-format on + +/** + * Creates and returns a new `FIRQuery` with the additional filter that documents must contain + * the specified field, it must be an array, and the array must contain the provided value. + * + * A query can have only one arrayContains filter. + * + * @param path The path of the field containing an array to search + * @param value The value that must be contained in the array + * + * @return The created `FIRQuery`. + */ +// clang-format off +- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path + arrayContains:(id)value NS_SWIFT_NAME(whereField(_:arrayContains:)); +// clang-format on + /** * Creates and returns a new `FIRQuery` with the additional filter that documents must * satisfy the specified predicate. -- cgit v1.2.3