diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index b312da6945c..2eae4894947 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -1763,7 +1763,7 @@ 1CA9800A53669EFBFFB824E3 /* memory_remote_document_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = memory_remote_document_cache_test.cc; sourceTree = ""; }; 1E0C7C0DCD2790019E66D8CC /* bloom_filter.pb.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = bloom_filter.pb.cc; sourceTree = ""; }; 1F50E872B3F117A674DA8E94 /* index_backfiller_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = index_backfiller_test.cc; sourceTree = ""; }; - 1F78CD3208A1D5885B4C134E /* field_behavior.pb.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = field_behavior.pb.cc; sourceTree = ""; }; + 1F78CD3208A1D5885B4C134E /* field_behavior.pb.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = field_behavior.pb.cc; sourceTree = ""; }; 214877F52A705012D6720CA0 /* object_value_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = object_value_test.cc; sourceTree = ""; }; 2220F583583EFC28DE792ABE /* Pods_Firestore_IntegrationTests_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2286F308EFB0534B1BDE05B9 /* memory_target_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = memory_target_cache_test.cc; sourceTree = ""; }; @@ -1803,12 +1803,12 @@ 403DBF6EFB541DFD01582AA3 /* path_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = path_test.cc; sourceTree = ""; }; 40F9D09063A07F710811A84F /* value_util_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = value_util_test.cc; sourceTree = ""; }; 4132F30044D5DF1FB15B2A9D /* fake_credentials_provider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = fake_credentials_provider.h; sourceTree = ""; }; - 428662F00938E9E21F7080D7 /* explain_stats.pb.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = explain_stats.pb.cc; sourceTree = ""; }; + 428662F00938E9E21F7080D7 /* explain_stats.pb.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = explain_stats.pb.cc; sourceTree = ""; }; 432C71959255C5DBDF522F52 /* byte_stream_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = byte_stream_test.cc; sourceTree = ""; }; 4334F87873015E3763954578 /* status_testing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = status_testing.h; sourceTree = ""; }; 4375BDCDBCA9938C7F086730 /* Validation_BloomFilterTest_MD5_5000_1_bloom_filter_proto.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_5000_1_bloom_filter_proto.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_5000_1_bloom_filter_proto.json; sourceTree = ""; }; 444B7AB3F5A2929070CB1363 /* hard_assert_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = hard_assert_test.cc; sourceTree = ""; }; - 4564AD9C55EC39C080EB9476 /* globals_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = globals_cache_test.cc; sourceTree = ""; }; + 4564AD9C55EC39C080EB9476 /* globals_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = globals_cache_test.cc; sourceTree = ""; }; 478DC75A0DCA6249A616DD30 /* Validation_BloomFilterTest_MD5_500_0001_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_500_0001_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_500_0001_membership_test_result.json; sourceTree = ""; }; 48D0915834C3D234E5A875A9 /* grpc_stream_tester.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = grpc_stream_tester.h; sourceTree = ""; }; 4B3E4A77493524333133C5DC /* Validation_BloomFilterTest_MD5_50000_1_bloom_filter_proto.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_50000_1_bloom_filter_proto.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_50000_1_bloom_filter_proto.json; sourceTree = ""; }; @@ -1927,7 +1927,7 @@ 5B5414D28802BC76FDADABD6 /* stream_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = stream_test.cc; sourceTree = ""; }; 5B96CC29E9946508F022859C /* Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json; sourceTree = ""; }; 5C68EE4CB94C0DD6E333F546 /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_1_01_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_1_01_membership_test_result.json; sourceTree = ""; }; - 5C6DEA63FBDE19D841291723 /* memory_globals_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = memory_globals_cache_test.cc; sourceTree = ""; }; + 5C6DEA63FBDE19D841291723 /* memory_globals_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = memory_globals_cache_test.cc; sourceTree = ""; }; 5C7942B6244F4C416B11B86C /* leveldb_mutation_queue_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_mutation_queue_test.cc; sourceTree = ""; }; 5CAE131920FFFED600BE9A4A /* Firestore_Benchmarks_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_Benchmarks_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 5CAE131D20FFFED600BE9A4A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -1968,6 +1968,7 @@ 61F72C5520BC48FD001A68CB /* serializer_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serializer_test.cc; sourceTree = ""; }; 620C1427763BA5D3CCFB5A1F /* BridgingHeader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = BridgingHeader.h; sourceTree = ""; }; 621D620928F9CE7400D2FA26 /* QueryIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryIntegrationTests.swift; sourceTree = ""; }; + 623E20B12E26FA8000614431 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 62E103B28B48A81D682A0DE9 /* Pods_Firestore_Example_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 62E54B832A9E910A003347C8 /* IndexingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndexingTests.swift; sourceTree = ""; }; 63136A2371C0C013EC7A540C /* target_index_matcher_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = target_index_matcher_test.cc; sourceTree = ""; }; @@ -1977,7 +1978,7 @@ 69E6C311558EC77729A16CF1 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig"; sourceTree = ""; }; 6A7A30A2DB3367E08939E789 /* bloom_filter.pb.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = bloom_filter.pb.h; sourceTree = ""; }; 6AE927CDFC7A72BF825BE4CB /* Pods-Firestore_Tests_tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_tvOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_tvOS/Pods-Firestore_Tests_tvOS.release.xcconfig"; sourceTree = ""; }; - 6E42FA109D363EA7F3387AAE /* thread_safe_memoizer_testing.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = thread_safe_memoizer_testing.cc; sourceTree = ""; }; + 6E42FA109D363EA7F3387AAE /* thread_safe_memoizer_testing.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = thread_safe_memoizer_testing.cc; sourceTree = ""; }; 6E8302DE210222ED003E1EA3 /* FSTFuzzTestFieldPath.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTFuzzTestFieldPath.h; sourceTree = ""; }; 6E8302DF21022309003E1EA3 /* FSTFuzzTestFieldPath.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTFuzzTestFieldPath.mm; sourceTree = ""; }; 6EA39FDD20FE820E008D461F /* FSTFuzzTestSerializer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTFuzzTestSerializer.mm; sourceTree = ""; }; @@ -2123,7 +2124,7 @@ D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = delayed_constructor_test.cc; sourceTree = ""; }; D22D4C211AC32E4F8B4883DA /* Validation_BloomFilterTest_MD5_500_0001_bloom_filter_proto.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_500_0001_bloom_filter_proto.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_500_0001_bloom_filter_proto.json; sourceTree = ""; }; D3CC3DC5338DCAF43A211155 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; - D49E7AEE500651D25C5360C3 /* pipeline.pb.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = pipeline.pb.cc; sourceTree = ""; }; + D49E7AEE500651D25C5360C3 /* pipeline.pb.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = pipeline.pb.cc; sourceTree = ""; }; D5B2593BCB52957D62F1C9D3 /* perf_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = perf_spec_test.json; sourceTree = ""; }; D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRNumericTransformTests.mm; sourceTree = ""; }; D7DF4A6F740086A2D8C0E28E /* Pods_Firestore_Tests_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -2159,7 +2160,7 @@ E42355285B9EF55ABD785792 /* Pods_Firestore_Example_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E592181BFD7C53C305123739 /* Pods-Firestore_Tests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS.debug.xcconfig"; sourceTree = ""; }; E76F0CDF28E5FA62D21DE648 /* leveldb_target_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_target_cache_test.cc; sourceTree = ""; }; - EA10515F99A42D71DA2D2841 /* thread_safe_memoizer_testing_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = thread_safe_memoizer_testing_test.cc; sourceTree = ""; }; + EA10515F99A42D71DA2D2841 /* thread_safe_memoizer_testing_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = thread_safe_memoizer_testing_test.cc; sourceTree = ""; }; ECEBABC7E7B693BE808A1052 /* Pods_Firestore_IntegrationTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EF3A65472C66B9560041EE69 /* FIRVectorValueTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRVectorValueTests.mm; sourceTree = ""; }; EF6C285029E462A200A7D4F1 /* FIRAggregateTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRAggregateTests.mm; sourceTree = ""; }; @@ -2177,7 +2178,7 @@ F848C41C03A25C42AD5A4BC2 /* target_cache_test.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = target_cache_test.h; sourceTree = ""; }; F869D85E900E5AF6CD02E2FC /* firebase_auth_credentials_provider_test.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; name = firebase_auth_credentials_provider_test.mm; path = credentials/firebase_auth_credentials_provider_test.mm; sourceTree = ""; }; FA2E9952BA2B299C1156C43C /* Pods-Firestore_Benchmarks_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Benchmarks_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Benchmarks_iOS/Pods-Firestore_Benchmarks_iOS.debug.xcconfig"; sourceTree = ""; }; - FC44D934D4A52C790659C8D6 /* leveldb_globals_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = leveldb_globals_cache_test.cc; sourceTree = ""; }; + FC44D934D4A52C790659C8D6 /* leveldb_globals_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_globals_cache_test.cc; sourceTree = ""; }; FC738525340E594EBFAB121E /* Pods-Firestore_Example_tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_tvOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_tvOS/Pods-Firestore_Example_tvOS.release.xcconfig"; sourceTree = ""; }; FF73B39D04D1760190E6B84A /* FIRQueryUnitTests.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRQueryUnitTests.mm; sourceTree = ""; }; FFCA39825D9678A03D1845D0 /* document_overlay_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = document_overlay_cache_test.cc; sourceTree = ""; }; diff --git a/Firestore/Swift/Source/ExprImpl.swift b/Firestore/Swift/Source/ExprImpl.swift index 51a82966b86..af968ead9e2 100644 --- a/Firestore/Swift/Source/ExprImpl.swift +++ b/Firestore/Swift/Source/ExprImpl.swift @@ -12,362 +12,361 @@ // See the License for the specific language governing permissions and // limitations under the License. -extension Expr { +extension Expression { func toBridge() -> ExprBridge { return (self as! BridgeWrapper).bridge } } -public extension Expr { - func `as`(_ name: String) -> ExprWithAlias { - return ExprWithAlias(self, name) +public extension Expression { + func `as`(_ name: String) -> AliasedExpression { + return AliasedExpression(self, name) } // MARK: Arithmetic Operators - func add(_ value: Expr) -> FunctionExpr { - return FunctionExpr("add", [self, value]) + func add(_ value: Expression) -> FunctionExpression { + return FunctionExpression("add", [self, value]) } - func add(_ value: Sendable) -> FunctionExpr { - return FunctionExpr("add", [self, Helper.sendableToExpr(value)]) + func add(_ value: Sendable) -> FunctionExpression { + return FunctionExpression("add", [self, Helper.sendableToExpr(value)]) } - func subtract(_ other: Expr) -> FunctionExpr { - return FunctionExpr("subtract", [self, other]) + func subtract(_ other: Expression) -> FunctionExpression { + return FunctionExpression("subtract", [self, other]) } - func subtract(_ other: Sendable) -> FunctionExpr { - return FunctionExpr("subtract", [self, Helper.sendableToExpr(other)]) + func subtract(_ other: Sendable) -> FunctionExpression { + return FunctionExpression("subtract", [self, Helper.sendableToExpr(other)]) } - func multiply(_ value: Expr) -> FunctionExpr { - return FunctionExpr("multiply", [self, value]) + func multiply(_ value: Expression) -> FunctionExpression { + return FunctionExpression("multiply", [self, value]) } - func multiply(_ value: Sendable) -> FunctionExpr { - return FunctionExpr("multiply", [self, Helper.sendableToExpr(value)]) + func multiply(_ value: Sendable) -> FunctionExpression { + return FunctionExpression("multiply", [self, Helper.sendableToExpr(value)]) } - func divide(_ other: Expr) -> FunctionExpr { - return FunctionExpr("divide", [self, other]) + func divide(_ other: Expression) -> FunctionExpression { + return FunctionExpression("divide", [self, other]) } - func divide(_ other: Sendable) -> FunctionExpr { - return FunctionExpr("divide", [self, Helper.sendableToExpr(other)]) + func divide(_ other: Sendable) -> FunctionExpression { + return FunctionExpression("divide", [self, Helper.sendableToExpr(other)]) } - func mod(_ other: Expr) -> FunctionExpr { - return FunctionExpr("mod", [self, other]) + func mod(_ other: Expression) -> FunctionExpression { + return FunctionExpression("mod", [self, other]) } - func mod(_ other: Sendable) -> FunctionExpr { - return FunctionExpr("mod", [self, Helper.sendableToExpr(other)]) + func mod(_ other: Sendable) -> FunctionExpression { + return FunctionExpression("mod", [self, Helper.sendableToExpr(other)]) } // MARK: Array Operations - func arrayConcat(_ secondArray: Expr, _ otherArrays: Expr...) -> FunctionExpr { - return FunctionExpr("array_concat", [self, secondArray] + otherArrays) + func arrayConcat(_ arrays: [Expression]) -> FunctionExpression { + return FunctionExpression("array_concat", [self] + arrays) } - func arrayConcat(_ secondArray: [Sendable], _ otherArrays: [Sendable]...) -> FunctionExpr { - let exprs = [self] + [Helper.sendableToExpr(secondArray)] + otherArrays - .map { Helper.sendableToExpr($0) } - return FunctionExpr("array_concat", exprs) + func arrayConcat(_ arrays: [[Sendable]]) -> FunctionExpression { + let exprs = [self] + arrays.map { Helper.sendableToExpr($0) } + return FunctionExpression("array_concat", exprs) } - func arrayContains(_ element: Expr) -> BooleanExpr { - return BooleanExpr("array_contains", [self, element]) + func arrayContains(_ element: Expression) -> BooleanExpression { + return BooleanExpression("array_contains", [self, element]) } - func arrayContains(_ element: Sendable) -> BooleanExpr { - return BooleanExpr("array_contains", [self, Helper.sendableToExpr(element)]) + func arrayContains(_ element: Sendable) -> BooleanExpression { + return BooleanExpression("array_contains", [self, Helper.sendableToExpr(element)]) } - func arrayContainsAll(_ values: [Expr]) -> BooleanExpr { - return BooleanExpr("array_contains_all", [self, Helper.array(values)]) + func arrayContainsAll(_ values: [Expression]) -> BooleanExpression { + return BooleanExpression("array_contains_all", [self, Helper.array(values)]) } - func arrayContainsAll(_ values: [Sendable]) -> BooleanExpr { - return BooleanExpr("array_contains_all", [self, Helper.array(values)]) + func arrayContainsAll(_ values: [Sendable]) -> BooleanExpression { + return BooleanExpression("array_contains_all", [self, Helper.array(values)]) } - func arrayContainsAny(_ values: [Expr]) -> BooleanExpr { - return BooleanExpr("array_contains_any", [self, Helper.array(values)]) + func arrayContainsAll(_ arrayExpression: Expression) -> BooleanExpression { + return BooleanExpression("array_contains_all", [self, arrayExpression]) } - func arrayContainsAny(_ values: [Sendable]) -> BooleanExpr { - return BooleanExpr("array_contains_any", [self, Helper.array(values)]) + func arrayContainsAny(_ values: [Expression]) -> BooleanExpression { + return BooleanExpression("array_contains_any", [self, Helper.array(values)]) } - func arrayLength() -> FunctionExpr { - return FunctionExpr("array_length", [self]) + func arrayContainsAny(_ values: [Sendable]) -> BooleanExpression { + return BooleanExpression("array_contains_any", [self, Helper.array(values)]) } - func arrayGet(_ offset: Int) -> FunctionExpr { - return FunctionExpr("array_get", [self, Helper.sendableToExpr(offset)]) + func arrayContainsAny(_ arrayExpression: Expression) -> BooleanExpression { + return BooleanExpression("array_contains_any", [self, arrayExpression]) } - func arrayGet(_ offsetExpr: Expr) -> FunctionExpr { - return FunctionExpr("array_get", [self, offsetExpr]) + func arrayLength() -> FunctionExpression { + return FunctionExpression("array_length", [self]) } - func gt(_ other: Expr) -> BooleanExpr { - return BooleanExpr("gt", [self, other]) + func arrayGet(_ offset: Int) -> FunctionExpression { + return FunctionExpression("array_get", [self, Helper.sendableToExpr(offset)]) } - func gt(_ other: Sendable) -> BooleanExpr { - let exprOther = Helper.sendableToExpr(other) - return BooleanExpr("gt", [self, exprOther]) + func arrayGet(_ offsetExpr: Expression) -> FunctionExpression { + return FunctionExpression("array_get", [self, offsetExpr]) } - // MARK: - Greater Than or Equal (gte) - - func gte(_ other: Expr) -> BooleanExpr { - return BooleanExpr("gte", [self, other]) + func greaterThan(_ other: Expression) -> BooleanExpression { + return BooleanExpression("gt", [self, other]) } - func gte(_ other: Sendable) -> BooleanExpr { + func greaterThan(_ other: Sendable) -> BooleanExpression { let exprOther = Helper.sendableToExpr(other) - return BooleanExpr("gte", [self, exprOther]) + return BooleanExpression("gt", [self, exprOther]) } - // MARK: - Less Than (lt) - - func lt(_ other: Expr) -> BooleanExpr { - return BooleanExpr("lt", [self, other]) + func greaterThanOrEqualTo(_ other: Expression) -> BooleanExpression { + return BooleanExpression("gte", [self, other]) } - func lt(_ other: Sendable) -> BooleanExpr { + func greaterThanOrEqualTo(_ other: Sendable) -> BooleanExpression { let exprOther = Helper.sendableToExpr(other) - return BooleanExpr("lt", [self, exprOther]) + return BooleanExpression("gte", [self, exprOther]) } - // MARK: - Less Than or Equal (lte) - - func lte(_ other: Expr) -> BooleanExpr { - return BooleanExpr("lte", [self, other]) + func lessThan(_ other: Expression) -> BooleanExpression { + return BooleanExpression("lt", [self, other]) } - func lte(_ other: Sendable) -> BooleanExpr { + func lessThan(_ other: Sendable) -> BooleanExpression { let exprOther = Helper.sendableToExpr(other) - return BooleanExpr("lte", [self, exprOther]) + return BooleanExpression("lt", [self, exprOther]) } - // MARK: - Equal (eq) + func lessThanOrEqualTo(_ other: Expression) -> BooleanExpression { + return BooleanExpression("lte", [self, other]) + } + + func lessThanOrEqualTo(_ other: Sendable) -> BooleanExpression { + let exprOther = Helper.sendableToExpr(other) + return BooleanExpression("lte", [self, exprOther]) + } - func eq(_ other: Expr) -> BooleanExpr { - return BooleanExpr("eq", [self, other]) + func equal(_ other: Expression) -> BooleanExpression { + return BooleanExpression("eq", [self, other]) } - func eq(_ other: Sendable) -> BooleanExpr { + func equal(_ other: Sendable) -> BooleanExpression { let exprOther = Helper.sendableToExpr(other) - return BooleanExpr("eq", [self, exprOther]) + return BooleanExpression("eq", [self, exprOther]) + } + + func notEqual(_ other: Expression) -> BooleanExpression { + return BooleanExpression("neq", [self, other]) } - func neq(_ other: Expr) -> BooleanExpr { - return BooleanExpr("neq", [self, other]) + func notEqual(_ other: Sendable) -> BooleanExpression { + return BooleanExpression("neq", [self, Helper.sendableToExpr(other)]) } - func neq(_ other: Sendable) -> BooleanExpr { - return BooleanExpr("neq", [self, Helper.sendableToExpr(other)]) + func equalAny(_ others: [Expression]) -> BooleanExpression { + return BooleanExpression("eq_any", [self, Helper.array(others)]) } - func eqAny(_ others: [Expr]) -> BooleanExpr { - return BooleanExpr("eq_any", [self, Helper.array(others)]) + func equalAny(_ others: [Sendable]) -> BooleanExpression { + return BooleanExpression("eq_any", [self, Helper.array(others)]) } - func eqAny(_ others: [Sendable]) -> BooleanExpr { - return BooleanExpr("eq_any", [self, Helper.array(others)]) + func equalAny(_ arrayExpression: Expression) -> BooleanExpression { + return BooleanExpression("eq_any", [self, arrayExpression]) } - func notEqAny(_ others: [Expr]) -> BooleanExpr { - return BooleanExpr("not_eq_any", [self, Helper.array(others)]) + func notEqualAny(_ others: [Expression]) -> BooleanExpression { + return BooleanExpression("not_eq_any", [self, Helper.array(others)]) } - func notEqAny(_ others: [Sendable]) -> BooleanExpr { - return BooleanExpr("not_eq_any", [self, Helper.array(others)]) + func notEqualAny(_ others: [Sendable]) -> BooleanExpression { + return BooleanExpression("not_eq_any", [self, Helper.array(others)]) + } + + func notEqualAny(_ arrayExpression: Expression) -> BooleanExpression { + return BooleanExpression("not_eq_any", [self, arrayExpression]) } // MARK: Checks // --- Added Type Check Operations --- - func isNan() -> BooleanExpr { - return BooleanExpr("is_nan", [self]) + func isNan() -> BooleanExpression { + return BooleanExpression("is_nan", [self]) } - func isNull() -> BooleanExpr { - return BooleanExpr("is_null", [self]) + func isNil() -> BooleanExpression { + return BooleanExpression("is_null", [self]) } - func exists() -> BooleanExpr { - return BooleanExpr("exists", [self]) + func exists() -> BooleanExpression { + return BooleanExpression("exists", [self]) } - func isError() -> BooleanExpr { - return BooleanExpr("is_error", [self]) + func isError() -> BooleanExpression { + return BooleanExpression("is_error", [self]) } - func isAbsent() -> BooleanExpr { - return BooleanExpr("is_absent", [self]) + func isAbsent() -> BooleanExpression { + return BooleanExpression("is_absent", [self]) } - func isNotNull() -> BooleanExpr { - return BooleanExpr("is_not_null", [self]) + func isNotNil() -> BooleanExpression { + return BooleanExpression("is_not_null", [self]) } - func isNotNan() -> BooleanExpr { - return BooleanExpr("is_not_nan", [self]) + func isNotNan() -> BooleanExpression { + return BooleanExpression("is_not_nan", [self]) } // --- Added String Operations --- - func charLength() -> FunctionExpr { - return FunctionExpr("char_length", [self]) - } - - func like(_ pattern: String) -> BooleanExpr { - return BooleanExpr("like", [self, Helper.sendableToExpr(pattern)]) + func charLength() -> FunctionExpression { + return FunctionExpression("char_length", [self]) } - func like(_ pattern: Expr) -> BooleanExpr { - return BooleanExpr("like", [self, pattern]) + func like(_ pattern: String) -> BooleanExpression { + return BooleanExpression("like", [self, Helper.sendableToExpr(pattern)]) } - func regexContains(_ pattern: String) -> BooleanExpr { - return BooleanExpr("regex_contains", [self, Helper.sendableToExpr(pattern)]) + func like(_ pattern: Expression) -> BooleanExpression { + return BooleanExpression("like", [self, pattern]) } - func regexContains(_ pattern: Expr) -> BooleanExpr { - return BooleanExpr("regex_contains", [self, pattern]) + func regexContains(_ pattern: String) -> BooleanExpression { + return BooleanExpression("regex_contains", [self, Helper.sendableToExpr(pattern)]) } - func regexMatch(_ pattern: String) -> BooleanExpr { - return BooleanExpr("regex_match", [self, Helper.sendableToExpr(pattern)]) + func regexContains(_ pattern: Expression) -> BooleanExpression { + return BooleanExpression("regex_contains", [self, pattern]) } - func regexMatch(_ pattern: Expr) -> BooleanExpr { - return BooleanExpr("regex_match", [self, pattern]) + func regexMatch(_ pattern: String) -> BooleanExpression { + return BooleanExpression("regex_match", [self, Helper.sendableToExpr(pattern)]) } - func strContains(_ substring: String) -> BooleanExpr { - return BooleanExpr("str_contains", [self, Helper.sendableToExpr(substring)]) + func regexMatch(_ pattern: Expression) -> BooleanExpression { + return BooleanExpression("regex_match", [self, pattern]) } - func strContains(_ expr: Expr) -> BooleanExpr { - return BooleanExpr("str_contains", [self, expr]) + func strContains(_ substring: String) -> BooleanExpression { + return BooleanExpression("str_contains", [self, Helper.sendableToExpr(substring)]) } - func startsWith(_ prefix: String) -> BooleanExpr { - return BooleanExpr("starts_with", [self, Helper.sendableToExpr(prefix)]) + func strContains(_ expr: Expression) -> BooleanExpression { + return BooleanExpression("str_contains", [self, expr]) } - func startsWith(_ prefix: Expr) -> BooleanExpr { - return BooleanExpr("starts_with", [self, prefix]) + func startsWith(_ prefix: String) -> BooleanExpression { + return BooleanExpression("starts_with", [self, Helper.sendableToExpr(prefix)]) } - func endsWith(_ suffix: String) -> BooleanExpr { - return BooleanExpr("ends_with", [self, Helper.sendableToExpr(suffix)]) + func startsWith(_ prefix: Expression) -> BooleanExpression { + return BooleanExpression("starts_with", [self, prefix]) } - func endsWith(_ suffix: Expr) -> BooleanExpr { - return BooleanExpr("ends_with", [self, suffix]) + func endsWith(_ suffix: String) -> BooleanExpression { + return BooleanExpression("ends_with", [self, Helper.sendableToExpr(suffix)]) } - func lowercased() -> FunctionExpr { - return FunctionExpr("to_lower", [self]) + func endsWith(_ suffix: Expression) -> BooleanExpression { + return BooleanExpression("ends_with", [self, suffix]) } - func uppercased() -> FunctionExpr { - return FunctionExpr("to_upper", [self]) + func lowercased() -> FunctionExpression { + return FunctionExpression("to_lower", [self]) } - func trim() -> FunctionExpr { - return FunctionExpr("trim", [self]) + func uppercased() -> FunctionExpression { + return FunctionExpression("to_upper", [self]) } - func strConcat(_ secondString: Expr, _ otherStrings: Expr...) -> FunctionExpr { - return FunctionExpr("str_concat", [self, secondString] + otherStrings) + func trim() -> FunctionExpression { + return FunctionExpression("trim", [self]) } - func strConcat(_ secondString: String, _ otherStrings: String...) -> FunctionExpr { - let exprs = [self] + [Helper.sendableToExpr(secondString)] + otherStrings - .map { Helper.sendableToExpr($0) } - return FunctionExpr("str_concat", exprs) + func strConcat(_ strings: [Expression]) -> FunctionExpression { + return FunctionExpression("str_concat", [self] + strings) } - func reverse() -> FunctionExpr { - return FunctionExpr("reverse", [self]) + func reverse() -> FunctionExpression { + return FunctionExpression("reverse", [self]) } - func replaceFirst(_ find: String, _ replace: String) -> FunctionExpr { - return FunctionExpr( + func replaceFirst(_ find: String, with replace: String) -> FunctionExpression { + return FunctionExpression( "replace_first", [self, Helper.sendableToExpr(find), Helper.sendableToExpr(replace)] ) } - func replaceFirst(_ find: Expr, _ replace: Expr) -> FunctionExpr { - return FunctionExpr("replace_first", [self, find, replace]) + func replaceFirst(_ find: Expression, with replace: Expression) -> FunctionExpression { + return FunctionExpression("replace_first", [self, find, replace]) } - func replaceAll(_ find: String, _ replace: String) -> FunctionExpr { - return FunctionExpr( + func replaceAll(_ find: String, with replace: String) -> FunctionExpression { + return FunctionExpression( "replace_all", [self, Helper.sendableToExpr(find), Helper.sendableToExpr(replace)] ) } - func replaceAll(_ find: Expr, _ replace: Expr) -> FunctionExpr { - return FunctionExpr("replace_all", [self, find, replace]) + func replaceAll(_ find: Expression, with replace: Expression) -> FunctionExpression { + return FunctionExpression("replace_all", [self, find, replace]) } - func byteLength() -> FunctionExpr { - return FunctionExpr("byte_length", [self]) + func byteLength() -> FunctionExpression { + return FunctionExpression("byte_length", [self]) } - func substr(_ position: Int, _ length: Int? = nil) -> FunctionExpr { + func substr(position: Int, length: Int? = nil) -> FunctionExpression { let positionExpr = Helper.sendableToExpr(position) if let length = length { - return FunctionExpr("substr", [self, positionExpr, Helper.sendableToExpr(length)]) + return FunctionExpression("substr", [self, positionExpr, Helper.sendableToExpr(length)]) } else { - return FunctionExpr("substr", [self, positionExpr]) + return FunctionExpression("substr", [self, positionExpr]) } } - func substr(_ position: Expr, _ length: Expr? = nil) -> FunctionExpr { + func substr(position: Expression, length: Expression? = nil) -> FunctionExpression { if let length = length { - return FunctionExpr("substr", [self, position, length]) + return FunctionExpression("substr", [self, position, length]) } else { - return FunctionExpr("substr", [self, position]) + return FunctionExpression("substr", [self, position]) } } // --- Added Map Operations --- - func mapGet(_ subfield: String) -> FunctionExpr { - return FunctionExpr("map_get", [self, Constant(subfield)]) + func mapGet(_ subfield: String) -> FunctionExpression { + return FunctionExpression("map_get", [self, Constant(subfield)]) } - func mapRemove(_ key: String) -> FunctionExpr { - return FunctionExpr("map_remove", [self, Helper.sendableToExpr(key)]) + func mapRemove(_ key: String) -> FunctionExpression { + return FunctionExpression("map_remove", [self, Helper.sendableToExpr(key)]) } - func mapRemove(_ keyExpr: Expr) -> FunctionExpr { - return FunctionExpr("map_remove", [self, keyExpr]) + func mapRemove(_ keyExpr: Expression) -> FunctionExpression { + return FunctionExpression("map_remove", [self, keyExpr]) } - func mapMerge(_ secondMap: [String: Sendable], - _ otherMaps: [String: Sendable]...) -> FunctionExpr { - let secondMapExpr = Helper.sendableToExpr(secondMap) - let otherMapExprs = otherMaps.map { Helper.sendableToExpr($0) } - return FunctionExpr("map_merge", [self, secondMapExpr] + otherMapExprs) + func mapMerge(_ maps: [[String: Sendable]]) -> FunctionExpression { + let mapExprs = maps.map { Helper.sendableToExpr($0) } + return FunctionExpression("map_merge", [self] + mapExprs) } - func mapMerge(_ secondMap: Expr, _ otherMaps: Expr...) -> FunctionExpr { - return FunctionExpr("map_merge", [self, secondMap] + otherMaps) + func mapMerge(_ maps: [Expression]) -> FunctionExpression { + return FunctionExpression("map_merge", [self] + maps) } // --- Added Aggregate Operations (on Expr) --- @@ -380,7 +379,7 @@ public extension Expr { return AggregateFunction("sum", [self]) } - func avg() -> AggregateFunction { + func average() -> AggregateFunction { return AggregateFunction("avg", [self]) } @@ -394,123 +393,121 @@ public extension Expr { // MARK: Logical min/max - func logicalMaximum(_ second: Expr, _ others: Expr...) -> FunctionExpr { - return FunctionExpr("logical_maximum", [self, second] + others) + func logicalMaximum(_ expressions: [Expression]) -> FunctionExpression { + return FunctionExpression("logical_maximum", [self] + expressions) } - func logicalMaximum(_ second: Sendable, _ others: Sendable...) -> FunctionExpr { - let exprs = [self] + [Helper.sendableToExpr(second)] + others - .map { Helper.sendableToExpr($0) } - return FunctionExpr("logical_maximum", exprs) + func logicalMaximum(_ values: [Sendable]) -> FunctionExpression { + let exprs = [self] + values.map { Helper.sendableToExpr($0) } + return FunctionExpression("logical_maximum", exprs) } - func logicalMinimum(_ second: Expr, _ others: Expr...) -> FunctionExpr { - return FunctionExpr("logical_minimum", [self, second] + others) + func logicalMinimum(_ expressions: [Expression]) -> FunctionExpression { + return FunctionExpression("logical_minimum", [self] + expressions) } - func logicalMinimum(_ second: Sendable, _ others: Sendable...) -> FunctionExpr { - let exprs = [self] + [Helper.sendableToExpr(second)] + others - .map { Helper.sendableToExpr($0) } - return FunctionExpr("logical_minimum", exprs) + func logicalMinimum(_ values: [Sendable]) -> FunctionExpression { + let exprs = [self] + values.map { Helper.sendableToExpr($0) } + return FunctionExpression("logical_minimum", exprs) } // MARK: Vector Operations - func vectorLength() -> FunctionExpr { - return FunctionExpr("vector_length", [self]) + func vectorLength() -> FunctionExpression { + return FunctionExpression("vector_length", [self]) } - func cosineDistance(_ other: Expr) -> FunctionExpr { - return FunctionExpr("cosine_distance", [self, other]) + func cosineDistance(_ expression: Expression) -> FunctionExpression { + return FunctionExpression("cosine_distance", [self, expression]) } - func cosineDistance(_ other: VectorValue) -> FunctionExpr { - return FunctionExpr("cosine_distance", [self, Helper.sendableToExpr(other)]) + func cosineDistance(_ vector: VectorValue) -> FunctionExpression { + return FunctionExpression("cosine_distance", [self, Helper.sendableToExpr(vector)]) } - func cosineDistance(_ other: [Double]) -> FunctionExpr { - return FunctionExpr("cosine_distance", [self, Helper.sendableToExpr(other)]) + func cosineDistance(_ vector: [Double]) -> FunctionExpression { + return FunctionExpression("cosine_distance", [self, Helper.sendableToExpr(vector)]) } - func dotProduct(_ other: Expr) -> FunctionExpr { - return FunctionExpr("dot_product", [self, other]) + func dotProduct(_ expression: Expression) -> FunctionExpression { + return FunctionExpression("dot_product", [self, expression]) } - func dotProduct(_ other: VectorValue) -> FunctionExpr { - return FunctionExpr("dot_product", [self, Helper.sendableToExpr(other)]) + func dotProduct(_ vector: VectorValue) -> FunctionExpression { + return FunctionExpression("dot_product", [self, Helper.sendableToExpr(vector)]) } - func dotProduct(_ other: [Double]) -> FunctionExpr { - return FunctionExpr("dot_product", [self, Helper.sendableToExpr(other)]) + func dotProduct(_ vector: [Double]) -> FunctionExpression { + return FunctionExpression("dot_product", [self, Helper.sendableToExpr(vector)]) } - func euclideanDistance(_ other: Expr) -> FunctionExpr { - return FunctionExpr("euclidean_distance", [self, other]) + func euclideanDistance(_ expression: Expression) -> FunctionExpression { + return FunctionExpression("euclidean_distance", [self, expression]) } - func euclideanDistance(_ other: VectorValue) -> FunctionExpr { - return FunctionExpr("euclidean_distance", [self, Helper.sendableToExpr(other)]) + func euclideanDistance(_ vector: VectorValue) -> FunctionExpression { + return FunctionExpression("euclidean_distance", [self, Helper.sendableToExpr(vector)]) } - func euclideanDistance(_ other: [Double]) -> FunctionExpr { - return FunctionExpr("euclidean_distance", [self, Helper.sendableToExpr(other)]) + func euclideanDistance(_ vector: [Double]) -> FunctionExpression { + return FunctionExpression("euclidean_distance", [self, Helper.sendableToExpr(vector)]) } - func manhattanDistance(_ other: Expr) -> FunctionExpr { - return FunctionExpr("manhattan_distance", [self, other]) + func manhattanDistance(_ expression: Expression) -> FunctionExpression { + return FunctionExpression("manhattan_distance", [self, expression]) } - func manhattanDistance(_ other: VectorValue) -> FunctionExpr { - return FunctionExpr("manhattan_distance", [self, Helper.sendableToExpr(other)]) + func manhattanDistance(_ vector: VectorValue) -> FunctionExpression { + return FunctionExpression("manhattan_distance", [self, Helper.sendableToExpr(vector)]) } - func manhattanDistance(_ other: [Double]) -> FunctionExpr { - return FunctionExpr("manhattan_distance", [self, Helper.sendableToExpr(other)]) + func manhattanDistance(_ vector: [Double]) -> FunctionExpression { + return FunctionExpression("manhattan_distance", [self, Helper.sendableToExpr(vector)]) } // MARK: Timestamp operations - func unixMicrosToTimestamp() -> FunctionExpr { - return FunctionExpr("unix_micros_to_timestamp", [self]) + func unixMicrosToTimestamp() -> FunctionExpression { + return FunctionExpression("unix_micros_to_timestamp", [self]) } - func timestampToUnixMicros() -> FunctionExpr { - return FunctionExpr("timestamp_to_unix_micros", [self]) + func timestampToUnixMicros() -> FunctionExpression { + return FunctionExpression("timestamp_to_unix_micros", [self]) } - func unixMillisToTimestamp() -> FunctionExpr { - return FunctionExpr("unix_millis_to_timestamp", [self]) + func unixMillisToTimestamp() -> FunctionExpression { + return FunctionExpression("unix_millis_to_timestamp", [self]) } - func timestampToUnixMillis() -> FunctionExpr { - return FunctionExpr("timestamp_to_unix_millis", [self]) + func timestampToUnixMillis() -> FunctionExpression { + return FunctionExpression("timestamp_to_unix_millis", [self]) } - func unixSecondsToTimestamp() -> FunctionExpr { - return FunctionExpr("unix_seconds_to_timestamp", [self]) + func unixSecondsToTimestamp() -> FunctionExpression { + return FunctionExpression("unix_seconds_to_timestamp", [self]) } - func timestampToUnixSeconds() -> FunctionExpr { - return FunctionExpr("timestamp_to_unix_seconds", [self]) + func timestampToUnixSeconds() -> FunctionExpression { + return FunctionExpression("timestamp_to_unix_seconds", [self]) } - func timestampAdd(_ unit: Expr, _ amount: Expr) -> FunctionExpr { - return FunctionExpr("timestamp_add", [self, unit, amount]) + func timestampAdd(amount: Expression, unit: Expression) -> FunctionExpression { + return FunctionExpression("timestamp_add", [self, unit, amount]) } - func timestampAdd(_ unit: TimeUnit, _ amount: Int) -> FunctionExpr { - return FunctionExpr( + func timestampAdd(_ amount: Int, _ unit: TimeUnit) -> FunctionExpression { + return FunctionExpression( "timestamp_add", [self, Helper.sendableToExpr(unit), Helper.sendableToExpr(amount)] ) } - func timestampSub(_ unit: Expr, _ amount: Expr) -> FunctionExpr { - return FunctionExpr("timestamp_sub", [self, unit, amount]) + func timestampSub(amount: Expression, unit: Expression) -> FunctionExpression { + return FunctionExpression("timestamp_sub", [self, unit, amount]) } - func timestampSub(_ unit: TimeUnit, _ amount: Int) -> FunctionExpr { - return FunctionExpr( + func timestampSub(_ amount: Int, _ unit: TimeUnit) -> FunctionExpression { + return FunctionExpression( "timestamp_sub", [self, Helper.sendableToExpr(unit), Helper.sendableToExpr(amount)] ) @@ -518,72 +515,72 @@ public extension Expr { // MARK: - Bitwise operations - func bitAnd(_ otherBits: Int) -> FunctionExpr { - return FunctionExpr("bit_and", [self, Helper.sendableToExpr(otherBits)]) + func bitAnd(_ otherBits: Int) -> FunctionExpression { + return FunctionExpression("bit_and", [self, Helper.sendableToExpr(otherBits)]) } - func bitAnd(_ otherBits: UInt8) -> FunctionExpr { - return FunctionExpr("bit_and", [self, Helper.sendableToExpr(otherBits)]) + func bitAnd(_ otherBits: UInt8) -> FunctionExpression { + return FunctionExpression("bit_and", [self, Helper.sendableToExpr(otherBits)]) } - func bitAnd(_ bitsExpression: Expr) -> FunctionExpr { - return FunctionExpr("bit_and", [self, bitsExpression]) + func bitAnd(_ bitsExpression: Expression) -> FunctionExpression { + return FunctionExpression("bit_and", [self, bitsExpression]) } - func bitOr(_ otherBits: Int) -> FunctionExpr { - return FunctionExpr("bit_or", [self, Helper.sendableToExpr(otherBits)]) + func bitOr(_ otherBits: Int) -> FunctionExpression { + return FunctionExpression("bit_or", [self, Helper.sendableToExpr(otherBits)]) } - func bitOr(_ otherBits: UInt8) -> FunctionExpr { - return FunctionExpr("bit_or", [self, Helper.sendableToExpr(otherBits)]) + func bitOr(_ otherBits: UInt8) -> FunctionExpression { + return FunctionExpression("bit_or", [self, Helper.sendableToExpr(otherBits)]) } - func bitOr(_ bitsExpression: Expr) -> FunctionExpr { - return FunctionExpr("bit_or", [self, bitsExpression]) + func bitOr(_ bitsExpression: Expression) -> FunctionExpression { + return FunctionExpression("bit_or", [self, bitsExpression]) } - func bitXor(_ otherBits: Int) -> FunctionExpr { - return FunctionExpr("bit_xor", [self, Helper.sendableToExpr(otherBits)]) + func bitXor(_ otherBits: Int) -> FunctionExpression { + return FunctionExpression("bit_xor", [self, Helper.sendableToExpr(otherBits)]) } - func bitXor(_ otherBits: UInt8) -> FunctionExpr { - return FunctionExpr("bit_xor", [self, Helper.sendableToExpr(otherBits)]) + func bitXor(_ otherBits: UInt8) -> FunctionExpression { + return FunctionExpression("bit_xor", [self, Helper.sendableToExpr(otherBits)]) } - func bitXor(_ bitsExpression: Expr) -> FunctionExpr { - return FunctionExpr("bit_xor", [self, bitsExpression]) + func bitXor(_ bitsExpression: Expression) -> FunctionExpression { + return FunctionExpression("bit_xor", [self, bitsExpression]) } - func bitNot() -> FunctionExpr { - return FunctionExpr("bit_not", [self]) + func bitNot() -> FunctionExpression { + return FunctionExpression("bit_not", [self]) } - func bitLeftShift(_ y: Int) -> FunctionExpr { - return FunctionExpr("bit_left_shift", [self, Helper.sendableToExpr(y)]) + func bitLeftShift(_ y: Int) -> FunctionExpression { + return FunctionExpression("bit_left_shift", [self, Helper.sendableToExpr(y)]) } - func bitLeftShift(_ numberExpr: Expr) -> FunctionExpr { - return FunctionExpr("bit_left_shift", [self, numberExpr]) + func bitLeftShift(_ numberExpr: Expression) -> FunctionExpression { + return FunctionExpression("bit_left_shift", [self, numberExpr]) } - func bitRightShift(_ y: Int) -> FunctionExpr { - return FunctionExpr("bit_right_shift", [self, Helper.sendableToExpr(y)]) + func bitRightShift(_ y: Int) -> FunctionExpression { + return FunctionExpression("bit_right_shift", [self, Helper.sendableToExpr(y)]) } - func bitRightShift(_ numberExpr: Expr) -> FunctionExpr { - return FunctionExpr("bit_right_shift", [self, numberExpr]) + func bitRightShift(_ numberExpr: Expression) -> FunctionExpression { + return FunctionExpression("bit_right_shift", [self, numberExpr]) } - func documentId() -> FunctionExpr { - return FunctionExpr("document_id", [self]) + func documentId() -> FunctionExpression { + return FunctionExpression("document_id", [self]) } - func ifError(_ catchExpr: Expr) -> FunctionExpr { - return FunctionExpr("if_error", [self, catchExpr]) + func ifError(_ catchExpr: Expression) -> FunctionExpression { + return FunctionExpression("if_error", [self, catchExpr]) } - func ifError(_ catchValue: Sendable) -> FunctionExpr { - return FunctionExpr("if_error", [self, Helper.sendableToExpr(catchValue)]) + func ifError(_ catchValue: Sendable) -> FunctionExpression { + return FunctionExpression("if_error", [self, Helper.sendableToExpr(catchValue)]) } // MARK: Sorting diff --git a/Firestore/Swift/Source/Helper/PipelineHelper.swift b/Firestore/Swift/Source/Helper/PipelineHelper.swift index 0d0e6b55d59..b5b38e8dbfe 100644 --- a/Firestore/Swift/Source/Helper/PipelineHelper.swift +++ b/Firestore/Swift/Source/Helper/PipelineHelper.swift @@ -13,12 +13,12 @@ // limitations under the License. enum Helper { - static func sendableToExpr(_ value: Sendable?) -> Expr { + static func sendableToExpr(_ value: Sendable?) -> Expression { guard let value = value else { return Constant.nil } - if let exprValue = value as? Expr { + if let exprValue = value as? Expression { return exprValue } else if let dictionaryValue = value as? [String: Sendable?] { return map(dictionaryValue) @@ -31,8 +31,8 @@ enum Helper { } } - static func selectablesToMap(selectables: [Selectable]) -> [String: Expr] { - let exprMap = selectables.reduce(into: [String: Expr]()) { result, selectable in + static func selectablesToMap(selectables: [Selectable]) -> [String: Expression] { + let exprMap = selectables.reduce(into: [String: Expression]()) { result, selectable in guard let value = selectable as? SelectableWrapper else { fatalError("Selectable class must conform to SelectableWrapper.") } @@ -41,20 +41,20 @@ enum Helper { return exprMap } - static func map(_ elements: [String: Sendable?]) -> FunctionExpr { - var result: [Expr] = [] + static func map(_ elements: [String: Sendable?]) -> FunctionExpression { + var result: [Expression] = [] for (key, value) in elements { result.append(Constant(key)) result.append(sendableToExpr(value)) } - return FunctionExpr("map", result) + return FunctionExpression("map", result) } - static func array(_ elements: [Sendable?]) -> FunctionExpr { + static func array(_ elements: [Sendable?]) -> FunctionExpression { let transformedElements = elements.map { element in sendableToExpr(element) } - return FunctionExpr("array", transformedElements) + return FunctionExpression("array", transformedElements) } // This function is used to convert Swift type into Objective-C type. @@ -63,7 +63,7 @@ enum Helper { return Constant.nil.bridge } - if let exprValue = value as? Expr { + if let exprValue = value as? Expression { return exprValue.toBridge() } else if let aggregateFunctionValue = value as? AggregateFunction { return aggregateFunctionValue.toBridge() diff --git a/Firestore/Swift/Source/PipelineWrapper.swift b/Firestore/Swift/Source/PipelineWrapper.swift index a057c2e4ea2..f0310a535cc 100644 --- a/Firestore/Swift/Source/PipelineWrapper.swift +++ b/Firestore/Swift/Source/PipelineWrapper.swift @@ -22,5 +22,5 @@ protocol AggregateBridgeWrapper { protocol SelectableWrapper: Sendable { var alias: String { get } - var expr: Expr { get } + var expr: Expression { get } } diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Aggregation/AggregateFunction.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Aggregation/AggregateFunction.swift index 6d7e05098a9..3adf83239db 100644 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Aggregation/AggregateFunction.swift +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/Aggregation/AggregateFunction.swift @@ -22,9 +22,9 @@ public class AggregateFunction: AggregateBridgeWrapper, @unchecked Sendable { let bridge: AggregateFunctionBridge let functionName: String - let args: [Expr] + let args: [Expression] - public init(_ functionName: String, _ args: [Expr]) { + public init(_ functionName: String, _ args: [Expression]) { self.functionName = functionName self.args = args bridge = AggregateFunctionBridge( @@ -34,7 +34,7 @@ public class AggregateFunction: AggregateBridgeWrapper, @unchecked Sendable { ) } - public func `as`(_ name: String) -> AggregateWithAlias { - return AggregateWithAlias(aggregate: self, alias: name) + public func `as`(_ name: String) -> AliasedAggregate { + return AliasedAggregate(aggregate: self, alias: name) } } diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Aggregation/AggregateWithAlias.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Aggregation/AliasedAggregate.swift similarity index 94% rename from Firestore/Swift/Source/SwiftAPI/Pipeline/Aggregation/AggregateWithAlias.swift rename to Firestore/Swift/Source/SwiftAPI/Pipeline/Aggregation/AliasedAggregate.swift index 8a1871907c6..5c16126c6a8 100644 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Aggregation/AggregateWithAlias.swift +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/Aggregation/AliasedAggregate.swift @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -public struct AggregateWithAlias { +public struct AliasedAggregate { public let aggregate: AggregateFunction public let alias: String } diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Aggregation/CountAll.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Aggregation/CountAll.swift index 064eb6d99bc..2c08f8e31d0 100644 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Aggregation/CountAll.swift +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/Aggregation/CountAll.swift @@ -12,7 +12,31 @@ // See the License for the specific language governing permissions and // limitations under the License. +/// +/// Represents an aggregation that counts all documents in the input set. +/// +/// `CountAll` is used within the `aggregate` pipeline stage to get the total number of documents +/// that match the query criteria up to that point. +/// +/// Example usage: +/// ```swift +/// // Count all books in the collection +/// firestore.pipeline() +/// .collection("books") +/// .aggregate([ +/// CountAll().as("totalBooks") +/// ]) +/// +/// // Count all sci-fi books published after 1960 +/// firestore.pipeline() +/// .collection("books") +/// .where(Field("genre").equal("Science Fiction") && Field("published").greaterThan(1960)) +/// .aggregate([ +/// CountAll().as("sciFiBooksCount") +/// ]) +/// ``` public class CountAll: AggregateFunction, @unchecked Sendable { + /// Initializes a new `CountAll` aggregation. public init() { super.init("count", []) } diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/ExprWithAlias.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/AliasedExpression.swift similarity index 81% rename from Firestore/Swift/Source/SwiftAPI/Pipeline/ExprWithAlias.swift rename to Firestore/Swift/Source/SwiftAPI/Pipeline/AliasedExpression.swift index 247427f2fd8..4c026ec34e7 100644 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/ExprWithAlias.swift +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/AliasedExpression.swift @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -public struct ExprWithAlias: Selectable, SelectableWrapper, Sendable { +public struct AliasedExpression: Selectable, SelectableWrapper, Sendable { public var alias: String - public var expr: Expr + public var expr: Expression - init(_ expr: Expr, _ alias: String) { + init(_ expr: Expression, _ alias: String) { self.alias = alias self.expr = expr } diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/ArrayContains.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/ArrayContains.swift index 7a70cfbc77b..c8b9322eef7 100644 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/ArrayContains.swift +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/ArrayContains.swift @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -public class ArrayContains: BooleanExpr, @unchecked Sendable { +public class ArrayContains: BooleanExpression, @unchecked Sendable { public init(fieldName: String, values: Sendable...) { super.init("array_contains", values.map { Helper.sendableToExpr($0) }) } diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Ascending.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Ascending.swift deleted file mode 100644 index e872b6e7f8a..00000000000 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Ascending.swift +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -public class Ascending: Ordering, @unchecked Sendable { - public init(_ fieldName: String) { - super.init(expr: Field(fieldName), direction: .ascending) - } -} diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Descending.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Descending.swift deleted file mode 100644 index 584d7b7ada3..00000000000 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Descending.swift +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -public class Descending: Ordering, @unchecked Sendable { - public init(_ fieldName: String) { - super.init(expr: Field(fieldName), direction: .descending) - } -} diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/DocumentId.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/DocumentId.swift deleted file mode 100644 index 70c621d8cbd..00000000000 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/DocumentId.swift +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -public class DocumentId: Field, @unchecked Sendable { - public init() { - super.init("__name__") - } -} diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/FunctionExpr/BooleanExpr.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/FunctionExpr/BooleanExpr.swift deleted file mode 100644 index 701276d51f7..00000000000 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/FunctionExpr/BooleanExpr.swift +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -public class BooleanExpr: FunctionExpr, @unchecked Sendable { - override public init(_ functionName: String, _ agrs: [Expr]) { - super.init(functionName, agrs) - } - - public func countIf() -> AggregateFunction { - return AggregateFunction("count_if", [self]) - } - - public func then(_ thenExpr: Expr, else elseExpr: Expr) -> FunctionExpr { - return FunctionExpr("cond", [self, thenExpr, elseExpr]) - } - - public static func && (lhs: BooleanExpr, - rhs: @autoclosure () throws -> BooleanExpr) rethrows -> BooleanExpr { - try BooleanExpr("and", [lhs, rhs()]) - } - - public static func || (lhs: BooleanExpr, - rhs: @autoclosure () throws -> BooleanExpr) rethrows -> BooleanExpr { - try BooleanExpr("or", [lhs, rhs()]) - } - - public static func ^ (lhs: BooleanExpr, - rhs: @autoclosure () throws -> BooleanExpr) rethrows -> BooleanExpr { - try BooleanExpr("xor", [lhs, rhs()]) - } - - public static prefix func ! (lhs: BooleanExpr) -> BooleanExpr { - return BooleanExpr("not", [lhs]) - } -} diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expression.swift similarity index 53% rename from Firestore/Swift/Source/SwiftAPI/Pipeline/Expr.swift rename to Firestore/Swift/Source/SwiftAPI/Pipeline/Expression.swift index d05c6a4c251..69529304899 100644 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr.swift +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expression.swift @@ -19,8 +19,7 @@ #endif // SWIFT_PACKAGE import Foundation -// TODO: the implementation of `Expr` is not complete -public protocol Expr: Sendable { +public protocol Expression: Sendable { /// Assigns an alias to this expression. /// /// Aliases are useful for renaming fields in the output of a stage or for giving meaningful @@ -32,145 +31,149 @@ public protocol Expr: Sendable { /// ``` /// /// - Parameter name: The alias to assign to this expression. - /// - Returns: A new `ExprWithAlias` wrapping this expression with the alias. - func `as`(_ name: String) -> ExprWithAlias + /// - Returns: A new `AliasedExpression` wrapping this expression with the alias. + func `as`(_ name: String) -> AliasedExpression // --- Added Mathematical Operations --- - /// Creates an expression that adds this expression to one or more other expressions. - /// Assumes `self` and all parameters evaluate to compatible types for addition (e.g., numbers, or + /// Creates an expression that adds another expression to this expression. + /// To add multiple expressions, chain calls to this method. + /// Assumes `self` and the parameter evaluate to compatible types for addition (e.g., numbers, or /// string/array concatenation if supported by the specific "add" implementation). /// /// ```swift - /// // Add the value of the 'quantity' field and the 'reserve' field. + /// // Add the value of the "quantity" field and the "reserve" field. /// Field("quantity").add(Field("reserve")) /// /// // Add multiple numeric fields - /// Field("subtotal").add(Field("tax"), Field("shipping")) + /// Field("subtotal").add(Field("tax")).add(Field("shipping")) /// ``` /// - /// - Parameter value: Expr` values to add. - /// - Returns: A new `FunctionExpr` representing the addition operation. - func add(_ value: Expr) -> FunctionExpr + /// - Parameter value: An `Expression` to add. + /// - Returns: A new `FunctionExpression` representing the addition operation. + func add(_ value: Expression) -> FunctionExpression - /// Creates an expression that adds this expression to one or more literal values. - /// Assumes `self` and all parameters evaluate to compatible types for addition. + /// Creates an expression that adds a literal value to this expression. + /// To add multiple literals, chain calls to this method. + /// Assumes `self` and the parameter evaluate to compatible types for addition. /// /// ```swift - /// // Add 5 to the 'count' field + /// // Add 5 to the "count" field /// Field("count").add(5) /// /// // Add multiple literal numbers - /// Field("score").add(10, 20, -5) + /// Field("score").add(10).add(20).add(-5) /// ``` /// - /// - Parameter value: Expr` value to add. - /// - Returns: A new `FunctionExpr` representing the addition operation. - func add(_ value: Sendable) -> FunctionExpr + /// - Parameter value: A `Sendable` literal value to add. + /// - Returns: A new `FunctionExpression` representing the addition operation. + func add(_ value: Sendable) -> FunctionExpression /// Creates an expression that subtracts another expression from this expression. /// Assumes `self` and `other` evaluate to numeric types. /// /// ```swift - /// // Subtract the 'discount' field from the 'price' field + /// // Subtract the "discount" field from the "price" field /// Field("price").subtract(Field("discount")) /// ``` /// - /// - Parameter other: The `Expr` (evaluating to a number) to subtract from this expression. - /// - Returns: A new `FunctionExpr` representing the subtraction operation. - func subtract(_ other: Expr) -> FunctionExpr + /// - Parameter other: The `Expression` (evaluating to a number) to subtract from this expression. + /// - Returns: A new `FunctionExpression` representing the subtraction operation. + func subtract(_ other: Expression) -> FunctionExpression /// Creates an expression that subtracts a literal value from this expression. /// Assumes `self` evaluates to a numeric type. /// /// ```swift - /// // Subtract 20 from the value of the 'total' field + /// // Subtract 20 from the value of the "total" field /// Field("total").subtract(20) /// ``` /// /// - Parameter other: The `Sendable` literal (numeric) value to subtract from this expression. - /// - Returns: A new `FunctionExpr` representing the subtraction operation. - func subtract(_ other: Sendable) -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the subtraction operation. + func subtract(_ other: Sendable) -> FunctionExpression - /// Creates an expression that multiplies this expression by one or more other expressions. - /// Assumes `self` and all parameters evaluate to numeric types. + /// Creates an expression that multiplies this expression by another expression. + /// To multiply multiple expressions, chain calls to this method. + /// Assumes `self` and the parameter evaluate to numeric types. /// /// ```swift - /// // Multiply the 'quantity' field by the 'price' field + /// // Multiply the "quantity" field by the "price" field /// Field("quantity").multiply(Field("price")) /// - /// // Multiply 'rate' by 'time' and 'conversionFactor' fields - /// Field("rate").multiply(Field("time"), Field("conversionFactor")) + /// // Multiply "rate" by "time" and "conversionFactor" fields + /// Field("rate").multiply(Field("time")).multiply(Field("conversionFactor")) /// ``` /// - /// - Parameter value: `Expr` value to multiply by. - /// - Returns: A new `FunctionExpr` representing the multiplication operation. - func multiply(_ value: Expr) -> FunctionExpr + /// - Parameter value: An `Expression` to multiply by. + /// - Returns: A new `FunctionExpression` representing the multiplication operation. + func multiply(_ value: Expression) -> FunctionExpression - /// Creates an expression that multiplies this expression by one or more literal values. + /// Creates an expression that multiplies this expression by a literal value. + /// To multiply multiple literals, chain calls to this method. /// Assumes `self` evaluates to a numeric type. /// /// ```swift - /// // Multiply the 'score' by 1.1 + /// // Multiply the "score" by 1.1 /// Field("score").multiply(1.1) /// - /// // Multiply 'base' by 2 and then by 3.0 - /// Field("base").multiply(2, 3.0) + /// // Multiply "base" by 2 and then by 3.0 + /// Field("base").multiply(2).multiply(3.0) /// ``` /// - /// - Parameter value: `Sendable` literal value to multiply by. - /// - Returns: A new `FunctionExpr` representing the multiplication operation. - func multiply(_ value: Sendable) -> FunctionExpr + /// - Parameter value: A `Sendable` literal value to multiply by. + /// - Returns: A new `FunctionExpression` representing the multiplication operation. + func multiply(_ value: Sendable) -> FunctionExpression /// Creates an expression that divides this expression by another expression. /// Assumes `self` and `other` evaluate to numeric types. /// /// ```swift - /// // Divide the 'total' field by the 'count' field + /// // Divide the "total" field by the "count" field /// Field("total").divide(Field("count")) /// ``` /// - /// - Parameter other: The `Expr` (evaluating to a number) to divide by. - /// - Returns: A new `FunctionExpr` representing the division operation. - func divide(_ other: Expr) -> FunctionExpr + /// - Parameter other: The `Expression` (evaluating to a number) to divide by. + /// - Returns: A new `FunctionExpression` representing the division operation. + func divide(_ other: Expression) -> FunctionExpression /// Creates an expression that divides this expression by a literal value. /// Assumes `self` evaluates to a numeric type. /// /// ```swift - /// // Divide the 'value' field by 10 + /// // Divide the "value" field by 10 /// Field("value").divide(10) /// ``` /// /// - Parameter other: The `Sendable` literal (numeric) value to divide by. - /// - Returns: A new `FunctionExpr` representing the division operation. - func divide(_ other: Sendable) -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the division operation. + func divide(_ other: Sendable) -> FunctionExpression /// Creates an expression that calculates the modulo (remainder) of dividing this expression by /// another expression. /// Assumes `self` and `other` evaluate to numeric types. /// /// ```swift - /// // Calculate the remainder of dividing the 'value' field by the 'divisor' field + /// // Calculate the remainder of dividing the "value" field by the "divisor" field /// Field("value").mod(Field("divisor")) /// ``` /// - /// - Parameter other: The `Expr` (evaluating to a number) to use as the divisor. - /// - Returns: A new `FunctionExpr` representing the modulo operation. - func mod(_ other: Expr) -> FunctionExpr + /// - Parameter other: The `Expression` (evaluating to a number) to use as the divisor. + /// - Returns: A new `FunctionExpression` representing the modulo operation. + func mod(_ other: Expression) -> FunctionExpression /// Creates an expression that calculates the modulo (remainder) of dividing this expression by a /// literal value. /// Assumes `self` evaluates to a numeric type. /// /// ```swift - /// // Calculate the remainder of dividing the 'value' field by 10 + /// // Calculate the remainder of dividing the "value" field by 10 /// Field("value").mod(10) /// ``` /// /// - Parameter other: The `Sendable` literal (numeric) value to use as the divisor. - /// - Returns: A new `FunctionExpr` representing the modulo operation. - func mod(_ other: Sendable) -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the modulo operation. + func mod(_ other: Sendable) -> FunctionExpression // --- Added Array Operations --- @@ -179,122 +182,142 @@ public protocol Expr: Sendable { /// Assumes `self` and all parameters evaluate to arrays. /// /// ```swift - /// // Combine the 'items' array with 'otherItems' and 'archiveItems' array fields. + /// // Combine the "items" array with "otherItems" and "archiveItems" array fields. /// Field("items").arrayConcat(Field("otherItems"), Field("archiveItems")) /// ``` - /// - Parameter secondArray: An `Expr` (evaluating to an array) to concatenate. - /// - Parameter otherArrays: Optional additional `Expr` values (evaluating to arrays) to + /// - Parameter arrays: An array of at least one `Expression` (evaluating to an array) to /// concatenate. - /// - Returns: A new `FunctionExpr` representing the concatenated array. - func arrayConcat(_ secondArray: Expr, _ otherArrays: Expr...) -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the concatenated array. + func arrayConcat(_ arrays: [Expression]) -> FunctionExpression /// Creates an expression that concatenates an array expression (from `self`) with one or more /// array literals. /// Assumes `self` evaluates to an array. /// /// ```swift - /// // Combine 'tags' (an array field) with ["new", "featured"] and ["urgent"] + /// // Combine "tags" (an array field) with ["new", "featured"] and ["urgent"] /// Field("tags").arrayConcat(["new", "featured"], ["urgent"]) /// ``` - /// - Parameter secondArray: An array literal of `Sendable` values to concatenate. - /// - Parameter otherArrays: Optional additional array literals of `Sendable` values to - /// concatenate. - /// - Returns: A new `FunctionExpr` representing the concatenated array. - func arrayConcat(_ secondArray: [Sendable], _ otherArrays: [Sendable]...) -> FunctionExpr + /// - Parameter arrays: An array of at least one `Sendable` values to concatenate. + /// - Returns: A new `FunctionExpression` representing the concatenated array. + func arrayConcat(_ arrays: [[Sendable]]) -> FunctionExpression /// Creates an expression that checks if an array (from `self`) contains a specific element /// expression. /// Assumes `self` evaluates to an array. /// /// ```swift - /// // Check if 'sizes' contains the value from 'selectedSize' field + /// // Check if "sizes" contains the value from "selectedSize" field /// Field("sizes").arrayContains(Field("selectedSize")) /// ``` /// - /// - Parameter element: The `Expr` representing the element to search for in the array. - /// - Returns: A new `BooleanExpr` representing the 'array_contains' comparison. - func arrayContains(_ element: Expr) -> BooleanExpr + /// - Parameter element: The `Expression` representing the element to search for in the array. + /// - Returns: A new `BooleanExpr` representing the "array_contains" comparison. + func arrayContains(_ element: Expression) -> BooleanExpression /// Creates an expression that checks if an array (from `self`) contains a specific literal /// element. /// Assumes `self` evaluates to an array. /// /// ```swift - /// // Check if 'colors' array contains "red" + /// // Check if "colors" array contains "red" /// Field("colors").arrayContains("red") /// ``` /// /// - Parameter element: The `Sendable` literal element to search for in the array. - /// - Returns: A new `BooleanExpr` representing the 'array_contains' comparison. - func arrayContains(_ element: Sendable) -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "array_contains" comparison. + func arrayContains(_ element: Sendable) -> BooleanExpression /// Creates an expression that checks if an array (from `self`) contains all the specified element /// expressions. /// Assumes `self` evaluates to an array. /// /// ```swift - /// // Check if 'candidateSkills' contains all skills from 'requiredSkill1' and 'requiredSkill2' + /// // Check if "candidateSkills" contains all skills from "requiredSkill1" and "requiredSkill2" /// fields /// Field("candidateSkills").arrayContainsAll([Field("requiredSkill1"), Field("requiredSkill2")]) /// ``` /// - /// - Parameter values: A list of `Expr` elements to check for in the array represented + /// - Parameter values: A list of `Expression` elements to check for in the array represented /// by `self`. - /// - Returns: A new `BooleanExpr` representing the 'array_contains_all' comparison. - func arrayContainsAll(_ values: [Expr]) -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "array_contains_all" comparison. + func arrayContainsAll(_ values: [Expression]) -> BooleanExpression /// Creates an expression that checks if an array (from `self`) contains all the specified literal /// elements. /// Assumes `self` evaluates to an array. /// /// ```swift - /// // Check if 'tags' contains both "urgent" and "review" + /// // Check if "tags" contains both "urgent" and "review" /// Field("tags").arrayContainsAll(["urgent", "review"]) /// ``` /// - /// - Parameter values: A list of `Sendable` literal elements to check for in the array - /// represented by `self`. - /// - Returns: A new `BooleanExpr` representing the 'array_contains_all' comparison. - func arrayContainsAll(_ values: [Sendable]) -> BooleanExpr + /// - Parameter values: An array of at least one `Sendable` element to check for in the array. + /// - Returns: A new `BooleanExpr` representing the "array_contains_all" comparison. + func arrayContainsAll(_ values: [Sendable]) -> BooleanExpression + + /// Creates an expression that checks if an array (from `self`) contains all the specified element + /// expressions. + /// Assumes `self` evaluates to an array. + /// + /// ```swift + /// // Check if the "tags" array contains "foo", "bar", and "baz" + /// Field("tags").arrayContainsAll(Constant(["foo", "bar", "baz"])) + /// ``` + /// + /// - Parameter values: An `Expression` elements evaluated to be array. + /// - Returns: A new `BooleanExpr` representing the "array_contains_all" comparison. + func arrayContainsAll(_ arrayExpression: Expression) -> BooleanExpression /// Creates an expression that checks if an array (from `self`) contains any of the specified /// element expressions. /// Assumes `self` evaluates to an array. /// /// ```swift - /// // Check if 'userGroups' contains any group from 'allowedGroup1' or 'allowedGroup2' fields + /// // Check if "userGroups" contains any group from "allowedGroup1" or "allowedGroup2" fields /// Field("userGroups").arrayContainsAny([Field("allowedGroup1"), Field("allowedGroup2")]) /// ``` /// - /// - Parameter values: A list of `Expr` elements to check for in the array represented - /// by `self`. - /// - Returns: A new `BooleanExpr` representing the 'array_contains_any' comparison. - func arrayContainsAny(_ values: [Expr]) -> BooleanExpr + /// - Parameter values: A list of `Expression` elements to check for in the array. + /// - Returns: A new `BooleanExpr` representing the "array_contains_any" comparison. + func arrayContainsAny(_ values: [Expression]) -> BooleanExpression /// Creates an expression that checks if an array (from `self`) contains any of the specified /// literal elements. /// Assumes `self` evaluates to an array. /// /// ```swift - /// // Check if 'categories' contains either "electronics" or "books" + /// // Check if "categories" contains either "electronics" or "books" /// Field("categories").arrayContainsAny(["electronics", "books"]) /// ``` /// - /// - Parameter values: A list of `Sendable` literal elements to check for in the array - /// represented by `self`. - /// - Returns: A new `BooleanExpr` representing the 'array_contains_any' comparison. - func arrayContainsAny(_ values: [Sendable]) -> BooleanExpr + /// - Parameter values: An array of at least one `Sendable` element to check for in the array. + /// - Returns: A new `BooleanExpr` representing the "array_contains_any" comparison. + func arrayContainsAny(_ values: [Sendable]) -> BooleanExpression + + /// Creates an expression that checks if an array (from `self`) contains any of the specified + /// element expressions. + /// Assumes `self` evaluates to an array. + /// + /// ```swift + /// // Check if "groups" array contains any of the values from the "userGroup" field + /// Field("groups").arrayContainsAny(Field("userGroup")) + /// ``` + /// + /// - Parameter arrayExpression: An `Expression` elements evaluated to be array. + /// - Returns: A new `BooleanExpr` representing the "array_contains_any" comparison. + func arrayContainsAny(_ arrayExpression: Expression) -> BooleanExpression /// Creates an expression that calculates the length of an array. /// Assumes `self` evaluates to an array. /// /// ```swift - /// // Get the number of items in the 'cart' array + /// // Get the number of items in the "cart" array /// Field("cart").arrayLength() /// ``` /// - /// - Returns: A new `FunctionExpr` representing the length of the array. - func arrayLength() -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the length of the array. + func arrayLength() -> FunctionExpression /// Creates an expression that accesses an element in an array (from `self`) at the specified /// integer offset. @@ -303,15 +326,15 @@ public protocol Expr: Sendable { /// Assumes `self` evaluates to an array. /// /// ```swift - /// // Return the value in the 'tags' field array at index 1. + /// // Return the value in the "tags" field array at index 1. /// Field("tags").arrayGet(1) - /// // Return the last element in the 'tags' field array. + /// // Return the last element in the "tags" field array. /// Field("tags").arrayGet(-1) /// ``` /// /// - Parameter offset: The literal `Int` offset of the element to return. - /// - Returns: A new `FunctionExpr` representing the 'arrayGet' operation. - func arrayGet(_ offset: Int) -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the "arrayGet" operation. + func arrayGet(_ offset: Int) -> FunctionExpression /// Creates an expression that accesses an element in an array (from `self`) at the offset /// specified by an expression. @@ -320,72 +343,178 @@ public protocol Expr: Sendable { /// Assumes `self` evaluates to an array and `offsetExpr` evaluates to an integer. /// /// ```swift - /// // Return the value in the tags field array at index specified by field 'favoriteTagIndex'. + /// // Return the value in the tags field array at index specified by field "favoriteTagIndex". /// Field("tags").arrayGet(Field("favoriteTagIndex")) /// ``` /// - /// - Parameter offsetExpr: An `Expr` (evaluating to an Int) representing the offset of the + /// - Parameter offsetExpr: An `Expression` (evaluating to an Int) representing the offset of the /// element to return. - /// - Returns: A new `FunctionExpr` representing the 'arrayGet' operation. - func arrayGet(_ offsetExpr: Expr) -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the "arrayGet" operation. + func arrayGet(_ offsetExpr: Expression) -> FunctionExpression - // MARK: Equality with Sendable + /// Creates a `BooleanExpr` that returns `true` if this expression is greater + /// than the given expression. + /// + /// - Parameter other: The expression to compare against. + /// - Returns: A `BooleanExpr` that can be used in `where` clauses. + func greaterThan(_ other: Expression) -> BooleanExpression + + /// Creates a `BooleanExpr` that returns `true` if this expression is greater + /// than the given value. + /// + /// - Parameter other: The value to compare against. + /// - Returns: A `BooleanExpr` that can be used in `where` clauses. + func greaterThan(_ other: Sendable) -> BooleanExpression + + /// Creates a `BooleanExpr` that returns `true` if this expression is + /// greater than or equal to the given expression. + /// + /// - Parameter other: The expression to compare against. + /// - Returns: A `BooleanExpr` that can be used in `where` clauses. + func greaterThanOrEqualTo(_ other: Expression) -> BooleanExpression + + /// Creates a `BooleanExpr` that returns `true` if this expression is + /// greater than or equal to the given value. + /// + /// - Parameter other: The value to compare against. + /// - Returns: A `BooleanExpr` that can be used in `where` clauses. + func greaterThanOrEqualTo(_ other: Sendable) -> BooleanExpression + + /// Creates a `BooleanExpr` that returns `true` if this expression is less + /// than the given expression. + /// + /// - Parameter other: The expression to compare against. + /// - Returns: A `BooleanExpr` that can be used in `where` clauses. + func lessThan(_ other: Expression) -> BooleanExpression + + /// Creates a `BooleanExpr` that returns `true` if this expression is less + /// than the given value. + /// + /// - Parameter other: The value to compare against. + /// - Returns: A `BooleanExpr` that can be used in `where` clauses. + func lessThan(_ other: Sendable) -> BooleanExpression + + /// Creates a `BooleanExpr` that returns `true` if this expression is less + /// than or equal to the given expression. + /// + /// - Parameter other: The expression to compare against. + /// - Returns: A `BooleanExpr` that can be used in `where` clauses. + func lessThanOrEqualTo(_ other: Expression) -> BooleanExpression + + /// Creates a `BooleanExpr` that returns `true` if this expression is less + /// than or equal to the given value. + /// + /// - Parameter other: The value to compare against. + /// - Returns: A `BooleanExpr` that can be used in `where` clauses. + func lessThanOrEqualTo(_ other: Sendable) -> BooleanExpression + + /// Creates a `BooleanExpr` that returns `true` if this expression is equal + /// to the given expression. + /// + /// - Parameter other: The expression to compare against. + /// - Returns: A `BooleanExpr` that can be used in `where` clauses. + func equal(_ other: Expression) -> BooleanExpression + + /// Creates a `BooleanExpr` that returns `true` if this expression is equal + /// to the given value. + /// + /// - Parameter other: The value to compare against. + /// - Returns: A `BooleanExpr` that can be used in `where` clauses. + func equal(_ other: Sendable) -> BooleanExpression + + /// Creates a `BooleanExpr` that returns `true` if this expression is not + /// equal to the given expression. + /// + /// - Parameter other: The expression to compare against. + /// - Returns: A `BooleanExpr` that can be used in `where` clauses. + func notEqual(_ other: Expression) -> BooleanExpression + + /// Creates a `BooleanExpr` that returns `true` if this expression is not + /// equal to the given value. + /// + /// - Parameter other: The value to compare against. + /// - Returns: A `BooleanExpr` that can be used in `where` clauses. + func notEqual(_ other: Sendable) -> BooleanExpression /// Creates an expression that checks if this expression is equal to any of the provided /// expression values. /// This is similar to an "IN" operator in SQL. /// /// ```swift - /// // Check if 'categoryID' field is equal to 'featuredCategory' or 'popularCategory' fields - /// Field("categoryID").eqAny([Field("featuredCategory"), Field("popularCategory")]) + /// // Check if "categoryID" field is equal to "featuredCategory" or "popularCategory" fields + /// Field("categoryID").equalAny([Field("featuredCategory"), Field("popularCategory")]) /// ``` /// - /// - Parameter others: A list of `Expr` values to check against. - /// - Returns: A new `BooleanExpr` representing the 'IN' comparison (eq_any). - func eqAny(_ others: [Expr]) -> BooleanExpr + /// - Parameter others: An array of at least one `Expression` value to check against. + /// - Returns: A new `BooleanExpr` representing the "IN" comparison (eq_any). + func equalAny(_ others: [Expression]) -> BooleanExpression /// Creates an expression that checks if this expression is equal to any of the provided literal /// values. /// This is similar to an "IN" operator in SQL. /// /// ```swift - /// // Check if 'category' is "Electronics", "Books", or "Home Goods" - /// Field("category").eqAny(["Electronics", "Books", "Home Goods"]) + /// // Check if "category" is "Electronics", "Books", or "Home Goods" + /// Field("category").equalAny(["Electronics", "Books", "Home Goods"]) /// ``` /// - /// - Parameter others: A list of `Sendable` literal values to check against. - /// - Returns: A new `BooleanExpr` representing the 'IN' comparison (eq_any). - func eqAny(_ others: [Sendable]) -> BooleanExpr + /// - Parameter others: An array of at least one `Sendable` literal value to check against. + /// - Returns: A new `BooleanExpr` representing the "IN" comparison (eq_any). + func equalAny(_ others: [Sendable]) -> BooleanExpression + + /// Creates an expression that checks if this expression is equal to any of the provided + /// expression values. + /// This is similar to an "IN" operator in SQL. + /// + /// ```swift + /// // Check if "categoryID" field is equal to any of "categoryIDs" fields + /// Field("categoryID").equalAny(Field("categoryIDs")) + /// ``` + /// + /// - Parameter arrayExpression: An `Expression` elements evaluated to be array. + /// - Returns: A new `BooleanExpr` representing the "IN" comparison (eq_any). + func equalAny(_ arrayExpression: Expression) -> BooleanExpression /// Creates an expression that checks if this expression is not equal to any of the provided /// expression values. /// This is similar to a "NOT IN" operator in SQL. /// /// ```swift - /// // Check if 'statusValue' is not equal to 'archivedStatus' or 'deletedStatus' fields - /// Field("statusValue").notEqAny([Field("archivedStatus"), Field("deletedStatus")]) + /// // Check if "statusValue" is not equal to "archivedStatus" or "deletedStatus" fields + /// Field("statusValue").notEqualAny([Field("archivedStatus"), Field("deletedStatus")]) /// ``` /// - /// - Parameter others: A list of `Expr` values to check against. - /// - Returns: A new `BooleanExpr` representing the 'NOT IN' comparison (not_eq_any). - func notEqAny(_ others: [Expr]) -> BooleanExpr + /// - Parameter others: An array of at least one `Expression` value to check against. + /// - Returns: A new `BooleanExpr` representing the "NOT IN" comparison (not_eq_any). + func notEqualAny(_ others: [Expression]) -> BooleanExpression /// Creates an expression that checks if this expression is not equal to any of the provided /// literal values. /// This is similar to a "NOT IN" operator in SQL. /// /// ```swift - /// // Check if 'status' is neither "pending" nor "archived" - /// Field("status").notEqAny(["pending", "archived"]) + /// // Check if "status" is neither "pending" nor "archived" + /// Field("status").notEqualAny(["pending", "archived"]) /// ``` /// - /// - Parameter others: A list of `Sendable` literal values to check against. - /// - Returns: A new `BooleanExpr` representing the 'NOT IN' comparison (not_eq_any). - func notEqAny(_ others: [Sendable]) -> BooleanExpr + /// - Parameter others: An array of at least one `Sendable` literal value to check against. + /// - Returns: A new `BooleanExpr` representing the "NOT IN" comparison (not_eq_any). + func notEqualAny(_ others: [Sendable]) -> BooleanExpression - // MARK: Checks + /// Creates an expression that checks if this expression is equal to any of the provided + /// expression values. + /// This is similar to an "IN" operator in SQL. + /// + /// ```swift + /// // Check if "categoryID" field is not equal to any of "categoryIDs" fields + /// Field("categoryID").equalAny(Field("categoryIDs")) + /// ``` + /// + /// - Parameter arrayExpression: An `Expression` elements evaluated to be array. + /// - Returns: A new `BooleanExpr` representing the "IN" comparison (eq_any). + func notEqualAny(_ arrayExpression: Expression) -> BooleanExpression - /// Creates an expression that checks if this expression evaluates to 'NaN' (Not a Number). + /// Creates an expression that checks if this expression evaluates to "NaN" (Not a Number). /// Assumes `self` evaluates to a numeric type. /// /// ```swift @@ -393,18 +522,18 @@ public protocol Expr: Sendable { /// Field("value").divide(0).isNan() /// ``` /// - /// - Returns: A new `BooleanExpr` representing the 'isNaN' check. - func isNan() -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "isNaN" check. + func isNan() -> BooleanExpression - /// Creates an expression that checks if this expression evaluates to 'Null'. + /// Creates an expression that checks if this expression evaluates to "Nil". /// /// ```swift - /// // Check if the 'optionalField' is null - /// Field("optionalField").isNull() + /// // Check if the "optionalField" is null + /// Field("optionalField").isNil() /// ``` /// - /// - Returns: A new `BooleanExpr` representing the 'isNull' check. - func isNull() -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "isNil" check. + func isNil() -> BooleanExpression /// Creates an expression that checks if a field exists in the document. /// @@ -415,8 +544,8 @@ public protocol Expr: Sendable { /// Field("phoneNumber").exists() /// ``` /// - /// - Returns: A new `BooleanExpr` representing the 'exists' check. - func exists() -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "exists" check. + func exists() -> BooleanExpression /// Creates an expression that checks if this expression produces an error during evaluation. /// @@ -427,8 +556,8 @@ public protocol Expr: Sendable { /// Field("myArray").arrayGet(100).isError() /// ``` /// - /// - Returns: A new `BooleanExpr` representing the 'isError' check. - func isError() -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "isError" check. + func isError() -> BooleanExpression /// Creates an expression that returns `true` if the result of this expression /// is absent (e.g., a field does not exist in a map). Otherwise, returns `false`, even if the @@ -442,20 +571,20 @@ public protocol Expr: Sendable { /// Field("value").isAbsent() /// ``` /// - /// - Returns: A new `BooleanExpr` representing the 'isAbsent' check. - func isAbsent() -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "isAbsent" check. + func isAbsent() -> BooleanExpression /// Creates an expression that checks if the result of this expression is not null. /// /// ```swift - /// // Check if the value of the 'name' field is not null - /// Field("name").isNotNull() + /// // Check if the value of the "name" field is not null + /// Field("name").isNotNil() /// ``` /// - /// - Returns: A new `BooleanExpr` representing the 'isNotNull' check. - func isNotNull() -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "isNotNil" check. + func isNotNil() -> BooleanExpression - /// Creates an expression that checks if the results of this expression is NOT 'NaN' (Not a + /// Creates an expression that checks if the results of this expression is NOT "NaN" (Not a /// Number). /// Assumes `self` evaluates to a numeric type. /// @@ -464,8 +593,8 @@ public protocol Expr: Sendable { /// Field("value").divide(Field("count")).isNotNan() // Assuming count might be 0 /// ``` /// - /// - Returns: A new `BooleanExpr` representing the 'isNotNaN' check. - func isNotNan() -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "isNotNaN" check. + func isNotNan() -> BooleanExpression // MARK: String Operations @@ -473,263 +602,257 @@ public protocol Expr: Sendable { /// Assumes `self` evaluates to a string. /// /// ```swift - /// // Get the character length of the 'name' field in its UTF-8 form. + /// // Get the character length of the "name" field in its UTF-8 form. /// Field("name").charLength() /// ``` /// - /// - Returns: A new `FunctionExpr` representing the length of the string. - func charLength() -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the length of the string. + func charLength() -> FunctionExpression /// Creates an expression that performs a case-sensitive string comparison using wildcards against /// a literal pattern. /// Assumes `self` evaluates to a string. /// /// ```swift - /// // Check if the 'title' field contains the word "guide" (case-sensitive) + /// // Check if the "title" field contains the word "guide" (case-sensitive) /// Field("title").like("%guide%") /// ``` /// /// - Parameter pattern: The literal string pattern to search for. Use "%" as a wildcard. - /// - Returns: A new `FunctionExpr` representing the 'like' comparison. - func like(_ pattern: String) -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "like" comparison. + func like(_ pattern: String) -> BooleanExpression /// Creates an expression that performs a case-sensitive string comparison using wildcards against /// an expression pattern. /// Assumes `self` evaluates to a string, and `pattern` evaluates to a string. /// /// ```swift - /// // Check if 'filename' matches a pattern stored in 'patternField' + /// // Check if "filename" matches a pattern stored in "patternField" /// Field("filename").like(Field("patternField")) /// ``` /// - /// - Parameter pattern: An `Expr` (evaluating to a string) representing the pattern to search + /// - Parameter pattern: An `Expression` (evaluating to a string) representing the pattern to + /// search /// for. - /// - Returns: A new `FunctionExpr` representing the 'like' comparison. - func like(_ pattern: Expr) -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "like" comparison. + func like(_ pattern: Expression) -> BooleanExpression /// Creates an expression that checks if a string (from `self`) contains a specified regular /// expression literal as a substring. /// Uses RE2 syntax. Assumes `self` evaluates to a string. /// /// ```swift - /// // Check if 'description' contains "example" (case-insensitive) + /// // Check if "description" contains "example" (case-insensitive) /// Field("description").regexContains("(?i)example") /// ``` /// /// - Parameter pattern: The literal string regular expression to use for the search. - /// - Returns: A new `BooleanExpr` representing the 'regex_contains' comparison. - func regexContains(_ pattern: String) -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "regex_contains" comparison. + func regexContains(_ pattern: String) -> BooleanExpression /// Creates an expression that checks if a string (from `self`) contains a specified regular /// expression (from an expression) as a substring. /// Uses RE2 syntax. Assumes `self` evaluates to a string, and `pattern` evaluates to a string. /// /// ```swift - /// // Check if 'logEntry' contains a pattern from 'errorPattern' field + /// // Check if "logEntry" contains a pattern from "errorPattern" field /// Field("logEntry").regexContains(Field("errorPattern")) /// ``` /// - /// - Parameter pattern: An `Expr` (evaluating to a string) representing the regular expression to + /// - Parameter pattern: An `Expression` (evaluating to a string) representing the regular + /// expression to /// use for the search. - /// - Returns: A new `BooleanExpr` representing the 'regex_contains' comparison. - func regexContains(_ pattern: Expr) -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "regex_contains" comparison. + func regexContains(_ pattern: Expression) -> BooleanExpression /// Creates an expression that checks if a string (from `self`) matches a specified regular /// expression literal entirely. /// Uses RE2 syntax. Assumes `self` evaluates to a string. /// /// ```swift - /// // Check if the 'email' field matches a valid email pattern - /// Field("email").regexMatch("[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}") + /// // Check if the "email" field matches a valid email pattern + /// Field("email").regexMatch("[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}") /// ``` /// /// - Parameter pattern: The literal string regular expression to use for the match. /// - Returns: A new `BooleanExpr` representing the regular expression match. - func regexMatch(_ pattern: String) -> BooleanExpr + func regexMatch(_ pattern: String) -> BooleanExpression /// Creates an expression that checks if a string (from `self`) matches a specified regular /// expression (from an expression) entirely. /// Uses RE2 syntax. Assumes `self` evaluates to a string, and `pattern` evaluates to a string. /// /// ```swift - /// // Check if 'input' matches the regex stored in 'validationRegex' + /// // Check if "input" matches the regex stored in "validationRegex" /// Field("input").regexMatch(Field("validationRegex")) /// ``` /// - /// - Parameter pattern: An `Expr` (evaluating to a string) representing the regular expression to + /// - Parameter pattern: An `Expression` (evaluating to a string) representing the regular + /// expression to /// use for the match. /// - Returns: A new `BooleanExpr` representing the regular expression match. - func regexMatch(_ pattern: Expr) -> BooleanExpr + func regexMatch(_ pattern: Expression) -> BooleanExpression /// Creates an expression that checks if a string (from `self`) contains a specified literal /// substring (case-sensitive). /// Assumes `self` evaluates to a string. /// /// ```swift - /// // Check if the 'description' field contains "example". + /// // Check if the "description" field contains "example". /// Field("description").strContains("example") /// ``` /// /// - Parameter substring: The literal string substring to search for. - /// - Returns: A new `BooleanExpr` representing the 'str_contains' comparison. - func strContains(_ substring: String) -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "str_contains" comparison. + func strContains(_ substring: String) -> BooleanExpression /// Creates an expression that checks if a string (from `self`) contains a specified substring /// from an expression (case-sensitive). /// Assumes `self` evaluates to a string, and `expr` evaluates to a string. /// /// ```swift - /// // Check if the 'message' field contains the value of the 'keyword' field. + /// // Check if the "message" field contains the value of the "keyword" field. /// Field("message").strContains(Field("keyword")) /// ``` /// - /// - Parameter expr: An `Expr` (evaluating to a string) representing the substring to search for. - /// - Returns: A new `BooleanExpr` representing the 'str_contains' comparison. - func strContains(_ expr: Expr) -> BooleanExpr + /// - Parameter expr: An `Expression` (evaluating to a string) representing the substring to + /// search for. + /// - Returns: A new `BooleanExpr` representing the "str_contains" comparison. + func strContains(_ expr: Expression) -> BooleanExpression /// Creates an expression that checks if a string (from `self`) starts with a given literal prefix /// (case-sensitive). /// Assumes `self` evaluates to a string. /// /// ```swift - /// // Check if the 'name' field starts with "Mr." + /// // Check if the "name" field starts with "Mr." /// Field("name").startsWith("Mr.") /// ``` /// /// - Parameter prefix: The literal string prefix to check for. - /// - Returns: A new `BooleanExpr` representing the 'starts_with' comparison. - func startsWith(_ prefix: String) -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "starts_with" comparison. + func startsWith(_ prefix: String) -> BooleanExpression /// Creates an expression that checks if a string (from `self`) starts with a given prefix from an /// expression (case-sensitive). /// Assumes `self` evaluates to a string, and `prefix` evaluates to a string. /// /// ```swift - /// // Check if 'fullName' starts with the value of 'firstName' + /// // Check if "fullName" starts with the value of "firstName" /// Field("fullName").startsWith(Field("firstName")) /// ``` /// - /// - Parameter prefix: An `Expr` (evaluating to a string) representing the prefix to check for. - /// - Returns: A new `BooleanExpr` representing the 'starts_with' comparison. - func startsWith(_ prefix: Expr) -> BooleanExpr + /// - Parameter prefix: An `Expression` (evaluating to a string) representing the prefix to check + /// for. + /// - Returns: A new `BooleanExpr` representing the "starts_with" comparison. + func startsWith(_ prefix: Expression) -> BooleanExpression /// Creates an expression that checks if a string (from `self`) ends with a given literal suffix /// (case-sensitive). /// Assumes `self` evaluates to a string. /// /// ```swift - /// // Check if the 'filename' field ends with ".txt" + /// // Check if the "filename" field ends with ".txt" /// Field("filename").endsWith(".txt") /// ``` /// /// - Parameter suffix: The literal string suffix to check for. - /// - Returns: A new `BooleanExpr` representing the 'ends_with' comparison. - func endsWith(_ suffix: String) -> BooleanExpr + /// - Returns: A new `BooleanExpr` representing the "ends_with" comparison. + func endsWith(_ suffix: String) -> BooleanExpression /// Creates an expression that checks if a string (from `self`) ends with a given suffix from an /// expression (case-sensitive). /// Assumes `self` evaluates to a string, and `suffix` evaluates to a string. /// /// ```swift - /// // Check if 'url' ends with the value of 'extension' field + /// // Check if "url" ends with the value of "extension" field /// Field("url").endsWith(Field("extension")) /// ``` /// - /// - Parameter suffix: An `Expr` (evaluating to a string) representing the suffix to check for. - /// - Returns: A new `BooleanExpr` representing the 'ends_with' comparison. - func endsWith(_ suffix: Expr) -> BooleanExpr + /// - Parameter suffix: An `Expression` (evaluating to a string) representing the suffix to check + /// for. + /// - Returns: A new `BooleanExpr` representing the "ends_with" comparison. + func endsWith(_ suffix: Expression) -> BooleanExpression /// Creates an expression that converts a string (from `self`) to lowercase. /// Assumes `self` evaluates to a string. /// /// ```swift - /// // Convert the 'name' field to lowercase + /// // Convert the "name" field to lowercase /// Field("name").lowercased() /// ``` /// - /// - Returns: A new `FunctionExpr` representing the lowercase string. - func lowercased() -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the lowercase string. + func lowercased() -> FunctionExpression /// Creates an expression that converts a string (from `self`) to uppercase. /// Assumes `self` evaluates to a string. /// /// ```swift - /// // Convert the 'title' field to uppercase + /// // Convert the "title" field to uppercase3 /// Field("title").uppercased() /// ``` /// - /// - Returns: A new `FunctionExpr` representing the uppercase string. - func uppercased() -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the uppercase string. + func uppercased() -> FunctionExpression /// Creates an expression that removes leading and trailing whitespace from a string (from /// `self`). /// Assumes `self` evaluates to a string. /// /// ```swift - /// // Trim whitespace from the 'userInput' field + /// // Trim whitespace from the "userInput" field /// Field("userInput").trim() /// ``` /// - /// - Returns: A new `FunctionExpr` representing the trimmed string. - func trim() -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the trimmed string. + func trim() -> FunctionExpression /// Creates an expression that concatenates this string expression with other string expressions. - /// Assumes `self` evaluates to a string. + /// Assumes `self` and all parameters evaluate to strings. /// /// ```swift - /// // Combine 'part1', 'part2', and 'part3' fields - /// Field("part1").strConcat(Field("part2"), Field("part3")) + /// // Combine "firstName", "middleName", and "lastName" fields + /// Field("firstName").strConcat(Field("middleName"), Field("lastName")) /// ``` /// - /// - Parameter secondString: An `Expr` (evaluating to a string) to concatenate. - /// - Parameter otherStrings: Optional additional `Expr` (evaluating to strings) to concatenate. - /// - Returns: A new `FunctionExpr` representing the concatenated string. - func strConcat(_ secondString: Expr, _ otherStrings: Expr...) -> FunctionExpr - - /// Creates an expression that concatenates this string expression with other string literals. - /// Assumes `self` evaluates to a string. - /// - /// ```swift - /// // Combine 'firstName', " ", and 'lastName' - /// Field("firstName").strConcat(" ", "lastName") - /// ``` - /// - /// - Parameter secondString: A string literal to concatenate. - /// - Parameter otherStrings: Optional additional string literals to concatenate. - /// - Returns: A new `FunctionExpr` representing the concatenated string. - func strConcat(_ secondString: String, _ otherStrings: String...) -> FunctionExpr + /// - Parameter secondString: An `Expression` (evaluating to a string) to concatenate. + /// - Parameter otherStrings: Optional additional `Expression` (evaluating to strings) to + /// concatenate. + /// - Returns: A new `FunctionExpression` representing the concatenated string. + func strConcat(_ strings: [Expression]) -> FunctionExpression /// Creates an expression that reverses this string expression. /// Assumes `self` evaluates to a string. /// /// ```swift - /// // Reverse the value of the 'myString' field. + /// // Reverse the value of the "myString" field. /// Field("myString").reverse() /// ``` /// /// - Returns: A new `FunctionExpr` representing the reversed string. - func reverse() -> FunctionExpr + func reverse() -> FunctionExpression /// Creates an expression that replaces the first occurrence of a literal substring within this /// string expression with another literal substring. /// Assumes `self` evaluates to a string. /// /// ```swift - /// // Replace the first "hello" with "hi" in the 'message' field + /// // Replace the first "hello" with "hi" in the "message" field /// Field("message").replaceFirst("hello", "hi") /// ``` /// /// - Parameter find: The literal string substring to search for. /// - Parameter replace: The literal string substring to replace the first occurrence with. /// - Returns: A new `FunctionExpr` representing the string with the first occurrence replaced. - func replaceFirst(_ find: String, _ replace: String) -> FunctionExpr + func replaceFirst(_ find: String, with replace: String) -> FunctionExpression /// Creates an expression that replaces the first occurrence of a substring (from an expression) /// within this string expression with another substring (from an expression). /// Assumes `self` evaluates to a string, and `find`/`replace` evaluate to strings. /// /// ```swift - /// // Replace first occurrence of field 'findPattern' with field 'replacePattern' in 'text' + /// // Replace first occurrence of field "findPattern" with field "replacePattern" in "text" /// Field("text").replaceFirst(Field("findPattern"), Field("replacePattern")) /// ``` /// @@ -737,28 +860,28 @@ public protocol Expr: Sendable { /// - Parameter replace: An `Expr` (evaluating to a string) for the substring to replace the first /// occurrence with. /// - Returns: A new `FunctionExpr` representing the string with the first occurrence replaced. - func replaceFirst(_ find: Expr, _ replace: Expr) -> FunctionExpr + func replaceFirst(_ find: Expression, with replace: Expression) -> FunctionExpression /// Creates an expression that replaces all occurrences of a literal substring within this string /// expression with another literal substring. /// Assumes `self` evaluates to a string. /// /// ```swift - /// // Replace all occurrences of " " with "_" in 'description' + /// // Replace all occurrences of " " with "_" in "description" /// Field("description").replaceAll(" ", "_") /// ``` /// /// - Parameter find: The literal string substring to search for. /// - Parameter replace: The literal string substring to replace all occurrences with. /// - Returns: A new `FunctionExpr` representing the string with all occurrences replaced. - func replaceAll(_ find: String, _ replace: String) -> FunctionExpr + func replaceAll(_ find: String, with replace: String) -> FunctionExpression /// Creates an expression that replaces all occurrences of a substring (from an expression) within /// this string expression with another substring (from an expression). /// Assumes `self` evaluates to a string, and `find`/`replace` evaluate to strings. /// /// ```swift - /// // Replace all occurrences of field 'target' with field 'replacement' in 'content' + /// // Replace all occurrences of field "target" with field "replacement" in "content" /// Field("content").replaceAll(Field("target"), Field("replacement")) /// ``` /// @@ -766,21 +889,21 @@ public protocol Expr: Sendable { /// - Parameter replace: An `Expr` (evaluating to a string) for the substring to replace all /// occurrences with. /// - Returns: A new `FunctionExpr` representing the string with all occurrences replaced. - func replaceAll(_ find: Expr, _ replace: Expr) -> FunctionExpr + func replaceAll(_ find: Expression, with replace: Expression) -> FunctionExpression /// Creates an expression that calculates the length of this string or bytes expression in bytes. /// Assumes `self` evaluates to a string or bytes. /// /// ```swift - /// // Calculate the length of the 'myString' field in bytes. + /// // Calculate the length of the "myString" field in bytes. /// Field("myString").byteLength() /// - /// // Calculate the size of the 'avatar' (Data/Bytes) field. + /// // Calculate the size of the "avatar" (Data/Bytes) field. /// Field("avatar").byteLength() /// ``` /// /// - Returns: A new `FunctionExpr` representing the length in bytes. - func byteLength() -> FunctionExpr + func byteLength() -> FunctionExpression /// Creates an expression that returns a substring of this expression (String or Bytes) using /// literal integers for position and optional length. @@ -792,14 +915,14 @@ public protocol Expr: Sendable { /// // Get substring from index 5 with length 10 /// Field("myString").substr(5, 10) /// - /// // Get substring from 'myString' starting at index 3 to the end + /// // Get substring from "myString" starting at index 3 to the end /// Field("myString").substr(3, nil) /// ``` /// /// - Parameter position: Literal `Int` index of the first character/byte. /// - Parameter length: Optional literal `Int` length of the substring. If `nil`, goes to the end. /// - Returns: A new `FunctionExpr` representing the substring. - func substr(_ position: Int, _ length: Int?) -> FunctionExpr + func substr(position: Int, length: Int?) -> FunctionExpression /// Creates an expression that returns a substring of this expression (String or Bytes) using /// expressions for position and optional length. @@ -821,7 +944,7 @@ public protocol Expr: Sendable { /// - Parameter length: Optional `Expr` (evaluating to an Int) for the length of the substring. If /// `nil`, goes to the end. /// - Returns: A new `FunctionExpr` representing the substring. - func substr(_ position: Expr, _ length: Expr?) -> FunctionExpr + func substr(position: Expression, length: Expression?) -> FunctionExpression // MARK: Map Operations @@ -829,13 +952,13 @@ public protocol Expr: Sendable { /// Assumes `self` evaluates to a Map. /// /// ```swift - /// // Get the 'city' value from the 'address' map field + /// // Get the "city" value from the "address" map field /// Field("address").mapGet("city") /// ``` /// /// - Parameter subfield: The literal string key to access in the map. /// - Returns: A new `FunctionExpr` representing the value associated with the given key. - func mapGet(_ subfield: String) -> FunctionExpr + func mapGet(_ subfield: String) -> FunctionExpression /// Creates an expression that removes a key (specified by a literal string) from the map produced /// by evaluating this expression. @@ -844,13 +967,13 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Removes the key 'baz' from the map held in field 'myMap' + /// // Removes the key "baz" from the map held in field "myMap" /// Field("myMap").mapRemove("baz") /// ``` /// /// - Parameter key: The literal string key to remove from the map. - /// - Returns: A new `FunctionExpr` representing the 'map_remove' operation. - func mapRemove(_ key: String) -> FunctionExpr + /// - Returns: A new `FunctionExpr` representing the "map_remove" operation. + func mapRemove(_ key: String) -> FunctionExpression /// Creates an expression that removes a key (specified by an expression) from the map produced by /// evaluating this expression. @@ -859,14 +982,14 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Removes the key specified by field 'keyToRemove' from the map in 'settings' + /// // Removes the key specified by field "keyToRemove" from the map in "settings" /// Field("settings").mapRemove(Field("keyToRemove")) /// ``` /// /// - Parameter keyExpr: An `Expr` (evaluating to a string) representing the key to remove from /// the map. - /// - Returns: A new `FunctionExpr` representing the 'map_remove' operation. - func mapRemove(_ keyExpr: Expr) -> FunctionExpr + /// - Returns: A new `FunctionExpr` representing the "map_remove" operation. + func mapRemove(_ keyExpr: Expression) -> FunctionExpression /// Creates an expression that merges this map with multiple other map literals. /// Assumes `self` evaluates to a Map. Later maps overwrite keys from earlier maps. @@ -874,16 +997,15 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Merge 'settings' field with { "enabled": true } and another map literal { "priority": 1 } + /// // Merge "settings" field with { "enabled": true } and another map literal { "priority": 1 } /// Field("settings").mapMerge(["enabled": true], ["priority": 1]) /// ``` /// - /// - Parameter secondMap: A required second map (dictionary literal with `Sendable` values) to - /// merge. - /// - Parameter otherMaps: Optional additional maps (dictionary literals with `Sendable` values) + /// - Parameter maps: Maps (dictionary literals with `Sendable` values) /// to merge. - /// - Returns: A new `FunctionExpr` representing the 'map_merge' operation. - func mapMerge(_ secondMap: [String: Sendable], _ otherMaps: [String: Sendable]...) -> FunctionExpr + /// - Returns: A new `FunctionExpr` representing the "map_merge" operation. + func mapMerge(_ maps: [[String: Sendable]]) + -> FunctionExpression /// Creates an expression that merges this map with multiple other map expressions. /// Assumes `self` and other arguments evaluate to Maps. Later maps overwrite keys from earlier @@ -892,14 +1014,13 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Merge 'baseSettings' field with 'userOverrides' field and 'adminConfig' field + /// // Merge "baseSettings" field with "userOverrides" field and "adminConfig" field /// Field("baseSettings").mapMerge(Field("userOverrides"), Field("adminConfig")) /// ``` /// - /// - Parameter secondMap: A required second `Expr` (evaluating to a Map) to merge. - /// - Parameter otherMaps: Optional additional `Expr` (evaluating to Maps) to merge. - /// - Returns: A new `FunctionExpr` representing the 'map_merge' operation. - func mapMerge(_ secondMap: Expr, _ otherMaps: Expr...) -> FunctionExpr + /// - Parameter maps: Additional `Expression` (evaluating to Maps) to merge. + /// - Returns: A new `FunctionExpr` representing the "map_merge" operation. + func mapMerge(_ maps: [Expression]) -> FunctionExpression // MARK: Aggregations @@ -907,11 +1028,11 @@ public protocol Expr: Sendable { /// to a valid, non-null value. /// /// ```swift - /// // Count the total number of products with a 'productId' + /// // Count the total number of products with a "productId" /// Field("productId").count().alias("totalProducts") /// ``` /// - /// - Returns: A new `AggregateFunction` representing the 'count' aggregation on this expression. + /// - Returns: A new `AggregateFunction` representing the "count" aggregation on this expression. func count() -> AggregateFunction /// Creates an aggregation that calculates the sum of this numeric expression across multiple @@ -923,7 +1044,7 @@ public protocol Expr: Sendable { /// Field("orderAmount").sum().alias("totalRevenue") /// ``` /// - /// - Returns: A new `AggregateFunction` representing the 'sum' aggregation. + /// - Returns: A new `AggregateFunction` representing the "sum" aggregation. func sum() -> AggregateFunction /// Creates an aggregation that calculates the average (mean) of this numeric expression across @@ -932,11 +1053,11 @@ public protocol Expr: Sendable { /// /// ```swift /// // Calculate the average age of users - /// Field("age").avg().alias("averageAge") + /// Field("age").average().as("averageAge") /// ``` /// - /// - Returns: A new `AggregateFunction` representing the 'avg' aggregation. - func avg() -> AggregateFunction + /// - Returns: A new `AggregateFunction` representing the "average" aggregation. + func average() -> AggregateFunction /// Creates an aggregation that finds the minimum value of this expression across multiple stage /// inputs. @@ -946,7 +1067,7 @@ public protocol Expr: Sendable { /// Field("price").minimum().alias("lowestPrice") /// ``` /// - /// - Returns: A new `AggregateFunction` representing the 'min' aggregation. + /// - Returns: A new `AggregateFunction` representing the "min" aggregation. func minimum() -> AggregateFunction /// Creates an aggregation that finds the maximum value of this expression across multiple stage @@ -957,62 +1078,58 @@ public protocol Expr: Sendable { /// Field("score").maximum().alias("highestScore") /// ``` /// - /// - Returns: A new `AggregateFunction` representing the 'max' aggregation. + /// - Returns: A new `AggregateFunction` representing the "max" aggregation. func maximum() -> AggregateFunction // MARK: Logical min/max /// Creates an expression that returns the larger value between this expression and other - /// expressions, based on Firestore's value type ordering. + /// expressions, based on Firestore"s value type ordering. /// /// ```swift - /// // Returns the largest of 'val1', 'val2', and 'val3' fields + /// // Returns the largest of "val1", "val2", and "val3" fields /// Field("val1").logicalMaximum(Field("val2"), Field("val3")) /// ``` /// - /// - Parameter second: The second `Expr` to compare with. - /// - Parameter others: Optional additional `Expr` values to compare with. - /// - Returns: A new `FunctionExpr` representing the logical max operation. - func logicalMaximum(_ second: Expr, _ others: Expr...) -> FunctionExpr + /// - Parameter expressions: An array of at least one `Expression` to compare with. + /// - Returns: A new `FunctionExpression` representing the logical max operation. + func logicalMaximum(_ expressions: [Expression]) -> FunctionExpression /// Creates an expression that returns the larger value between this expression and other literal - /// values, based on Firestore's value type ordering. + /// values, based on Firestore"s value type ordering. /// /// ```swift - /// // Returns the largest of 'val1' (a field), 100, and 200.0 + /// // Returns the largest of "val1" (a field), 100, and 200.0 /// Field("val1").logicalMaximum(100, 200.0) /// ``` /// - /// - Parameter second: The second literal `Sendable` value to compare with. - /// - Parameter others: Optional additional literal `Sendable` values to compare with. - /// - Returns: A new `FunctionExpr` representing the logical max operation. - func logicalMaximum(_ second: Sendable, _ others: Sendable...) -> FunctionExpr + /// - Parameter values: An array of at least one `Sendable` value to compare with. + /// - Returns: A new `FunctionExpression` representing the logical max operation. + func logicalMaximum(_ values: [Sendable]) -> FunctionExpression /// Creates an expression that returns the smaller value between this expression and other - /// expressions, based on Firestore's value type ordering. + /// expressions, based on Firestore"s value type ordering. /// /// ```swift - /// // Returns the smallest of 'val1', 'val2', and 'val3' fields + /// // Returns the smallest of "val1", "val2", and "val3" fields /// Field("val1").logicalMinimum(Field("val2"), Field("val3")) /// ``` /// - /// - Parameter second: The second `Expr` to compare with. - /// - Parameter others: Optional additional `Expr` values to compare with. - /// - Returns: A new `FunctionExpr` representing the logical min operation. - func logicalMinimum(_ second: Expr, _ others: Expr...) -> FunctionExpr + /// - Parameter expressions: An array of at least one `Expression` to compare with. + /// - Returns: A new `FunctionExpression` representing the logical min operation. + func logicalMinimum(_ expressions: [Expression]) -> FunctionExpression /// Creates an expression that returns the smaller value between this expression and other literal - /// values, based on Firestore's value type ordering. + /// values, based on Firestore"s value type ordering. /// /// ```swift - /// // Returns the smallest of 'val1' (a field), 0, and -5.5 + /// // Returns the smallest of "val1" (a field), 0, and -5.5 /// Field("val1").logicalMinimum(0, -5.5) /// ``` /// - /// - Parameter second: The second literal `Sendable` value to compare with. - /// - Parameter others: Optional additional literal `Sendable` values to compare with. - /// - Returns: A new `FunctionExpr` representing the logical min operation. - func logicalMinimum(_ second: Sendable, _ others: Sendable...) -> FunctionExpr + /// - Parameter values: An array of at least one `Sendable` value to compare with. + /// - Returns: A new `FunctionExpression` representing the logical min operation. + func logicalMinimum(_ values: [Sendable]) -> FunctionExpression // MARK: Vector Operations @@ -1021,24 +1138,24 @@ public protocol Expr: Sendable { /// Assumes `self` evaluates to a Vector. /// /// ```swift - /// // Get the vector length (dimension) of the field 'embedding'. + /// // Get the vector length (dimension) of the field "embedding". /// Field("embedding").vectorLength() /// ``` /// - /// - Returns: A new `FunctionExpr` representing the length of the vector. - func vectorLength() -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the length of the vector. + func vectorLength() -> FunctionExpression /// Calculates the cosine distance between this vector expression and another vector expression. /// Assumes both `self` and `other` evaluate to Vectors. /// /// ```swift - /// // Cosine distance between 'userVector' field and 'itemVector' field + /// // Cosine distance between "userVector" field and "itemVector" field /// Field("userVector").cosineDistance(Field("itemVector")) /// ``` /// - /// - Parameter other: The other vector as an `Expr` to compare against. - /// - Returns: A new `FunctionExpr` representing the cosine distance. - func cosineDistance(_ other: Expr) -> FunctionExpr + /// - Parameter expression: The other vector as an `Expr` to compare against. + /// - Returns: A new `FunctionExpression` representing the cosine distance. + func cosineDistance(_ expression: Expression) -> FunctionExpression /// Calculates the cosine distance between this vector expression and another vector literal /// (`VectorValue`). @@ -1049,33 +1166,33 @@ public protocol Expr: Sendable { /// let targetVector = VectorValue(vector: [0.1, 0.2, 0.3]) /// Field("docVector").cosineDistance(targetVector) /// ``` - /// - Parameter other: The other vector as a `VectorValue` to compare against. - /// - Returns: A new `FunctionExpr` representing the cosine distance. - func cosineDistance(_ other: VectorValue) -> FunctionExpr + /// - Parameter vector: The other vector as a `VectorValue` to compare against. + /// - Returns: A new `FunctionExpression` representing the cosine distance. + func cosineDistance(_ vector: VectorValue) -> FunctionExpression /// Calculates the cosine distance between this vector expression and another vector literal /// (`[Double]`). /// Assumes `self` evaluates to a Vector. /// /// ```swift - /// // Cosine distance between 'location' field and a target location + /// // Cosine distance between "location" field and a target location /// Field("location").cosineDistance([37.7749, -122.4194]) /// ``` - /// - Parameter other: The other vector as `[Double]` to compare against. - /// - Returns: A new `FunctionExpr` representing the cosine distance. - func cosineDistance(_ other: [Double]) -> FunctionExpr + /// - Parameter vector: The other vector as `[Double]` to compare against. + /// - Returns: A new `FunctionExpression` representing the cosine distance. + func cosineDistance(_ vector: [Double]) -> FunctionExpression /// Calculates the dot product between this vector expression and another vector expression. /// Assumes both `self` and `other` evaluate to Vectors. /// /// ```swift - /// // Dot product between 'vectorA' and 'vectorB' fields + /// // Dot product between "vectorA" and "vectorB" fields /// Field("vectorA").dotProduct(Field("vectorB")) /// ``` /// - /// - Parameter other: The other vector as an `Expr` to calculate with. - /// - Returns: A new `FunctionExpr` representing the dot product. - func dotProduct(_ other: Expr) -> FunctionExpr + /// - Parameter expression: The other vector as an `Expr` to calculate with. + /// - Returns: A new `FunctionExpression` representing the dot product. + func dotProduct(_ expression: Expression) -> FunctionExpression /// Calculates the dot product between this vector expression and another vector literal /// (`VectorValue`). @@ -1086,9 +1203,9 @@ public protocol Expr: Sendable { /// let weightVector = VectorValue(vector: [0.5, -0.5]) /// Field("features").dotProduct(weightVector) /// ``` - /// - Parameter other: The other vector as a `VectorValue` to calculate with. - /// - Returns: A new `FunctionExpr` representing the dot product. - func dotProduct(_ other: VectorValue) -> FunctionExpr + /// - Parameter vector: The other vector as a `VectorValue` to calculate with. + /// - Returns: A new `FunctionExpression` representing the dot product. + func dotProduct(_ vector: VectorValue) -> FunctionExpression /// Calculates the dot product between this vector expression and another vector literal /// (`[Double]`). @@ -1098,22 +1215,22 @@ public protocol Expr: Sendable { /// // Dot product between a feature vector and a target vector literal /// Field("features").dotProduct([0.5, 0.8, 0.2]) /// ``` - /// - Parameter other: The other vector as `[Double]` to calculate with. - /// - Returns: A new `FunctionExpr` representing the dot product. - func dotProduct(_ other: [Double]) -> FunctionExpr + /// - Parameter vector: The other vector as `[Double]` to calculate with. + /// - Returns: A new `FunctionExpression` representing the dot product. + func dotProduct(_ vector: [Double]) -> FunctionExpression /// Calculates the Euclidean distance between this vector expression and another vector /// expression. /// Assumes both `self` and `other` evaluate to Vectors. /// /// ```swift - /// // Euclidean distance between 'pointA' and 'pointB' fields + /// // Euclidean distance between "pointA" and "pointB" fields /// Field("pointA").euclideanDistance(Field("pointB")) /// ``` /// - /// - Parameter other: The other vector as an `Expr` to compare against. - /// - Returns: A new `FunctionExpr` representing the Euclidean distance. - func euclideanDistance(_ other: Expr) -> FunctionExpr + /// - Parameter expression: The other vector as an `Expr` to compare against. + /// - Returns: A new `FunctionExpression` representing the Euclidean distance. + func euclideanDistance(_ expression: Expression) -> FunctionExpression /// Calculates the Euclidean distance between this vector expression and another vector literal /// (`VectorValue`). @@ -1123,21 +1240,21 @@ public protocol Expr: Sendable { /// let targetPoint = VectorValue(vector: [1.0, 2.0]) /// Field("currentLocation").euclideanDistance(targetPoint) /// ``` - /// - Parameter other: The other vector as a `VectorValue` to compare against. - /// - Returns: A new `FunctionExpr` representing the Euclidean distance. - func euclideanDistance(_ other: VectorValue) -> FunctionExpr + /// - Parameter vector: The other vector as a `VectorValue` to compare against. + /// - Returns: A new `FunctionExpression` representing the Euclidean distance. + func euclideanDistance(_ vector: VectorValue) -> FunctionExpression /// Calculates the Euclidean distance between this vector expression and another vector literal /// (`[Double]`). /// Assumes `self` evaluates to a Vector. /// /// ```swift - /// // Euclidean distance between 'location' field and a target location literal + /// // Euclidean distance between "location" field and a target location literal /// Field("location").euclideanDistance([37.7749, -122.4194]) /// ``` - /// - Parameter other: The other vector as `[Double]` to compare against. - /// - Returns: A new `FunctionExpr` representing the Euclidean distance. - func euclideanDistance(_ other: [Double]) -> FunctionExpr + /// - Parameter vector: The other vector as `[Double]` to compare against. + /// - Returns: A new `FunctionExpression` representing the Euclidean distance. + func euclideanDistance(_ vector: [Double]) -> FunctionExpression /// Calculates the Manhattan (L1) distance between this vector expression and another vector /// expression. @@ -1146,13 +1263,13 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Manhattan distance between 'vector1' field and 'vector2' field + /// // Manhattan distance between "vector1" field and "vector2" field /// Field("vector1").manhattanDistance(Field("vector2")) /// ``` /// - /// - Parameter other: The other vector as an `Expr` to compare against. - /// - Returns: A new `FunctionExpr` representing the Manhattan distance. - func manhattanDistance(_ other: Expr) -> FunctionExpr + /// - Parameter expression: The other vector as an `Expr` to compare against. + /// - Returns: A new `FunctionExpression` representing the Manhattan distance. + func manhattanDistance(_ expression: Expression) -> FunctionExpression /// Calculates the Manhattan (L1) distance between this vector expression and another vector /// literal (`VectorValue`). @@ -1162,9 +1279,9 @@ public protocol Expr: Sendable { /// let referencePoint = VectorValue(vector: [5.0, 10.0]) /// Field("dataPoint").manhattanDistance(referencePoint) /// ``` - /// - Parameter other: The other vector as a `VectorValue` to compare against. - /// - Returns: A new `FunctionExpr` representing the Manhattan distance. - func manhattanDistance(_ other: VectorValue) -> FunctionExpr + /// - Parameter vector: The other vector as a `VectorValue` to compare against. + /// - Returns: A new `FunctionExpression` representing the Manhattan distance. + func manhattanDistance(_ vector: VectorValue) -> FunctionExpression /// Calculates the Manhattan (L1) distance between this vector expression and another vector /// literal (`[Double]`). @@ -1172,12 +1289,12 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Manhattan distance between 'point' field and a target point + /// // Manhattan distance between "point" field and a target point /// Field("point").manhattanDistance([10.0, 20.0]) /// ``` - /// - Parameter other: The other vector as `[Double]` to compare against. - /// - Returns: A new `FunctionExpr` representing the Manhattan distance. - func manhattanDistance(_ other: [Double]) -> FunctionExpr + /// - Parameter vector: The other vector as `[Double]` to compare against. + /// - Returns: A new `FunctionExpression` representing the Manhattan distance. + func manhattanDistance(_ vector: [Double]) -> FunctionExpression // MARK: Timestamp operations @@ -1186,69 +1303,69 @@ public protocol Expr: Sendable { /// Assumes `self` evaluates to a number. /// /// ```swift - /// // Interpret 'microseconds' field as microseconds since epoch. + /// // Interpret "microseconds" field as microseconds since epoch. /// Field("microseconds").unixMicrosToTimestamp() /// ``` /// - /// - Returns: A new `FunctionExpr` representing the timestamp. - func unixMicrosToTimestamp() -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the timestamp. + func unixMicrosToTimestamp() -> FunctionExpression /// Creates an expression that converts this timestamp expression to the number of microseconds /// since the Unix epoch. Assumes `self` evaluates to a Timestamp. /// /// ```swift - /// // Convert 'timestamp' field to microseconds since epoch. + /// // Convert "timestamp" field to microseconds since epoch. /// Field("timestamp").timestampToUnixMicros() /// ``` /// - /// - Returns: A new `FunctionExpr` representing the number of microseconds. - func timestampToUnixMicros() -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the number of microseconds. + func timestampToUnixMicros() -> FunctionExpression /// Creates an expression that interprets this expression (evaluating to a number) as milliseconds /// since the Unix epoch and returns a timestamp. /// Assumes `self` evaluates to a number. /// /// ```swift - /// // Interpret 'milliseconds' field as milliseconds since epoch. + /// // Interpret "milliseconds" field as milliseconds since epoch. /// Field("milliseconds").unixMillisToTimestamp() /// ``` /// - /// - Returns: A new `FunctionExpr` representing the timestamp. - func unixMillisToTimestamp() -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the timestamp. + func unixMillisToTimestamp() -> FunctionExpression /// Creates an expression that converts this timestamp expression to the number of milliseconds /// since the Unix epoch. Assumes `self` evaluates to a Timestamp. /// /// ```swift - /// // Convert 'timestamp' field to milliseconds since epoch. + /// // Convert "timestamp" field to milliseconds since epoch. /// Field("timestamp").timestampToUnixMillis() /// ``` /// - /// - Returns: A new `FunctionExpr` representing the number of milliseconds. - func timestampToUnixMillis() -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the number of milliseconds. + func timestampToUnixMillis() -> FunctionExpression /// Creates an expression that interprets this expression (evaluating to a number) as seconds /// since the Unix epoch and returns a timestamp. /// Assumes `self` evaluates to a number. /// /// ```swift - /// // Interpret 'seconds' field as seconds since epoch. + /// // Interpret "seconds" field as seconds since epoch. /// Field("seconds").unixSecondsToTimestamp() /// ``` /// - /// - Returns: A new `FunctionExpr` representing the timestamp. - func unixSecondsToTimestamp() -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the timestamp. + func unixSecondsToTimestamp() -> FunctionExpression /// Creates an expression that converts this timestamp expression to the number of seconds /// since the Unix epoch. Assumes `self` evaluates to a Timestamp. /// /// ```swift - /// // Convert 'timestamp' field to seconds since epoch. + /// // Convert "timestamp" field to seconds since epoch. /// Field("timestamp").timestampToUnixSeconds() /// ``` /// - /// - Returns: A new `FunctionExpr` representing the number of seconds. - func timestampToUnixSeconds() -> FunctionExpr + /// - Returns: A new `FunctionExpression` representing the number of seconds. + func timestampToUnixSeconds() -> FunctionExpression /// Creates an expression that adds a specified amount of time to this timestamp expression, /// where unit and amount are provided as expressions. @@ -1256,30 +1373,30 @@ public protocol Expr: Sendable { /// evaluates to an integer. /// /// ```swift - /// // Add duration from 'unitField'/'amountField' to 'timestamp' - /// Field("timestamp").timestampAdd(Field("unitField"), Field("amountField")) + /// // Add duration from "unitField"/"amountField" to "timestamp" + /// Field("timestamp").timestampAdd(amount: Field("amountField"), unit: Field("unitField")) /// ``` /// /// - Parameter unit: An `Expr` evaluating to the unit of time string (e.g., "day", "hour"). - /// Valid units are 'microsecond', 'millisecond', 'second', 'minute', 'hour', - /// 'day'. + /// Valid units are "microsecond", "millisecond", "second", "minute", "hour", + /// "day". /// - Parameter amount: An `Expr` evaluating to the amount (Int) of the unit to add. - /// - Returns: A new `FunctionExpr` representing the resulting timestamp. - func timestampAdd(_ unit: Expr, _ amount: Expr) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the resulting timestamp. + func timestampAdd(amount: Expression, unit: Expression) -> FunctionExpression /// Creates an expression that adds a specified amount of time to this timestamp expression, /// where unit and amount are provided as literals. /// Assumes `self` evaluates to a Timestamp. /// /// ```swift - /// // Add 1 day to the 'timestamp' field. - /// Field("timestamp").timestampAdd(.day, 1) + /// // Add 1 day to the "timestamp" field. + /// Field("timestamp").timestampAdd(1, .day) /// ``` /// /// - Parameter unit: The `TimeUnit` enum representing the unit of time. /// - Parameter amount: The literal `Int` amount of the unit to add. - /// - Returns: A new `FunctionExpr` representing the resulting timestamp. - func timestampAdd(_ unit: TimeUnit, _ amount: Int) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the resulting timestamp. + func timestampAdd(_ amount: Int, _ unit: TimeUnit) -> FunctionExpression /// Creates an expression that subtracts a specified amount of time from this timestamp /// expression, @@ -1288,16 +1405,16 @@ public protocol Expr: Sendable { /// evaluates to an integer. /// /// ```swift - /// // Subtract duration from 'unitField'/'amountField' from 'timestamp' - /// Field("timestamp").timestampSub(Field("unitField"), Field("amountField")) + /// // Subtract duration from "unitField"/"amountField" from "timestamp" + /// Field("timestamp").timestampSub(amount: Field("amountField"), unit: Field("unitField")) /// ``` /// /// - Parameter unit: An `Expr` evaluating to the unit of time string (e.g., "day", "hour"). - /// Valid units are 'microsecond', 'millisecond', 'second', 'minute', 'hour', - /// 'day'. + /// Valid units are "microsecond", "millisecond", "second", "minute", "hour", + /// "day". /// - Parameter amount: An `Expr` evaluating to the amount (Int) of the unit to subtract. - /// - Returns: A new `FunctionExpr` representing the resulting timestamp. - func timestampSub(_ unit: Expr, _ amount: Expr) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the resulting timestamp. + func timestampSub(amount: Expression, unit: Expression) -> FunctionExpression /// Creates an expression that subtracts a specified amount of time from this timestamp /// expression, @@ -1305,14 +1422,14 @@ public protocol Expr: Sendable { /// Assumes `self` evaluates to a Timestamp. /// /// ```swift - /// // Subtract 1 day from the 'timestamp' field. - /// Field("timestamp").timestampSub(.day, 1) + /// // Subtract 1 day from the "timestamp" field. + /// Field("timestamp").timestampSub(1, .day) /// ``` /// /// - Parameter unit: The `TimeUnit` enum representing the unit of time. /// - Parameter amount: The literal `Int` amount of the unit to subtract. - /// - Returns: A new `FunctionExpr` representing the resulting timestamp. - func timestampSub(_ unit: TimeUnit, _ amount: Int) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the resulting timestamp. + func timestampSub(_ amount: Int, _ unit: TimeUnit) -> FunctionExpression // MARK: - Bitwise operations @@ -1322,37 +1439,37 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Bitwise AND of 'flags' field and 0xFF + /// // Bitwise AND of "flags" field and 0xFF /// Field("flags").bitAnd(0xFF) /// ``` /// /// - Parameter otherBits: The integer literal operand. - /// - Returns: A new `FunctionExpr` representing the bitwise AND operation. - func bitAnd(_ otherBits: Int) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the bitwise AND operation. + func bitAnd(_ otherBits: Int) -> FunctionExpression /// Creates an expression applying bitwise AND between this expression and a UInt8 literal (often /// for byte masks). /// Assumes `self` evaluates to an Integer or Bytes. /// - Note: This API is in beta. /// ```swift - /// // Bitwise AND of 'byteFlags' field and a byte mask + /// // Bitwise AND of "byteFlags" field and a byte mask /// Field("byteFlags").bitAnd(0b00001111 as UInt8) /// ``` /// - Parameter otherBits: The UInt8 literal operand. - /// - Returns: A new `FunctionExpr` representing the bitwise AND operation. - func bitAnd(_ otherBits: UInt8) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the bitwise AND operation. + func bitAnd(_ otherBits: UInt8) -> FunctionExpression /// Creates an expression applying bitwise AND between this expression and another expression. /// Assumes `self` and `bitsExpression` evaluate to Integer or Bytes. /// - Note: This API is in beta. /// /// ```swift - /// // Bitwise AND of 'mask1' and 'mask2' fields + /// // Bitwise AND of "mask1" and "mask2" fields /// Field("mask1").bitAnd(Field("mask2")) /// ``` /// - Parameter bitsExpression: The other `Expr` operand. - /// - Returns: A new `FunctionExpr` representing the bitwise AND operation. - func bitAnd(_ bitsExpression: Expr) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the bitwise AND operation. + func bitAnd(_ bitsExpression: Expression) -> FunctionExpression /// Creates an expression applying bitwise OR between this expression and an integer literal. /// Assumes `self` evaluates to an Integer or Bytes. @@ -1360,36 +1477,36 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Bitwise OR of 'flags' field and 0x01 + /// // Bitwise OR of "flags" field and 0x01 /// Field("flags").bitOr(0x01) /// ``` /// /// - Parameter otherBits: The integer literal operand. - /// - Returns: A new `FunctionExpr` representing the bitwise OR operation. - func bitOr(_ otherBits: Int) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the bitwise OR operation. + func bitOr(_ otherBits: Int) -> FunctionExpression /// Creates an expression applying bitwise OR between this expression and a UInt8 literal. /// Assumes `self` evaluates to an Integer or Bytes. /// - Note: This API is in beta. /// ```swift - /// // Set specific bits in 'controlByte' + /// // Set specific bits in "controlByte" /// Field("controlByte").bitOr(0b10000001 as UInt8) /// ``` /// - Parameter otherBits: The UInt8 literal operand. - /// - Returns: A new `FunctionExpr` representing the bitwise OR operation. - func bitOr(_ otherBits: UInt8) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the bitwise OR operation. + func bitOr(_ otherBits: UInt8) -> FunctionExpression /// Creates an expression applying bitwise OR between this expression and another expression. /// Assumes `self` and `bitsExpression` evaluate to Integer or Bytes. /// - Note: This API is in beta. /// /// ```swift - /// // Bitwise OR of 'permissionSet1' and 'permissionSet2' fields + /// // Bitwise OR of "permissionSet1" and "permissionSet2" fields /// Field("permissionSet1").bitOr(Field("permissionSet2")) /// ``` /// - Parameter bitsExpression: The other `Expr` operand. - /// - Returns: A new `FunctionExpr` representing the bitwise OR operation. - func bitOr(_ bitsExpression: Expr) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the bitwise OR operation. + func bitOr(_ bitsExpression: Expression) -> FunctionExpression /// Creates an expression applying bitwise XOR between this expression and an integer literal. /// Assumes `self` evaluates to an Integer or Bytes. @@ -1397,36 +1514,36 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Bitwise XOR of 'toggle' field and 0xFFFF + /// // Bitwise XOR of "toggle" field and 0xFFFF /// Field("toggle").bitXor(0xFFFF) /// ``` /// /// - Parameter otherBits: The integer literal operand. - /// - Returns: A new `FunctionExpr` representing the bitwise XOR operation. - func bitXor(_ otherBits: Int) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the bitwise XOR operation. + func bitXor(_ otherBits: Int) -> FunctionExpression /// Creates an expression applying bitwise XOR between this expression and a UInt8 literal. /// Assumes `self` evaluates to an Integer or Bytes. /// - Note: This API is in beta. /// ```swift - /// // Toggle bits in 'statusByte' using a XOR mask + /// // Toggle bits in "statusByte" using a XOR mask /// Field("statusByte").bitXor(0b01010101 as UInt8) /// ``` /// - Parameter otherBits: The UInt8 literal operand. - /// - Returns: A new `FunctionExpr` representing the bitwise XOR operation. - func bitXor(_ otherBits: UInt8) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the bitwise XOR operation. + func bitXor(_ otherBits: UInt8) -> FunctionExpression /// Creates an expression applying bitwise XOR between this expression and another expression. /// Assumes `self` and `bitsExpression` evaluate to Integer or Bytes. /// - Note: This API is in beta. /// /// ```swift - /// // Bitwise XOR of 'key1' and 'key2' fields (assuming Bytes) + /// // Bitwise XOR of "key1" and "key2" fields (assuming Bytes) /// Field("key1").bitXor(Field("key2")) /// ``` /// - Parameter bitsExpression: The other `Expr` operand. - /// - Returns: A new `FunctionExpr` representing the bitwise XOR operation. - func bitXor(_ bitsExpression: Expr) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the bitwise XOR operation. + func bitXor(_ bitsExpression: Expression) -> FunctionExpression /// Creates an expression applying bitwise NOT to this expression. /// Assumes `self` evaluates to an Integer or Bytes. @@ -1434,12 +1551,12 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Bitwise NOT of 'mask' field + /// // Bitwise NOT of "mask" field /// Field("mask").bitNot() /// ``` /// - /// - Returns: A new `FunctionExpr` representing the bitwise NOT operation. - func bitNot() -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the bitwise NOT operation. + func bitNot() -> FunctionExpression /// Creates an expression applying bitwise left shift to this expression by a literal number of /// bits. @@ -1448,13 +1565,13 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Left shift 'value' field by 2 bits + /// // Left shift "value" field by 2 bits /// Field("value").bitLeftShift(2) /// ``` /// /// - Parameter y: The number of bits (Int literal) to shift by. - /// - Returns: A new `FunctionExpr` representing the bitwise left shift operation. - func bitLeftShift(_ y: Int) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the bitwise left shift operation. + func bitLeftShift(_ y: Int) -> FunctionExpression /// Creates an expression applying bitwise left shift to this expression by a number of bits /// specified by an expression. @@ -1462,12 +1579,12 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Left shift 'data' by number of bits in 'shiftCount' field + /// // Left shift "data" by number of bits in "shiftCount" field /// Field("data").bitLeftShift(Field("shiftCount")) /// ``` /// - Parameter numberExpr: An `Expr` (evaluating to an Int) for the number of bits to shift by. - /// - Returns: A new `FunctionExpr` representing the bitwise left shift operation. - func bitLeftShift(_ numberExpr: Expr) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the bitwise left shift operation. + func bitLeftShift(_ numberExpr: Expression) -> FunctionExpression /// Creates an expression applying bitwise right shift to this expression by a literal number of /// bits. @@ -1476,13 +1593,13 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Right shift 'value' field by 4 bits + /// // Right shift "value" field by 4 bits /// Field("value").bitRightShift(4) /// ``` /// /// - Parameter y: The number of bits (Int literal) to shift by. - /// - Returns: A new `FunctionExpr` representing the bitwise right shift operation. - func bitRightShift(_ y: Int) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the bitwise right shift operation. + func bitRightShift(_ y: Int) -> FunctionExpression /// Creates an expression applying bitwise right shift to this expression by a number of bits /// specified by an expression. @@ -1490,12 +1607,14 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Right shift 'data' by number of bits in 'shiftCount' field + /// // Right shift "data" by number of bits in "shiftCount" field /// Field("data").bitRightShift(Field("shiftCount")) /// ``` /// - Parameter numberExpr: An `Expr` (evaluating to an Int) for the number of bits to shift by. - /// - Returns: A new `FunctionExpr` representing the bitwise right shift operation. - func bitRightShift(_ numberExpr: Expr) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the bitwise right shift operation. + func bitRightShift(_ numberExpr: Expression) -> FunctionExpression + + func documentId() -> FunctionExpression /// Creates an expression that returns the result of `catchExpr` if this expression produces an /// error during evaluation, @@ -1504,13 +1623,13 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Try dividing 'a' by 'b', return field 'fallbackValue' on error (e.g., division by zero) + /// // Try dividing "a" by "b", return field "fallbackValue" on error (e.g., division by zero) /// Field("a").divide(Field("b")).ifError(Field("fallbackValue")) /// ``` /// - /// - Parameter catchExpr: The `Expr` to evaluate and return if this expression errors. - /// - Returns: A new `FunctionExpr` representing the 'ifError' operation. - func ifError(_ catchExpr: Expr) -> FunctionExpr + /// - Parameter catchExpr: The `Expression` to evaluate and return if this expression errors. + /// - Returns: A new "FunctionExpression" representing the "ifError" operation. + func ifError(_ catchExpr: Expression) -> FunctionExpression /// Creates an expression that returns the literal `catchValue` if this expression produces an /// error during evaluation, @@ -1519,20 +1638,20 @@ public protocol Expr: Sendable { /// - Note: This API is in beta. /// /// ```swift - /// // Get first item in 'title' array, or return "Default Title" if error (e.g., empty array) + /// // Get first item in "title" array, or return "Default Title" if error (e.g., empty array) /// Field("title").arrayGet(0).ifError("Default Title") /// ``` /// /// - Parameter catchValue: The literal `Sendable` value to return if this expression errors. - /// - Returns: A new `FunctionExpr` representing the 'ifError' operation. - func ifError(_ catchValue: Sendable) -> FunctionExpr + /// - Returns: A new "FunctionExpression" representing the "ifError" operation. + func ifError(_ catchValue: Sendable) -> FunctionExpression // MARK: Sorting /// Creates an `Ordering` object that sorts documents in ascending order based on this expression. /// /// ```swift - /// // Sort documents by the 'name' field in ascending order + /// // Sort documents by the "name" field in ascending order /// firestore.pipeline().collection("users") /// .sort(Field("name").ascending()) /// ``` @@ -1544,7 +1663,7 @@ public protocol Expr: Sendable { /// expression. /// /// ```swift - /// // Sort documents by the 'createdAt' field in descending order + /// // Sort documents by the "createdAt" field in descending order /// firestore.pipeline().collection("users") /// .sort(Field("createdAt").descending()) /// ``` diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/ArrayExpression.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/ArrayExpression.swift similarity index 87% rename from Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/ArrayExpression.swift rename to Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/ArrayExpression.swift index e1f5d749c5f..673485d6e59 100644 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/ArrayExpression.swift +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/ArrayExpression.swift @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -public class ArrayExpression: FunctionExpr, @unchecked Sendable { - var result: [Expr] = [] +public class ArrayExpression: FunctionExpression, @unchecked Sendable { + var result: [Expression] = [] public init(_ elements: [Sendable]) { for element in elements { result.append(Helper.sendableToExpr(element)) diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/Constant.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/Constant.swift similarity index 96% rename from Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/Constant.swift rename to Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/Constant.swift index 8f6b3709892..4505133f148 100644 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/Constant.swift +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/Constant.swift @@ -18,7 +18,7 @@ @_exported import FirebaseFirestoreInternal #endif // SWIFT_PACKAGE -public struct Constant: Expr, BridgeWrapper, @unchecked Sendable { +public struct Constant: Expression, BridgeWrapper, @unchecked Sendable { let bridge: ExprBridge let value: Any? diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/DocumentId.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/DocumentId.swift new file mode 100644 index 00000000000..d1a8d8594ef --- /dev/null +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/DocumentId.swift @@ -0,0 +1,48 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// +/// Represents the ID of a document. +/// +/// A `DocumentId` expression can be used in pipeline stages like `where`, `sort`, and `select` +/// to refer to the unique identifier of a document. It is a special field that is implicitly +/// available on every document. +/// +/// Example usage: +/// +/// ```swift +/// // Sort documents by their ID in ascending order +/// firestore.pipeline() +/// .collection("users") +/// .sort(DocumentId().ascending()) +/// +/// // Select the document ID and another field +/// firestore.pipeline() +/// .collection("products") +/// .select([ +/// DocumentId().as("productId"), +/// Field("name") +/// ]) +/// +/// // Filter documents based on their ID +/// firestore.pipeline() +/// .collection("orders") +/// .where(DocumentId().equal("some-order-id")) +/// ``` +public class DocumentId: Field, @unchecked Sendable { + /// Initializes a new `DocumentId` expression. + public init() { + super.init("__name__") + } +} diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/Field.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/Field.swift similarity index 90% rename from Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/Field.swift rename to Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/Field.swift index 99dc7e1b21d..4ec5dfb0d78 100644 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/Field.swift +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/Field.swift @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -public class Field: ExprBridge, Expr, Selectable, BridgeWrapper, SelectableWrapper, +public class Field: ExprBridge, Expression, Selectable, BridgeWrapper, SelectableWrapper, @unchecked Sendable { let bridge: ExprBridge var alias: String - var expr: Expr { + var expr: Expression { return self } diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/FunctionExpr.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/FunctionExpression.swift similarity index 82% rename from Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/FunctionExpr.swift rename to Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/FunctionExpression.swift index 533f6a5ef51..825487c9a56 100644 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/FunctionExpr.swift +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/FunctionExpression.swift @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -public class FunctionExpr: Expr, BridgeWrapper, @unchecked Sendable { +public class FunctionExpression: Expression, BridgeWrapper, @unchecked Sendable { let bridge: ExprBridge let functionName: String - let agrs: [Expr] + let agrs: [Expression] - public init(_ functionName: String, _ agrs: [Expr]) { + public init(_ functionName: String, _ agrs: [Expression]) { self.functionName = functionName self.agrs = agrs bridge = FunctionExprBridge( diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/FunctionExpressions/BooleanExpression.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/FunctionExpressions/BooleanExpression.swift new file mode 100644 index 00000000000..514a9ac8858 --- /dev/null +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/FunctionExpressions/BooleanExpression.swift @@ -0,0 +1,173 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Foundation + +/// +/// A `BooleanExpression` is a specialized `FunctionExpression` that evaluates to a boolean value. +/// +/// It is used to construct conditional logic within Firestore pipelines, such as in `where` +/// clauses or `cond` expressions. `BooleanExpression` instances can be combined using standard +/// logical operators (`&&`, `||`, `!`, `^`) to create complex conditions. +/// +/// Example usage in a `where` clause: +/// ```swift +/// firestore.pipeline() +/// .collection("products") +/// .where( +/// Field("price").greaterThan(100) && +/// (Field("category").equal("electronics") || Field("on_sale").equal(true)) +/// ) +/// ``` +public class BooleanExpression: FunctionExpression, @unchecked Sendable { + override public init(_ functionName: String, _ agrs: [Expression]) { + super.init(functionName, agrs) + } + + /// Creates an aggregation that counts the number of documents for which this boolean expression + /// evaluates to `true`. + /// + /// This is useful for counting documents that meet a specific condition without retrieving the + /// documents themselves. + /// + /// ```swift + /// // Count how many books were published after 1980 + /// let post1980Condition = Field("published").greaterThan(1980) + /// firestore.pipeline() + /// .collection("books") + /// .aggregate([ + /// post1980Condition.countIf().as("modernBooksCount") + /// ]) + /// ``` + /// + /// - Returns: An `AggregateFunction` that performs the conditional count. + public func countIf() -> AggregateFunction { + return AggregateFunction("count_if", [self]) + } + + /// Creates a conditional expression that returns one of two specified expressions based on the + /// result of this boolean expression. + /// + /// This is equivalent to a ternary operator (`condition ? then : else`). + /// + /// ```swift + /// // Create a new field "status" based on the "rating" field. + /// // If rating > 4.5, status is "top_rated", otherwise "regular". + /// firestore.pipeline() + /// .collection("products") + /// .addFields([ + /// Field("rating").greaterThan(4.5) + /// .then(Constant("top_rated"), else: Constant("regular")) + /// .as("status") + /// ]) + /// ``` + /// + /// - Parameters: + /// - thenExpression: The `Expression` to evaluate if this boolean expression is `true`. + /// - elseExpression: The `Expression` to evaluate if this boolean expression is `false`. + /// - Returns: A new `FunctionExpression` representing the conditional logic. + public func then(_ thenExpression: Expression, + else elseExpression: Expression) -> FunctionExpression { + return FunctionExpression("cond", [self, thenExpression, elseExpression]) + } + + /// Combines two boolean expressions with a logical AND (`&&`). + /// + /// The resulting expression is `true` only if both the left-hand side (`lhs`) and the right-hand + /// side (`rhs`) are `true`. + /// + /// ```swift + /// // Find books in the "Fantasy" genre with a rating greater than 4.5 + /// firestore.pipeline() + /// .collection("books") + /// .where( + /// Field("genre").equal("Fantasy") && Field("rating").greaterThan(4.5) + /// ) + /// ``` + /// + /// - Parameters: + /// - lhs: The left-hand boolean expression. + /// - rhs: The right-hand boolean expression. + /// - Returns: A new `BooleanExpression` representing the logical AND. + public static func && (lhs: BooleanExpression, + rhs: @autoclosure () throws -> BooleanExpression) rethrows + -> BooleanExpression { + try BooleanExpression("and", [lhs, rhs()]) + } + + /// Combines two boolean expressions with a logical OR (`||`). + /// + /// The resulting expression is `true` if either the left-hand side (`lhs`) or the right-hand + /// side (`rhs`) is `true`. + /// + /// ```swift + /// // Find books that are either in the "Romance" genre or were published before 1900 + /// firestore.pipeline() + /// .collection("books") + /// .where( + /// Field("genre").equal("Romance") || Field("published").lessThan(1900) + /// ) + /// ``` + /// + /// - Parameters: + /// - lhs: The left-hand boolean expression. + /// - rhs: The right-hand boolean expression. + /// - Returns: A new `BooleanExpression` representing the logical OR. + public static func || (lhs: BooleanExpression, + rhs: @autoclosure () throws -> BooleanExpression) rethrows + -> BooleanExpression { + try BooleanExpression("or", [lhs, rhs()]) + } + + /// Combines two boolean expressions with a logical XOR (`^`). + /// + /// The resulting expression is `true` if the left-hand side (`lhs`) and the right-hand side + /// (`rhs`) have different boolean values. + /// + /// ```swift + /// // Find books that are in the "Dystopian" genre OR have a rating of 5.0, but not both. + /// firestore.pipeline() + /// .collection("books") + /// .where( + /// Field("genre").equal("Dystopian") ^ Field("rating").equal(5.0) + /// ) + /// ``` + /// + /// - Parameters: + /// - lhs: The left-hand boolean expression. + /// - rhs: The right-hand boolean expression. + /// - Returns: A new `BooleanExpression` representing the logical XOR. + public static func ^ (lhs: BooleanExpression, + rhs: @autoclosure () throws -> BooleanExpression) rethrows + -> BooleanExpression { + try BooleanExpression("xor", [lhs, rhs()]) + } + + /// Negates a boolean expression with a logical NOT (`!`). + /// + /// The resulting expression is `true` if the original expression is `false`, and vice versa. + /// + /// ```swift + /// // Find books that are NOT in the "Science Fiction" genre + /// firestore.pipeline() + /// .collection("books") + /// .where(!Field("genre").equal("Science Fiction")) + /// ``` + /// + /// - Parameter lhs: The boolean expression to negate. + /// - Returns: A new `BooleanExpression` representing the logical NOT. + public static prefix func ! (lhs: BooleanExpression) -> BooleanExpression { + return BooleanExpression("not", [lhs]) + } +} diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/FunctionExpr/RandomExpr.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/FunctionExpressions/RandomExpression.swift similarity index 89% rename from Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/FunctionExpr/RandomExpr.swift rename to Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/FunctionExpressions/RandomExpression.swift index 5ea39db81fc..a2a7ea41fe0 100644 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/FunctionExpr/RandomExpr.swift +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/FunctionExpressions/RandomExpression.swift @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -public class RandomExpr: FunctionExpr, @unchecked Sendable { +public class RandomExpression: FunctionExpression, @unchecked Sendable { public init() { super.init("rand", []) } diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/MapExpression.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/MapExpression.swift similarity index 88% rename from Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/MapExpression.swift rename to Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/MapExpression.swift index 93d9bb4859b..78f05c0fba1 100644 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Expr/MapExpression.swift +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/MapExpression.swift @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -public class MapExpression: FunctionExpr, @unchecked Sendable { - var result: [Expr] = [] +public class MapExpression: FunctionExpression, @unchecked Sendable { + var result: [Expression] = [] public init(_ elements: [String: Sendable]) { for element in elements { result.append(Constant(element.key)) diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Ordering.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Ordering.swift index 9659e95e682..4eae5eefafb 100644 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Ordering.swift +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/Ordering.swift @@ -15,18 +15,18 @@ */ public class Ordering: @unchecked Sendable { - let expr: Expr + let expr: Expression let direction: Direction let bridge: OrderingBridge - init(expr: Expr, direction: Direction) { + init(expr: Expression, direction: Direction) { self.expr = expr self.direction = direction bridge = OrderingBridge(expr: expr.toBridge(), direction: direction.rawValue) } } -public struct Direction: Sendable, Equatable, Hashable { +struct Direction: Sendable, Equatable, Hashable { let kind: Kind let rawValue: String @@ -35,11 +35,11 @@ public struct Direction: Sendable, Equatable, Hashable { case descending } - public static var ascending: Direction { + static var ascending: Direction { return self.init(kind: .ascending, rawValue: "ascending") } - public static var descending: Direction { + static var descending: Direction { return self.init(kind: .descending, rawValue: "descending") } diff --git a/Firestore/Swift/Source/SwiftAPI/Pipeline/Pipeline.swift b/Firestore/Swift/Source/SwiftAPI/Pipeline/Pipeline.swift index 6c2a6e34053..c5343ca4f89 100644 --- a/Firestore/Swift/Source/SwiftAPI/Pipeline/Pipeline.swift +++ b/Firestore/Swift/Source/SwiftAPI/Pipeline/Pipeline.swift @@ -44,36 +44,34 @@ import Foundation /// // Example 1: Select specific fields and rename 'rating' to 'bookRating'. /// // Assumes `Field("rating").as("bookRating")` is a valid `Selectable` expression. /// do { -/// let results1 = try await db.pipeline().collection("books") +/// let snapshot1 = try await db.pipeline().collection("books") /// .select(Field("title"), Field("author"), Field("rating").as("bookRating")) /// .execute() -/// print("Results 1: \(results1.documents)") +/// print("Results 1: \(snapshot1.results)") /// } catch { /// print("Error in example 1: \(error)") /// } /// /// // Example 2: Filter documents where 'genre' is "Science Fiction" and 'published' is after 1950. -/// // Assumes `Function.eq`, `Function.gt`, and `Function.and` create `BooleanExpr`. /// do { -/// let results2 = try await db.pipeline().collection("books") -/// .where(Function.and( -/// Function.eq(Field("genre"), "Science Fiction"), -/// Function.gt(Field("published"), 1950) -/// )) +/// let snapshot2 = try await db.pipeline().collection("books") +/// .where( +/// Field("genre").equal("Science Fiction") +/// && Field("published").greaterThan(1950) +/// ) /// .execute() -/// print("Results 2: \(results2.documents)") +/// print("Results 2: \(snapshot2.results)") /// } catch { /// print("Error in example 2: \(error)") /// } /// /// // Example 3: Calculate the average rating of books published after 1980. -/// // Assumes `avg()` creates an `Accumulator` and `AggregateWithAlias` is used correctly. /// do { -/// let results3 = try await db.pipeline().collection("books") -/// .where(Function.gt(Field("published"), 1980)) -/// .aggregate(AggregateWithas(avg(Field("rating")), alias: "averageRating")) +/// let snapshot3 = try await db.pipeline().collection("books") +/// .where(Field("published").greaterThan(1980)) +/// .aggregate(Field("rating").average().as("averageRating")) /// .execute() -/// print("Results 3: \(results3.documents)") +/// print("Results 3: \(snapshot3.results)") /// } catch { /// print("Error in example 3: \(error)") /// } @@ -127,45 +125,36 @@ public struct Pipeline: @unchecked Sendable { /// stages or constants. You can use this to create new fields or overwrite existing ones /// (if there is a name overlap). /// - /// The added fields are defined using `Selectable`s, which can be: - /// - `Field`: References an existing document field. - /// - `Function`: Performs a calculation using functions like `Function.add` or - /// `Function.multiply`, - /// typically with an assigned alias (e.g., `Function.multiply(Field("price"), - /// 1.1).as("priceWithTax")`). - /// /// ```swift /// // let pipeline: Pipeline = ... // Assume initial pipeline from a collection. - /// let updatedPipeline = pipeline.addFields( + /// let updatedPipeline = pipeline.addFields([ /// Field("rating").as("bookRating"), // Rename 'rating' to 'bookRating'. - /// Function.add(5, Field("quantity")).as("totalQuantityPlusFive") // Calculate - /// 'totalQuantityPlusFive'. - /// ) + /// Field("quantity").add(5).as("totalQuantityPlusFive") // Calculate + /// // 'totalQuantityPlusFive'. + /// ]) /// // let results = try await updatedPipeline.execute() /// ``` /// - /// - Parameter field: The first field to add to the documents, specified as a `Selectable`. - /// - Parameter additionalFields: Optional additional fields to add, specified as `Selectable`s. + /// - Parameter selectables: An array of at least one `Selectable` to add to the documents. /// - Returns: A new `Pipeline` object with this stage appended. - public func addFields(_ field: Selectable, _ additionalFields: Selectable...) -> Pipeline { - let fields = [field] + additionalFields - return Pipeline(stages: stages + [AddFields(fields: fields)], db: db) + public func addFields(_ selectables: [Selectable]) -> Pipeline { + return Pipeline(stages: stages + [AddFields(selectables: selectables)], db: db) } /// Removes fields from outputs of previous stages. /// /// ```swift /// // let pipeline: Pipeline = ... // Assume initial pipeline. - /// let updatedPipeline = pipeline.removeFields(Field("confidentialData"), Field("internalNotes")) + /// let updatedPipeline = pipeline.removeFields([Field("confidentialData"), + /// Field("internalNotes")]) /// // let results = try await updatedPipeline.execute() /// ``` /// - /// - Parameter field: The first field to remove, specified as a `Field` instance. - /// - Parameter additionalFields: Optional additional fields to remove. + /// - Parameter fields: An array of at least one `Field` instance to remove. /// - Returns: A new `Pipeline` object with this stage appended. - public func removeFields(_ field: Field, _ additionalFields: Field...) -> Pipeline { + public func removeFields(_ fields: [Field]) -> Pipeline { return Pipeline( - stages: stages + [RemoveFieldsStage(fields: [field] + additionalFields)], + stages: stages + [RemoveFieldsStage(fields: fields)], db: db ) } @@ -175,16 +164,15 @@ public struct Pipeline: @unchecked Sendable { /// ```swift /// // let pipeline: Pipeline = ... // Assume initial pipeline. /// // Removes fields 'rating' and 'cost' from the previous stage outputs. - /// let updatedPipeline = pipeline.removeFields("rating", "cost") + /// let updatedPipeline = pipeline.removeFields(["rating", "cost"]) /// // let results = try await updatedPipeline.execute() /// ``` /// - /// - Parameter field: The name of the first field to remove. - /// - Parameter additionalFields: Optional additional field names to remove. + /// - Parameter fields: An array of at least one field name to remove. /// - Returns: A new `Pipeline` object with this stage appended. - public func removeFields(_ field: String, _ additionalFields: String...) -> Pipeline { + public func removeFields(_ fields: [String]) -> Pipeline { return Pipeline( - stages: stages + [RemoveFieldsStage(fields: [field] + additionalFields)], + stages: stages + [RemoveFieldsStage(fields: fields)], db: db ) } @@ -194,8 +182,8 @@ public struct Pipeline: @unchecked Sendable { /// The selected fields are defined using `Selectable` expressions, which can be: /// - `String`: Name of an existing field (implicitly converted to `Field`). /// - `Field`: References an existing field. - /// - `Function`: Represents the result of a function with an assigned alias - /// (e.g., `Function.toUppercase(Field("address")).as("upperAddress")`). + /// - `FunctionExpression`: Represents the result of a function with an assigned alias + /// (e.g., `Field("address").uppercased().as("upperAddress")`). /// /// If no selections are provided, the output of this stage is typically empty. /// Use `addFields` if only additions are desired without replacing the existing document @@ -203,21 +191,18 @@ public struct Pipeline: @unchecked Sendable { /// /// ```swift /// // let pipeline: Pipeline = ... // Assume initial pipeline. - /// let projectedPipeline = pipeline.select( + /// let projectedPipeline = pipeline.select([ /// Field("firstName"), /// Field("lastName"), - /// Function.toUppercase(Field("address")).as("upperAddress") - /// ) + /// Field("address").uppercased().as("upperAddress") + /// ]) /// // let results = try await projectedPipeline.execute() /// ``` /// - /// - Parameter selection: The first field to include in the output documents, specified as a - /// `Selectable`. - /// - Parameter additionalSelections: Optional additional fields to include, specified as - /// `Selectable`s. + /// - Parameter selections: An array of at least one `Selectable` expression to include in the + /// output documents. /// - Returns: A new `Pipeline` object with this stage appended. - public func select(_ selection: Selectable, _ additionalSelections: Selectable...) -> Pipeline { - let selections = [selection] + additionalSelections + public func select(_ selections: [Selectable]) -> Pipeline { return Pipeline( stages: stages + [Select(selections: selections)], db: db @@ -231,22 +216,15 @@ public struct Pipeline: @unchecked Sendable { /// /// ```swift /// // let pipeline: Pipeline = ... // Assume initial pipeline. - /// let projectedPipeline = pipeline.select("title", "author", "yearPublished") + /// let projectedPipeline = pipeline.select(["title", "author", "yearPublished"]) /// // let results = try await projectedPipeline.execute() /// ``` /// - /// - Parameter selection: The name of the first field to include in the output documents. - /// - Parameter additionalSelections: Optional additional field names to include. + /// - Parameter selections: An array of at least one field name to include in the output + /// documents. /// - Returns: A new `Pipeline` object with this stage appended. - public func select(_ selection: String, _ additionalSelections: String...) -> Pipeline { - let selections = ([selection] + additionalSelections).map { Field($0) } - return Pipeline( - stages: stages + [Select(selections: selections)], - db: db - ) - } - - public func select(_ selections: [Selectable]) -> Pipeline { + public func select(_ selections: [String]) -> Pipeline { + let selections = selections.map { Field($0) } return Pipeline( stages: stages + [Select(selections: selections)], db: db @@ -254,26 +232,26 @@ public struct Pipeline: @unchecked Sendable { } /// Filters documents from previous stages, including only those matching the specified - /// `BooleanExpr`. + /// `BooleanExpression`. /// /// This stage applies conditions similar to a "WHERE" clause in SQL. - /// Filter documents based on field values using `BooleanExpr` implementations, such as: - /// - Field comparators: `Function.eq`, `Function.lt` (less than), `Function.gt` (greater than). - /// - Logical operators: `Function.and`, `Function.or`, `Function.not`. - /// - Advanced functions: `Function.regexMatch`, `Function.arrayContains`. + /// Filter documents based on field values using `BooleanExpression` implementations, such as: + /// - Field comparators: `equal`, `lessThan`, `greaterThan`. + /// - Logical operators: `&&` (and), `||` (or), `!` (not). + /// - Advanced functions: `regexMatch`, `arrayContains`. /// /// ```swift /// // let pipeline: Pipeline = ... // Assume initial pipeline. /// let filteredPipeline = pipeline.where( - /// Field("rating").gt(4.0) // Rating greater than 4.0. - /// && Field("genre").eq("Science Fiction") // Genre is "Science Fiction". + /// Field("rating").greaterThan(4.0) // Rating greater than 4.0. + /// && Field("genre").equal("Science Fiction") // Genre is "Science Fiction". /// ) /// // let results = try await filteredPipeline.execute() /// ``` /// - /// - Parameter condition: The `BooleanExpr` to apply. + /// - Parameter condition: The `BooleanExpression` to apply. /// - Returns: A new `Pipeline` object with this stage appended. - public func `where`(_ condition: BooleanExpr) -> Pipeline { + public func `where`(_ condition: BooleanExpression) -> Pipeline { return Pipeline(stages: stages + [Where(condition: condition)], db: db) } @@ -287,7 +265,7 @@ public struct Pipeline: @unchecked Sendable { /// // let pipeline: Pipeline = ... // Assume initial pipeline, possibly sorted. /// // Retrieve the second page of 20 results (skip first 20, limit to next 20). /// let pagedPipeline = pipeline - /// .sort(Ascending("published")) // Example sort. + /// .sort(Field("published").ascending()) // Example sort. /// .offset(20) // Skip the first 20 results. /// .limit(20) // Take the next 20 results. /// // let results = try await pagedPipeline.execute() @@ -336,11 +314,10 @@ public struct Pipeline: @unchecked Sendable { /// // let results = try await distinctAuthorsGenresPipeline.execute() /// ``` /// - /// - Parameter group: The name of the first field for distinct value combinations. - /// - Parameter additionalGroups: Optional additional field names. + /// - Parameter groups: An array of at least one field name for distinct value combinations. /// - Returns: A new `Pipeline` object with this stage appended. - public func distinct(_ group: String, _ additionalGroups: String...) -> Pipeline { - let selections = ([group] + additionalGroups).map { Field($0) } + public func distinct(_ groups: [String]) -> Pipeline { + let selections = groups.map { Field($0) } return Pipeline(stages: stages + [Distinct(groups: selections)], db: db) } @@ -366,45 +343,12 @@ public struct Pipeline: @unchecked Sendable { /// // let results = try await distinctPipeline.execute() /// ``` /// - /// - Parameter group: The first `Selectable` expression to consider. - /// - Parameter additionalGroups: Optional additional `Selectable` expressions. + /// - Parameter groups: An array of at least one `Selectable` expression to consider. /// - Returns: A new `Pipeline` object with this stage appended. - public func distinct(_ group: Selectable, _ additionalGroups: Selectable...) -> Pipeline { - let groups = [group] + additionalGroups + public func distinct(_ groups: [Selectable]) -> Pipeline { return Pipeline(stages: stages + [Distinct(groups: groups)], db: db) } - /// Performs aggregation operations on all documents from previous stages. - /// - /// Computes aggregate values (e.g., sum, average, count) over the entire set of documents - /// from the previous stage. Aggregations are defined using `AggregateWithAlias`, - /// which pairs an `Accumulator` (e.g., `avg(Field("price"))`) with a result field name. - /// - /// ```swift - /// // let pipeline: Pipeline = ... // Assume pipeline from a "books" collection. - /// // Calculate the average rating and total number of books. - /// let aggregatedPipeline = pipeline.aggregate( - /// AggregateWithas(aggregate: avg(Field("rating")), alias: "averageRating"), - /// AggregateWithas(aggregate: countAll(), alias: "totalBooks") - /// ) - /// // let results = try await aggregatedPipeline.execute() - /// // results.documents might be: [["averageRating": 4.2, "totalBooks": 150]] - /// ``` - /// - /// - Parameter accumulator: The first `AggregateWithAlias` expression. - /// - Parameter additionalAccumulators: Optional additional `AggregateWithAlias` expressions. - /// - Returns: A new `Pipeline` object with this stage appended. - public func aggregate(_ accumulator: AggregateWithAlias, - _ additionalAccumulators: AggregateWithAlias...) -> Pipeline { - return Pipeline( - stages: stages + [Aggregate( - accumulators: [accumulator] + additionalAccumulators, - groups: nil // No grouping: aggregate over all documents. - )], - db: db - ) - } - /// Performs optionally grouped aggregation operations on documents from previous stages. /// /// Calculates aggregate values, optionally grouping documents by fields or `Selectable` @@ -419,7 +363,7 @@ public struct Pipeline: @unchecked Sendable { /// // let pipeline: Pipeline = ... // Assume pipeline from "books" collection. /// // Calculate the average rating for each genre. /// let groupedAggregationPipeline = pipeline.aggregate( - /// [AggregateWithas(aggregate: avg(Field("rating")), alias: "avg_rating")], + /// [AggregateWithas(aggregate: average(Field("rating")), alias: "avg_rating")], /// groups: [Field("genre")] // Group by the "genre" field. /// ) /// // let results = try await groupedAggregationPipeline.execute() @@ -431,46 +375,13 @@ public struct Pipeline: @unchecked Sendable { /// ``` /// /// - Parameters: - /// - accumulator: An array of `AggregateWithAlias` expressions for calculations. + /// - aggregates: An array of at least one `AliasedAggregate` expression for calculations. /// - groups: Optional array of `Selectable` expressions for grouping. If `nil` or empty, /// aggregates across all documents. /// - Returns: A new `Pipeline` object with this stage appended. - public func aggregate(_ accumulator: [AggregateWithAlias], + public func aggregate(_ aggregates: [AliasedAggregate], groups: [Selectable]? = nil) -> Pipeline { - return Pipeline(stages: stages + [Aggregate(accumulators: accumulator, groups: groups)], db: db) - } - - /// Performs optionally grouped aggregation operations using field names for grouping. - /// - /// Similar to the other `aggregate` method, but `groups` are specified as an array of `String` - /// field names. - /// - /// ```swift - /// // let pipeline: Pipeline = ... // Assume pipeline from "books" collection. - /// // Count books for each publisher. - /// let groupedByPublisherPipeline = pipeline.aggregate( - /// [AggregateWithas(aggregate: countAll(), alias: "book_count")], - /// groups: ["publisher"] // Group by the "publisher" field name. - /// ) - /// // let results = try await groupedByPublisherPipeline.execute() - /// // results.documents might be: - /// // [ - /// // ["publisher": "Penguin", "book_count": 50], - /// // ["publisher": "HarperCollins", "book_count": 35] - /// // ] - /// ``` - /// - /// - Parameters: - /// - accumulator: An array of `AggregateWithAlias` expressions. - /// - groups: An optional array of `String` field names for grouping. - /// - Returns: A new `Pipeline` object with this stage appended. - public func aggregate(_ accumulator: [AggregateWithAlias], - groups: [String]? = nil) -> Pipeline { - let selectables = groups?.map { Field($0) } - return Pipeline( - stages: stages + [Aggregate(accumulators: accumulator, groups: selectables)], - db: db - ) + return Pipeline(stages: stages + [Aggregate(accumulators: aggregates, groups: groups)], db: db) } /// Performs a vector similarity search, ordering results by similarity. @@ -526,19 +437,16 @@ public struct Pipeline: @unchecked Sendable { /// ```swift /// // let pipeline: Pipeline = ... // Assume initial pipeline. /// // Sort books by rating (descending), then by title (ascending). - /// let sortedPipeline = pipeline.sort( - /// Ascending("rating"), - /// Descending("title") // or Field("title").ascending() for ascending. - /// ) + /// let sortedPipeline = pipeline.sort([ + /// Field("rating").descending(), + /// Field("title").ascending() + /// ]) /// // let results = try await sortedPipeline.execute() /// ``` /// - /// - Parameter ordering: The primary `Ordering` criterion. - /// - Parameter additionalOrdering: Optional additional `Ordering` criteria for secondary sorting, - /// etc. + /// - Parameter orderings: An array of at least one `Ordering` criterion. /// - Returns: A new `Pipeline` object with this stage appended. - public func sort(_ ordering: Ordering, _ additionalOrdering: Ordering...) -> Pipeline { - let orderings = [ordering] + additionalOrdering + public func sort(_ orderings: [Ordering]) -> Pipeline { return Pipeline(stages: stages + [Sort(orderings: orderings)], db: db) } @@ -562,7 +470,7 @@ public struct Pipeline: @unchecked Sendable { /// /// - Parameter expr: The `Expr` (typically a `Field`) that resolves to the nested map. /// - Returns: A new `Pipeline` object with this stage appended. - public func replace(with expr: Expr) -> Pipeline { + public func replace(with expr: Expression) -> Pipeline { return Pipeline(stages: stages + [ReplaceWith(expr: expr)], db: db) } @@ -637,18 +545,18 @@ public struct Pipeline: @unchecked Sendable { /// /// ```swift /// // let db: Firestore = ... - /// // let booksPipeline = db.collection("books").pipeline().select("title", "category") - /// // let magazinesPipeline = db.collection("magazines").pipeline().select("title", - /// Field("topic").as("category")) + /// // let booksPipeline = db.pipeline().collection("books").select(["title", "category"]) + /// // let magazinesPipeline = db.pipeline().collection("magazines").select(["title", + /// // Field("topic").as("category")]) /// /// // Emit documents from both "books" and "magazines" collections. - /// let combinedPipeline = booksPipeline.union(magazinesPipeline) + /// let combinedPipeline = booksPipeline.union(with: [magazinesPipeline]) /// // let results = try await combinedPipeline.execute() /// ``` /// - /// - Parameter other: The other `Pipeline` whose documents will be unioned. + /// - Parameter other: An array of at least one `Pipeline` whose documents will be unioned. /// - Returns: A new `Pipeline` object with this stage appended. - public func union(_ other: Pipeline) -> Pipeline { + public func union(with other: Pipeline) -> Pipeline { return Pipeline(stages: stages + [Union(other: other)], db: db) } @@ -719,7 +627,7 @@ public struct Pipeline: @unchecked Sendable { /// - params: An array of ordered, `Sendable` parameters for the stage. /// - options: Optional dictionary of named, `Sendable` parameters. /// - Returns: A new `Pipeline` object with this stage appended. - public func rawStage(name: String, params: [Sendable?], + public func rawStage(name: String, params: [Sendable], options: [String: Sendable]? = nil) -> Pipeline { return Pipeline( stages: stages + [RawStage(name: name, params: params, options: options)], diff --git a/Firestore/Swift/Source/SwiftAPI/Stages.swift b/Firestore/Swift/Source/SwiftAPI/Stages.swift index 9f6d071d9ff..8bc52927641 100644 --- a/Firestore/Swift/Source/SwiftAPI/Stages.swift +++ b/Firestore/Swift/Source/SwiftAPI/Stages.swift @@ -116,9 +116,9 @@ class Where: Stage { let name: String = "where" let bridge: StageBridge - private var condition: BooleanExpr + private var condition: BooleanExpression - init(condition: BooleanExpr) { + init(condition: BooleanExpression) { self.condition = condition bridge = WhereStageBridge(expr: condition.toBridge()) } @@ -154,16 +154,16 @@ class Offset: Stage { class AddFields: Stage { let name: String = "addFields" let bridge: StageBridge - private var fields: [Selectable] + private var selectables: [Selectable] - init(fields: [Selectable]) { - self.fields = fields - let objc_accumulators = fields.reduce(into: [String: ExprBridge]()) { + init(selectables: [Selectable]) { + self.selectables = selectables + let objc_accumulators = selectables.reduce(into: [String: ExprBridge]()) { result, - field + selectable in - let seletable = field as! SelectableWrapper - result[seletable.alias] = seletable.expr.toBridge() + let selectableWrapper = selectable as! SelectableWrapper + result[selectableWrapper.alias] = selectableWrapper.expr.toBridge() } bridge = AddFieldsStageBridge(fields: objc_accumulators) } @@ -218,10 +218,10 @@ class Distinct: Stage { class Aggregate: Stage { let name: String = "aggregate" let bridge: StageBridge - private var accumulators: [AggregateWithAlias] - private var groups: [String: Expr] = [:] + private var accumulators: [AliasedAggregate] + private var groups: [String: Expression] = [:] - init(accumulators: [AggregateWithAlias], groups: [Selectable]?) { + init(accumulators: [AliasedAggregate], groups: [Selectable]?) { self.accumulators = accumulators if groups != nil { self.groups = Helper.selectablesToMap(selectables: groups!) @@ -283,9 +283,9 @@ class Sort: Stage { class ReplaceWith: Stage { let name: String = "replaceWith" let bridge: StageBridge - private var expr: Expr + private var expr: Expression - init(expr: Expr) { + init(expr: Expression) { self.expr = expr bridge = ReplaceWithStageBridge(expr: expr.toBridge()) } @@ -327,8 +327,8 @@ class Union: Stage { class Unnest: Stage { let name: String = "unnest" let bridge: StageBridge - private var alias: Expr - private var field: Expr + private var alias: Expression + private var field: Expression private var indexField: String? init(field: Selectable, indexField: String? = nil) { @@ -349,10 +349,10 @@ class Unnest: Stage { class RawStage: Stage { let name: String let bridge: StageBridge - private var params: [Sendable?] + private var params: [Sendable] private var options: [String: Sendable]? - init(name: String, params: [Sendable?], options: [String: Sendable]? = nil) { + init(name: String, params: [Sendable], options: [String: Sendable]? = nil) { self.name = name self.params = params self.options = options diff --git a/Firestore/Swift/Tests/Integration/PipelineApiTests.swift b/Firestore/Swift/Tests/Integration/PipelineApiTests.swift index f712cceca1f..e1d8111715b 100644 --- a/Firestore/Swift/Tests/Integration/PipelineApiTests.swift +++ b/Firestore/Swift/Tests/Integration/PipelineApiTests.swift @@ -17,7 +17,7 @@ import XCTest import FirebaseFirestore -final class PipelineTests: FSTIntegrationTestCase { +final class PipelineApiTests: FSTIntegrationTestCase { override func setUp() { FSTIntegrationTestCase.switchToEnterpriseMode() super.setUp() @@ -45,10 +45,8 @@ final class PipelineTests: FSTIntegrationTestCase { func testWhereStage() async throws { _ = db.pipeline().collection("books") .where( - Field("rating").gt(4.0) && Field("genre").eq("Science Fiction") || ArrayContains( - fieldName: "fieldName", - values: "rating" - ) + Field("rating").greaterThan(4.0) && Field("genre").equal("Science Fiction") || Field("tags") + .arrayContains("comedy") ) } @@ -59,15 +57,15 @@ final class PipelineTests: FSTIntegrationTestCase { // { title: 'title3', price: 5, discount: 0.66 } // An expression that will compute price from the value of msrp field and discount field - let priceExpr: FunctionExpr = Field("msrp").multiply(Field("discount")) + let priceExpr: FunctionExpression = Field("msrp").multiply(Field("discount")) // An expression becomes a Selectable when given an alias. In this case // the alias is 'salePrice' - let priceSelectableExpr: Selectable = priceExpr.as("salePrice") + let priceSelectableExpr: AliasedExpression = priceExpr.as("salePrice") _ = db.pipeline().collection("books") .addFields( - priceSelectableExpr // Add field `salePrice` based computed from msrp and discount + [priceSelectableExpr] // Add field `salePrice` based computed from msrp and discount ) // We don't expect customers to separate the Expression definition from the @@ -76,8 +74,9 @@ final class PipelineTests: FSTIntegrationTestCase { // is to inline the Expr definition _ = db.pipeline().collection("books") .addFields( - Field("msrp").multiply(Field("discount")).as("salePrice"), - Field("author") + [ + Field("msrp").multiply(Field("discount")).as("salePrice"), + ] ) // Output @@ -88,10 +87,10 @@ final class PipelineTests: FSTIntegrationTestCase { func testRemoveFieldsStage() async throws { // removes field 'rating' and 'cost' from the previous stage outputs. - _ = db.pipeline().collection("books").removeFields("rating", "cost") + _ = db.pipeline().collection("books").removeFields(["rating", "cost"]) // removes field 'rating'. - _ = db.pipeline().collection("books").removeFields(Field("rating")) + _ = db.pipeline().collection("books").removeFields(["rating"]) } func testSelectStage() async throws { @@ -103,11 +102,13 @@ final class PipelineTests: FSTIntegrationTestCase { // Overload for string and Selectable _ = db.pipeline().collection("books") .select( - Field("title"), // Field class inheritates Selectable - Field("msrp").multiply(Field("discount")).as("salePrice") + [ + Field("title"), // Field class inheritates Selectable + Field("msrp").multiply(Field("discount")).as("salePrice"), + ] ) - _ = db.pipeline().collection("books").select("title", "author") + _ = db.pipeline().collection("books").select(["title", "author"]) // Output // { title: 'title1', salePrice: 8.0}, @@ -120,22 +121,24 @@ final class PipelineTests: FSTIntegrationTestCase { // with the same rating _ = db.pipeline().collection("books") .sort( - Field("rating").descending(), - Ascending("title") // alternative API offered + [ + Field("rating").descending(), + Field("title").ascending(), // alternative API offered + ] ) } func testLimitStage() async throws { // Limit the results to the top 10 highest-rated books _ = db.pipeline().collection("books") - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) .limit(10) } func testOffsetStage() async throws { // Retrieve the second page of 20 results _ = db.pipeline().collection("books") - .sort(Field("published").descending()) + .sort([Field("published").descending()]) .offset(20) // Skip the first 20 results. Note that this must come // before .limit(...) unlike in Query where the order did not matter. .limit(20) // Take the next 20 results @@ -150,8 +153,10 @@ final class PipelineTests: FSTIntegrationTestCase { // Get a list of unique author names in uppercase and genre combinations. _ = db.pipeline().collection("books") .distinct( - Field("author").uppercased().as("authorName"), - Field("genre") + [ + Field("author").uppercased().as("authorName"), + Field("genre"), + ] ) // Output @@ -168,8 +173,10 @@ final class PipelineTests: FSTIntegrationTestCase { // Calculate the average rating and the total number of books _ = db.pipeline().collection("books") .aggregate( - Field("rating").avg().as("averageRating"), - CountAll().as("totalBooks") + [ + Field("rating").average().as("averageRating"), + CountAll().as("totalBooks"), + ] ) // Output @@ -183,10 +190,10 @@ final class PipelineTests: FSTIntegrationTestCase { // Calculate the average rating and the total number of books and group by field 'genre' _ = db.pipeline().collection("books") .aggregate([ - Field("rating").avg().as("averageRating"), + Field("rating").average().as("averageRating"), CountAll().as("totalBooks"), ], - groups: ["genre"]) + groups: [Field("genre")]) // Output // { genre: 'genreA', totalBooks: 1, averageRating: 5.0 } @@ -204,11 +211,11 @@ final class PipelineTests: FSTIntegrationTestCase { func testReplaceStage() async throws { // Input. // { -// "name": "John Doe Jr.", -// "parents": { -// "father": "John Doe Sr.", -// "mother": "Jane Doe" -// } + // "name": "John Doe Jr.", + // "parents": { + // "father": "John Doe Sr.", + // "mother": "Jane Doe" + // } // } // Emit field parents as the document. @@ -217,8 +224,8 @@ final class PipelineTests: FSTIntegrationTestCase { // Output // { -// "father": "John Doe Sr.", -// "mother": "Jane Doe" + // "father": "John Doe Sr.", + // "mother": "Jane Doe" // } } @@ -233,7 +240,7 @@ final class PipelineTests: FSTIntegrationTestCase { func testUnionStage() async throws { // Emit documents from books collection and magazines collection. _ = db.pipeline().collection("books") - .union(db.pipeline().collection("magazines")) + .union(with: db.pipeline().collection("magazines")) } func testUnnestStage() async throws { @@ -268,102 +275,109 @@ final class PipelineTests: FSTIntegrationTestCase { // add this stage by calling rawStage, passing the name of the stage "where", // and providing positional argument values. _ = db.pipeline().collection("books") - .rawStage(name: "where", - params: [Field("published").lt(1900)]) - .select("title", "author") + .rawStage( + name: "where", + params: [Field("published").lessThan(1900)] + ) + .select(["title", "author"]) // In cases where the stage also supports named argument values, then these can be // provided with a third argument that maps the argument name to value. // Note that these named arguments are always optional in the stage definition. _ = db.pipeline().collection("books") - .rawStage(name: "where", - params: [Field("published").lt(1900)], - options: ["someOptionalParamName": "the argument value for this param"]) - .select("title", "author") + .rawStage( + name: "where", + params: [Field("published").lessThan(1900)], + options: ["someOptionalParamName": "the argument value for this param"] + ) + .select(["title", "author"]) } func testField() async throws { // An expression that will return the value of the field `name` in the document - let nameField = Field("name") + _ = Field("name") // An expression that will return the value of the field `description` in the document // Field is a sub-type of Expr, so we can also declare our var of type Expr - let descriptionField: Expr = Field("description") + _ = Field("description") // USAGE: anywhere an Expr type is accepted // Use a field in a pipeline _ = db.pipeline().collection("books") .addFields( - Field("rating").as("bookRating") // Duplicate field 'rating' as 'bookRating' + [ + Field("rating").as("bookRating"), // Duplicate field 'rating' as 'bookRating' + ] ) // One special Field value is conveniently exposed as static function to help the user reference // reserved field values of __name__. - _ = db.pipeline().collection("books") - .addFields( - DocumentId() - ) + // TBD + // _ = db.pipeline().collection("books") + // .addFields( + // FieldPath.documentID() + // ) } func testConstant() async throws { // A constant for a number - let three = Constant(3) + _ = Constant(3) // A constant for a string - let name = Constant("Expressions API") + _ = Constant("Expressions API") // Const is a sub-type of Expr, so we can also declare our var of type Expr - let nothing: Expr = Constant.nil + _ = Constant.nil // USAGE: Anywhere an Expr type is accepted // Add field `fromTheLibraryOf: 'Rafi'` to every document in the collection. _ = db.pipeline().collection("books") - .addFields(Constant("Rafi").as("fromTheLibraryOf")) + .addFields([Constant("Rafi").as("fromTheLibraryOf")]) } func testFunctionExpr() async throws { let secondsField = Field("seconds") // Create a FunctionExpr using the multiply function to compute milliseconds - let milliseconds: FunctionExpr = secondsField.multiply(1000) + let milliseconds: FunctionExpression = secondsField.multiply(1000) // A firestore function is also a sub-type of Expr - let myExpr: Expr = milliseconds + _ = milliseconds } func testBooleanExpr() async throws { - let isApple: BooleanExpr = Field("type").eq("apple") + let isApple: BooleanExpression = Field("type").equal("apple") // USAGE: stage where requires an expression of type BooleanExpr - let allAppleOptions: Pipeline = db.pipeline().collection("fruitOptions").where(isApple) + let _: Pipeline = db.pipeline().collection("fruitOptions").where(isApple) } func testSelectableExpr() async throws { let secondsField = Field("seconds") // Create a selectable from our milliseconds expression. - let millisecondsSelectable: Selectable = secondsField.multiply(1000).as("milliseconds") + let _: Selectable = secondsField.multiply(1000).as("milliseconds") // USAGE: stages addFields and select accept expressions of type Selectable // Add (or overwrite) the 'milliseconds` field to each of our documents using the // `.addFields(...)` stage. _ = db.pipeline().collection("lapTimes") - .addFields(secondsField.multiply(1000).as("milliseconds")) + .addFields([secondsField.multiply(1000).as("milliseconds")]) // NOTE: Field implements Selectable, the alias is the same as the name - let secondsSelectable: Selectable = secondsField + let _: Selectable = secondsField } func testAggregateExpr() async throws { let lapTimeSum: AggregateFunction = Field("seconds").sum() - let lapTimeSumTarget: AggregateWithAlias = lapTimeSum.as("totalTrackTime") + let _: AliasedAggregate = lapTimeSum.as("totalTrackTime") // USAGE: stage aggregate accepts expressions of type AggregateWithAlias // A pipeline that will return one document with one field `totalTrackTime` that // is the sum of all laps ever taken on the track. _ = db.pipeline().collection("lapTimes") - .aggregate(lapTimeSum.as("totalTrackTime")) + .aggregate([lapTimeSum.as("totalTrackTime")]) } func testOrdering() async throws { @@ -371,34 +385,34 @@ final class PipelineTests: FSTIntegrationTestCase { // USAGE: stage sort accepts objects of type Ordering // Use this ordering to sort our lap times collection from fastest to slowest - _ = db.pipeline().collection("lapTimes").sort(fastestToSlowest) + _ = db.pipeline().collection("lapTimes").sort([fastestToSlowest]) } func testExpr() async throws { // An expression that computes the area of a circle // by chaining together two calls to the multiply function - let radiusField: Expr = Field("radius") - let radiusSq: Expr = radiusField.multiply(Field("radius")) - let areaExpr: Expr = radiusSq.multiply(3.14) + let radiusField = Field("radius") + let radiusSq = radiusField.multiply(Field("radius")) + _ = radiusSq.multiply(3.14) // Or define this expression in one clean, fluent statement - let areaOfCircle: Selectable = Field("radius") + let areaOfCircle = Field("radius") .multiply(Field("radius")) .multiply(3.14) .as("area") // And pass the expression to a Pipeline for evaluation - _ = db.pipeline().collection("circles").addFields(areaOfCircle) + _ = db.pipeline().collection("circles").addFields([areaOfCircle]) } func testGeneric() async throws { // This is the same of the logicalMin('price', 0)', if it did not exist - let myLm = FunctionExpr("logicalMin", [Field("price"), Constant(0)]) + _ = FunctionExpression("logicalMin", [Field("price"), Constant(0)]) // Create a generic BooleanExpr for use where BooleanExpr is required - let myEq = BooleanExpr("eq", [Field("price"), Constant(10)]) + _ = BooleanExpression("eq", [Field("price"), Constant(10)]) // Create a generic AggregateFunction for use where AggregateFunction is required - let mySum = AggregateFunction("sum", [Field("price")]) + _ = AggregateFunction("sum", [Field("price")]) } } diff --git a/Firestore/Swift/Tests/Integration/PipelineTests.swift b/Firestore/Swift/Tests/Integration/PipelineTests.swift index f05a7dcc9eb..77ed1fd818f 100644 --- a/Firestore/Swift/Tests/Integration/PipelineTests.swift +++ b/Firestore/Swift/Tests/Integration/PipelineTests.swift @@ -267,7 +267,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .aggregate(Field("rating").avg().as("avgRating")) + .aggregate([Field("rating").average().as("avgRating")]) let snapshot = try await pipeline.execute() XCTAssertEqual(snapshot.results.count, 1, "Aggregate query should return a single result") @@ -283,8 +283,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .aggregate( - [Field("rating").avg().as("avgRating")], - groups: ["genre"] + [Field("rating").average().as("avgRating")], + groups: [Field("genre")] ) // Make sure 'groupBy' and 'average' are correct let snapshot = try await pipeline.execute() @@ -395,7 +395,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collectionGroup(randomSubCollectionId) - .sort(Field("order").ascending()) + .sort([Field("order").ascending()]) let snapshot = try await pipeline.execute() @@ -447,8 +447,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .database() // Source is the entire database - .where(Field("randomId").eq(randomIDValue)) - .sort(Ascending("order")) + .where(Field("randomId").equal(randomIDValue)) + .sort([Field("order").ascending()]) let snapshot = try await pipeline.execute() // We expect 3 documents: docA, docB, and docE (from sub-sub-collection) @@ -580,7 +580,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { .select( constantsFirst + constantsSecond ) - let snapshot = try await pipeline.execute() + let snapshot: PipelineSnapshot = try await pipeline.execute() TestHelper.compare(pipelineResult: snapshot.results.first!, expected: expectedResultsMap) } @@ -644,18 +644,18 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) .limit(1) // This should pick "The Lord of the Rings" (rating 4.7) - .select( + .select([ Field("title"), Field("author"), Field("genre"), Field("rating"), Field("published"), Field("tags"), - Field("awards") - ) - .addFields( + Field("awards"), + ]) + .addFields([ ArrayExpression([ 1, 2, @@ -669,11 +669,11 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { "rating": Field("rating").multiply(10), "nestedArray": ArrayExpression([Field("title")]), "nestedMap": MapExpression(["published": Field("published")]), - ]).as("metadata") - ) + ]).as("metadata"), + ]) .where( - Field("metadataArray").eq(metadataArrayElements) && - Field("metadata").eq(metadataMapElements) + Field("metadataArray").equal(metadataArrayElements) && + Field("metadata").equal(metadataMapElements) ) let snapshot = try await pipeline.execute() @@ -705,7 +705,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { var pipeline = db.pipeline() .collection(collRef.path) - .aggregate(CountAll().as("count")) + .aggregate([CountAll().as("count")]) var snapshot = try await pipeline.execute() XCTAssertEqual(snapshot.results.count, 1, "Count all should return a single aggregate document") @@ -717,12 +717,12 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { pipeline = db.pipeline() .collection(collRef.path) - .where(Field("genre").eq("Science Fiction")) - .aggregate( + .where(Field("genre").equal("Science Fiction")) + .aggregate([ CountAll().as("count"), - Field("rating").avg().as("avgRating"), - Field("rating").maximum().as("maxRating") - ) + Field("rating").average().as("avgRating"), + Field("rating").maximum().as("maxRating"), + ]) snapshot = try await pipeline.execute() XCTAssertEqual(snapshot.results.count, 1, "Filtered aggregate should return a single document") @@ -748,8 +748,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { do { _ = try await db.pipeline() .collection(collRef.path) - .where(Field("published").lt(1900)) - .aggregate([], groups: ["genre"]) + .where(Field("published").lessThan(1900)) + .aggregate([], groups: [Field("genre")]) .execute() XCTFail( @@ -767,13 +767,13 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .where(Field("published").lt(1984)) + .where(Field("published").lessThan(1984)) .aggregate( - [Field("rating").avg().as("avgRating")], - groups: ["genre"] + [Field("rating").average().as("avgRating")], + groups: [Field("genre")] ) - .where(Field("avgRating").gt(4.3)) - .sort(Field("avgRating").descending()) + .where(Field("avgRating").greaterThan(4.3)) + .sort([Field("avgRating").descending()]) let snapshot = try await pipeline.execute() @@ -799,12 +799,12 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .aggregate( + .aggregate([ Field("cost").count().as("booksWithCost"), CountAll().as("count"), Field("rating").maximum().as("maxRating"), - Field("published").minimum().as("minPublished") - ) + Field("published").minimum().as("minPublished"), + ]) let snapshot = try await pipeline.execute() @@ -830,12 +830,12 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let expectedCount = 3 let expectedResults: [String: Sendable] = ["count": expectedCount] - let condition = Field("rating").gt(4.3) + let condition = Field("rating").greaterThan(4.3) - var pipeline = db.pipeline() + let pipeline = db.pipeline() .collection(collRef.path) - .aggregate(condition.countIf().as("count")) - var snapshot = try await pipeline.execute() + .aggregate([condition.countIf().as("count")]) + let snapshot = try await pipeline.execute() XCTAssertEqual(snapshot.results.count, 1, "countIf aggregate should return a single document") if let result = snapshot.results.first { @@ -851,8 +851,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .distinct(Field("genre"), Field("author")) - .sort(Field("genre").ascending(), Field("author").ascending()) + .distinct([Field("genre"), Field("author")]) + .sort([Field("genre").ascending(), Field("author").ascending()]) let snapshot = try await pipeline.execute() @@ -880,8 +880,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .select(Field("title"), Field("author")) - .sort(Field("author").ascending()) + .select([Field("title"), Field("author")]) + .sort([Field("author").ascending()]) let snapshot = try await pipeline.execute() @@ -913,9 +913,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .select(Field("title"), Field("author")) - .addFields(Constant("bar").as("foo")) - .sort(Field("author").ascending()) + .select([Field("title"), Field("author")]) + .addFields([Constant("bar").as("foo")]) + .sort([Field("author").ascending()]) let snapshot = try await pipeline.execute() @@ -947,9 +947,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .select(Field("title"), Field("author")) - .sort(Field("author").ascending()) // Sort before removing the 'author' field - .removeFields(Field("author")) + .select([Field("title"), Field("author")]) + .sort([Field("author").ascending()]) // Sort before removing the 'author' field + .removeFields(["author"]) let snapshot = try await pipeline.execute() @@ -983,8 +983,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { // Test Case 1: Two AND conditions var pipeline = db.pipeline() .collection(collRef.path) - .where(Field("rating").gt(4.5) - && Field("genre").eqAny(["Science Fiction", "Romance", "Fantasy"])) + .where(Field("rating").greaterThan(4.5) + && Field("genre").equalAny(["Science Fiction", "Romance", "Fantasy"])) var snapshot = try await pipeline.execute() var expectedIDs = ["book10", "book4"] // Dune (SF, 4.6), LOTR (Fantasy, 4.7) TestHelper.compare(pipelineSnapshot: snapshot, expectedIDs: expectedIDs, enforceOrder: false) @@ -993,9 +993,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { pipeline = db.pipeline() .collection(collRef.path) .where( - Field("rating").gt(4.5) - && Field("genre").eqAny(["Science Fiction", "Romance", "Fantasy"]) - && Field("published").lt(1965) + Field("rating").greaterThan(4.5) + && Field("genre").equalAny(["Science Fiction", "Romance", "Fantasy"]) + && Field("published").lessThan(1965) ) snapshot = try await pipeline.execute() expectedIDs = ["book4"] // LOTR (Fantasy, 4.7, published 1954) @@ -1010,12 +1010,12 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { var pipeline = db.pipeline() .collection(collRef.path) .where( - Field("genre").eq("Romance") - || Field("genre").eq("Dystopian") - || Field("genre").eq("Fantasy") + Field("genre").equal("Romance") + || Field("genre").equal("Dystopian") + || Field("genre").equal("Fantasy") ) - .select(Field("title")) - .sort(Field("title").ascending()) + .select([Field("title")]) + .sort([Field("title").ascending()]) var snapshot = try await pipeline.execute() var expectedResults: [[String: Sendable]] = [ @@ -1037,13 +1037,13 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { pipeline = db.pipeline() .collection(collRef.path) .where( - Field("genre").eq("Romance") // Book2 (T), Book5 (F), Book4 (F), Book8 (F) - ^ Field("genre").eq("Dystopian") // Book2 (F), Book5 (T), Book4 (F), Book8 (T) - ^ Field("genre").eq("Fantasy") // Book2 (F), Book5 (F), Book4 (T), Book8 (F) - ^ Field("published").eq(1949) // Book2 (F), Book5 (F), Book4 (F), Book8 (T) + Field("genre").equal("Romance") // Book2 (T), Book5 (F), Book4 (F), Book8 (F) + ^ Field("genre").equal("Dystopian") // Book2 (F), Book5 (T), Book4 (F), Book8 (T) + ^ Field("genre").equal("Fantasy") // Book2 (F), Book5 (F), Book4 (T), Book8 (F) + ^ Field("published").equal(1949) // Book2 (F), Book5 (F), Book4 (F), Book8 (T) ) - .select(Field("title")) - .sort(Field("title").ascending()) + .select([Field("title")]) + .sort([Field("title").ascending()]) snapshot = try await pipeline.execute() @@ -1067,10 +1067,10 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("author").ascending()) + .sort([Field("author").ascending()]) .offset(5) .limit(3) - .select("title", "author") + .select(["title", "author"]) let snapshot = try await pipeline.execute() @@ -1104,10 +1104,10 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .rawStage(name: "select", params: selectParameters) - .sort(Field("title").ascending()) + .sort([Field("title").ascending()]) .limit(1) - let snapshot = try await pipeline.execute() + let snapshot: PipelineSnapshot = try await pipeline.execute() XCTAssertEqual(snapshot.results.count, 1, "Should retrieve one document") TestHelper.compare( @@ -1123,17 +1123,17 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("author").ascending()) + .sort([Field("author").ascending()]) .limit(1) - .select("title", "author") + .select(["title", "author"]) .rawStage( name: "add_fields", params: [ [ - "display": Field("title").strConcat( + "display": Field("title").strConcat([ Constant(" - "), - Field("author") - ), + Field("author"), + ]), ], ] ) @@ -1159,14 +1159,14 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .select("title", "author", "rating") + .select(["title", "author", "rating"]) .rawStage( name: "distinct", params: [ ["rating": Field("rating")], ] ) - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) let snapshot = try await pipeline.execute() @@ -1193,12 +1193,12 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .select("title", "author", "rating") + .select(["title", "author", "rating"]) .rawStage( name: "aggregate", params: [ [ - "averageRating": Field("rating").avg(), + "averageRating": Field("rating").average(), ], emptySendableDictionary, ] @@ -1223,10 +1223,10 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .select("title", "author") + .select(["title", "author"]) .rawStage( name: "where", - params: [Field("author").eq("Douglas Adams")] + params: [Field("author").equal("Douglas Adams")] ) let snapshot = try await pipeline.execute() @@ -1249,7 +1249,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .select("title", "author") + .select(["title", "author"]) .rawStage( name: "sort", params: [ @@ -1284,7 +1284,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .where(Field("title").eq("The Hitchhiker's Guide to the Galaxy")) + .where(Field("title").equal("The Hitchhiker's Guide to the Galaxy")) .replace(with: "awards") let snapshot = try await pipeline.execute() @@ -1311,7 +1311,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .where(Field("title").eq("The Hitchhiker's Guide to the Galaxy")) + .where(Field("title").equal("The Hitchhiker's Guide to the Galaxy")) .replace(with: MapExpression([ "foo": "bar", @@ -1372,9 +1372,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .union(db.pipeline() + .union(with: db.pipeline() .collection(collRef.path)) - .sort(Field(FieldPath.documentID()).ascending()) + .sort([Field(FieldPath.documentID()).ascending()]) let snapshot = try await pipeline.execute() @@ -1409,9 +1409,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .where(Field("title").eq("The Hitchhiker's Guide to the Galaxy")) + .where(Field("title").equal("The Hitchhiker's Guide to the Galaxy")) .unnest(Field("tags").as("tag"), indexField: "tagsIndex") - .select( + .select([ "title", "author", "genre", @@ -1420,8 +1420,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { "tags", "tag", "awards", - "nestedField" - ) + "nestedField", + ]) let snapshot = try await pipeline.execute() @@ -1470,9 +1470,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .where(Field("title").eq("The Hitchhiker's Guide to the Galaxy")) + .where(Field("title").equal("The Hitchhiker's Guide to the Galaxy")) .unnest(ArrayExpression([1, 2, 3]).as("copy")) - .select( + .select([ "title", "author", "genre", @@ -1481,8 +1481,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { "tags", "copy", "awards", - "nestedField" - ) + "nestedField", + ]) let snapshot = try await pipeline.execute() @@ -1544,7 +1544,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { vectorValue: [10, 1, 3, 1, 2, 1, 1, 1, 1, 1], distanceMeasure: measure, limit: 3 ) - .select("title") + .select(["title"]) let snapshot = try await pipeline.execute() TestHelper.compare(pipelineSnapshot: snapshot, expected: expectedResults, enforceOrder: true) } @@ -1573,7 +1573,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { distanceMeasure: .euclidean, limit: 2, distanceField: "computedDistance" ) - .select("title", "computedDistance") + .select(["title", "computedDistance"]) let snapshot = try await pipeline.execute() TestHelper.compare(pipelineSnapshot: snapshot, expected: expectedResults, enforceOrder: false) } @@ -1584,11 +1584,11 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .select( + .select([ Field("title"), - Field("published").logicalMaximum(Constant(1960), 1961).as("published-safe") - ) - .sort(Field("title").ascending()) + Field("published").logicalMaximum([Constant(1960), 1961]).as("published-safe"), + ]) + .sort([Field("title").ascending()]) .limit(3) let snapshot = try await pipeline.execute() @@ -1608,14 +1608,14 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .select( + .select([ Field("title"), - Field("published").logicalMinimum(Constant(1960), 1961).as("published-safe") - ) - .sort(Field("title").ascending()) + Field("published").logicalMinimum([Constant(1960), 1961]).as("published-safe"), + ]) + .sort([Field("title").ascending()]) .limit(3) - let snapshot = try await pipeline.execute() + let snapshot: PipelineSnapshot = try await pipeline.execute() let expectedResults: [[String: Sendable]] = [ ["title": "1984", "published-safe": 1949], @@ -1632,12 +1632,12 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .select( + .select([ Field("title"), - Field("published").lt(1960).then(Constant(1960), else: Field("published")) - .as("published-safe") - ) - .sort(Field("title").ascending()) + Field("published").lessThan(1960).then(Constant(1960), else: Field("published")) + .as("published-safe"), + ]) + .sort([Field("title").ascending()]) .limit(3) let snapshot = try await pipeline.execute() @@ -1651,17 +1651,17 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { TestHelper.compare(pipelineSnapshot: snapshot, expected: expectedResults, enforceOrder: true) } - func testEqAnyWorks() async throws { + func testInWorks() async throws { let collRef = collectionRef(withDocuments: bookDocs) let db = collRef.firestore let pipeline = db.pipeline() .collection(collRef.path) - .where(Field("published").eqAny([1979, 1999, 1967])) - .sort(Field("title").descending()) - .select("title") + .where(Field("published").equalAny([1979, 1999, 1967])) + .sort([Field("title").descending()]) + .select(["title"]) - let snapshot = try await pipeline.execute() + let snapshot: PipelineSnapshot = try await pipeline.execute() let expectedResults: [[String: Sendable]] = [ ["title": "The Hitchhiker's Guide to the Galaxy"], @@ -1677,8 +1677,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .where(Field("published").notEqAny([1965, 1925, 1949, 1960, 1866, 1985, 1954, 1967, 1979])) - .select("title") + .where(Field("published") + .notEqualAny([1965, 1925, 1949, 1960, 1866, 1985, 1954, 1967, 1979])) + .select(["title"]) let snapshot = try await pipeline.execute() @@ -1696,7 +1697,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .where(Field("tags").arrayContains("comedy")) - .select("title") + .select(["title"]) let snapshot = try await pipeline.execute() @@ -1714,8 +1715,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .where(Field("tags").arrayContainsAny(["comedy", "classic"])) - .sort(Field("title").descending()) - .select("title") + .sort([Field("title").descending()]) + .select(["title"]) let snapshot = try await pipeline.execute() @@ -1734,7 +1735,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .where(Field("tags").arrayContainsAll(["adventure", "magic"])) - .select("title") + .select(["title"]) let snapshot = try await pipeline.execute() @@ -1751,8 +1752,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .select(Field("tags").arrayLength().as("tagsCount")) - .where(Field("tagsCount").eq(3)) + .select([Field("tags").arrayLength().as("tagsCount")]) + .where(Field("tagsCount").equal(3)) let snapshot = try await pipeline.execute() @@ -1765,8 +1766,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("author").ascending()) - .select(Field("author").strConcat(Constant(" - "), Field("title")).as("bookInfo")) + .sort([Field("author").ascending()]) + .select([Field("author").strConcat([Constant(" - "), Field("title")]).as("bookInfo")]) .limit(1) let snapshot = try await pipeline.execute() @@ -1785,8 +1786,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .where(Field("title").startsWith("The")) - .select("title") - .sort(Field("title").ascending()) + .select(["title"]) + .sort([Field("title").ascending()]) let snapshot = try await pipeline.execute() @@ -1807,8 +1808,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .where(Field("title").endsWith("y")) - .select("title") - .sort(Field("title").descending()) + .select(["title"]) + .sort([Field("title").descending()]) let snapshot = try await pipeline.execute() @@ -1827,8 +1828,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .where(Field("title").strContains("'s")) - .select("title") - .sort(Field("title").ascending()) + .select(["title"]) + .sort([Field("title").ascending()]) let snapshot = try await pipeline.execute() @@ -1846,12 +1847,12 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .select( + .select([ Field("title").charLength().as("titleLength"), - Field("title") - ) - .where(Field("titleLength").gt(20)) - .sort(Field("title").ascending()) + Field("title"), + ]) + .where(Field("titleLength").greaterThan(20)) + .sort([Field("title").ascending()]) let snapshot = try await pipeline.execute() @@ -1872,7 +1873,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .where(Field("title").like("%Guide%")) - .select("title") + .select(["title"]) let snapshot = try await pipeline.execute() @@ -1915,16 +1916,16 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .where(Field("title").eq("To Kill a Mockingbird")) - .select( + .where(Field("title").equal("To Kill a Mockingbird")) + .select([ Field("rating").add(1).as("ratingPlusOne"), Field("published").subtract(1900).as("yearsSince1900"), Field("rating").multiply(10).as("ratingTimesTen"), Field("rating").divide(2).as("ratingDividedByTwo"), Field("rating").multiply(20).as("ratingTimes20"), Field("rating").add(3).as("ratingPlus3"), - Field("rating").mod(2).as("ratingMod2") - ) + Field("rating").mod(2).as("ratingMod2"), + ]) .limit(1) let snapshot = try await pipeline.execute() @@ -1954,12 +1955,12 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .where( - Field("rating").gt(4.2) && - Field("rating").lte(4.5) && - Field("genre").neq("Science Fiction") + Field("rating").greaterThan(4.2) && + Field("rating").lessThanOrEqualTo(4.5) && + Field("genre").notEqual("Science Fiction") ) - .select("rating", "title") - .sort(Field("title").ascending()) + .select(["rating", "title"]) + .sort([Field("title").ascending()]) let snapshot = try await pipeline.execute() @@ -1979,11 +1980,11 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .where( - (Field("rating").gt(4.5) && Field("genre").eq("Science Fiction")) || - Field("published").lt(1900) + (Field("rating").greaterThan(4.5) && Field("genre").equal("Science Fiction")) || + Field("published").lessThan(1900) ) - .select("title") - .sort(Field("title").ascending()) + .select(["title"]) + .sort([Field("title").ascending()]) let snapshot = try await pipeline.execute() @@ -2003,18 +2004,20 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { // Part 1 var pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) .limit(1) .select( - Field("rating").isNull().as("ratingIsNull"), - Field("rating").isNan().as("ratingIsNaN"), - Field("title").arrayGet(0).isError().as("isError"), - Field("title").arrayGet(0).ifError(Constant("was error")).as("ifError"), - Field("foo").isAbsent().as("isAbsent"), - Field("title").isNotNull().as("titleIsNotNull"), - Field("cost").isNotNan().as("costIsNotNan"), - Field("fooBarBaz").exists().as("fooBarBazExists"), - Field("title").exists().as("titleExists") + [ + Field("rating").isNil().as("ratingIsNull"), + Field("rating").isNan().as("ratingIsNaN"), + Field("title").arrayGet(0).isError().as("isError"), + Field("title").arrayGet(0).ifError(Constant("was error")).as("ifError"), + Field("foo").isAbsent().as("isAbsent"), + Field("title").isNotNil().as("titleIsNotNull"), + Field("cost").isNotNan().as("costIsNotNan"), + Field("fooBarBaz").exists().as("fooBarBazExists"), + Field("title").exists().as("titleExists"), + ] ) var snapshot = try await pipeline.execute() @@ -2040,16 +2043,18 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { // Part 2 pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) .limit(1) .select( - Field("rating").isNull().as("ratingIsNull"), - Field("rating").isNan().as("ratingIsNaN"), - Field("title").arrayGet(0).isError().as("isError"), - Field("title").arrayGet(0).ifError(Constant("was error")).as("ifError"), - Field("foo").isAbsent().as("isAbsent"), - Field("title").isNotNull().as("titleIsNotNull"), - Field("cost").isNotNan().as("costIsNotNan") + [ + Field("rating").isNil().as("ratingIsNull"), + Field("rating").isNan().as("ratingIsNaN"), + Field("title").arrayGet(0).isError().as("isError"), + Field("title").arrayGet(0).ifError(Constant("was error")).as("ifError"), + Field("foo").isAbsent().as("isAbsent"), + Field("title").isNotNil().as("titleIsNotNull"), + Field("cost").isNotNan().as("costIsNotNan"), + ] ) snapshot = try await pipeline.execute() @@ -2077,13 +2082,15 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("published").descending()) + .sort([Field("published").descending()]) .select( - Field("awards").mapGet("hugo").as("hugoAward"), - Field("awards").mapGet("others").as("others"), - Field("title") + [ + Field("awards").mapGet("hugo").as("hugoAward"), + Field("awards").mapGet("others").as("others"), + Field("title"), + ] ) - .where(Field("hugoAward").eq(true)) + .where(Field("hugoAward").equal(true)) let snapshot = try await pipeline.execute() @@ -2124,10 +2131,14 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(randomCol.path) .select( - Constant(VectorValue(sourceVector)).cosineDistance(targetVectorValue).as("cosineDistance"), - Constant(VectorValue(sourceVector)).dotProduct(targetVectorValue).as("dotProductDistance"), - Constant(VectorValue(sourceVector)).euclideanDistance(targetVectorValue) - .as("euclideanDistance") + [ + Constant(VectorValue(sourceVector)).cosineDistance(targetVectorValue) + .as("cosineDistance"), + Constant(VectorValue(sourceVector)).dotProduct(targetVectorValue) + .as("dotProductDistance"), + Constant(VectorValue(sourceVector)).euclideanDistance(targetVectorValue) + .as("euclideanDistance"), + ] ) .limit(1) @@ -2171,7 +2182,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .limit(1) // Limit to the document we just added - .select(Field("embedding").vectorLength().as("vectorLength")) + .select([Field("embedding").vectorLength().as("vectorLength")]) // Execute the pipeline let snapshot = try await pipeline.execute() @@ -2192,9 +2203,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .where(Field("awards.hugo").eq(true)) - .sort(Field("title").descending()) - .select(Field("title"), Field("awards.hugo")) + .where(Field("awards.hugo").equal(true)) + .sort([Field("title").descending()]) + .select([Field("title"), Field("awards.hugo")]) let snapshot = try await pipeline.execute() @@ -2212,13 +2223,13 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .where(Field("awards.hugo").eq(true)) // Filters to book1 and book10 - .select( + .where(Field("awards.hugo").equal(true)) // Filters to book1 and book10 + .select([ Field("title"), Field("nestedField.level.1"), - Field("nestedField").mapGet("level.1").mapGet("level.2").as("nested") - ) - .sort(Field("title").descending()) + Field("nestedField").mapGet("level.1").mapGet("level.2").as("nested"), + ]) + .sort([Field("title").descending()]) let snapshot = try await pipeline.execute() @@ -2249,12 +2260,14 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) .limit(1) .select( - FunctionExpr("add", [Field("rating"), Constant(1)]).as( - "rating" - ) + [ + FunctionExpression("add", [Field("rating"), Constant(1)]).as( + "rating" + ), + ] ) let snapshot = try await pipeline.execute() @@ -2279,11 +2292,11 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .where( - BooleanExpr("and", [Field("rating").gt(0), - Field("title").charLength().lt(5), - Field("tags").arrayContains("propaganda")]) + BooleanExpression("and", [Field("rating").greaterThan(0), + Field("title").charLength().lessThan(5), + Field("tags").arrayContains("propaganda")]) ) - .select("title") + .select(["title"]) let snapshot = try await pipeline.execute() @@ -2302,8 +2315,11 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .where(BooleanExpr("array_contains_any", [Field("tags"), ArrayExpression(["politics"])])) - .select(Field("title")) + .where(BooleanExpression( + "array_contains_any", + [Field("tags"), ArrayExpression(["politics"])] + )) + .select([Field("title")]) let snapshot = try await pipeline.execute() @@ -2322,7 +2338,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .aggregate(AggregateFunction("count_if", [Field("rating").gte(4.5)]).as("countOfBest")) + .aggregate([AggregateFunction("count_if", [Field("rating").greaterThanOrEqualTo(4.5)]) + .as("countOfBest")]) let snapshot = try await pipeline.execute() @@ -2346,11 +2363,13 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .sort( - FunctionExpr("char_length", [Field("title")]).ascending(), - Field("__name__").descending() + [ + FunctionExpression("char_length", [Field("title")]).ascending(), + Field("__name__").descending(), + ] ) .limit(3) - .select(Field("title")) + .select([Field("title")]) let snapshot = try await pipeline.execute() @@ -2372,7 +2391,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .limit(10) - .select(RandomExpr().as("result")) + .select([RandomExpression().as("result")]) let snapshot = try await pipeline.execute() @@ -2402,9 +2421,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) .limit(1) - .select(ArrayExpression([1, 2, 3, 4]).as("metadata")) + .select([ArrayExpression([1, 2, 3, 4]).as("metadata")]) let snapshot = try await pipeline.execute() @@ -2425,14 +2444,14 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) .limit(1) - .select(ArrayExpression([ + .select([ArrayExpression([ 1, 2, Field("genre"), Field("rating").multiply(10), - ]).as("metadata")) + ]).as("metadata")]) let snapshot = try await pipeline.execute() @@ -2459,9 +2478,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline1 = db.pipeline() .collection(collRef.path) - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) .limit(3) - .select(Field("tags").arrayGet(0).as("firstTag")) + .select([Field("tags").arrayGet(0).as("firstTag")]) let snapshot1 = try await pipeline1.execute() XCTAssertEqual(snapshot1.results.count, 3, "Part 1: Should retrieve three documents") @@ -2478,9 +2497,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) .limit(1) - .select(MapExpression(["foo": "bar"]).as("metadata")) + .select([MapExpression(["foo": "bar"]).as("metadata")]) let snapshot = try await pipeline.execute() @@ -2501,12 +2520,12 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) .limit(1) - .select(MapExpression([ + .select([MapExpression([ "genre": Field("genre"), // "Fantasy" "rating": Field("rating").multiply(10), // 4.7 * 10 = 47.0 - ]).as("metadata")) + ]).as("metadata")]) let snapshot = try await pipeline.execute() @@ -2530,9 +2549,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline2 = db.pipeline() .collection(collRef.path) - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) .limit(1) - .select(Field("awards").mapRemove("hugo").as("awards")) + .select([Field("awards").mapRemove("hugo").as("awards")]) let snapshot2 = try await pipeline2.execute() XCTAssertEqual(snapshot2.results.count, 1, "Should retrieve one document") @@ -2547,15 +2566,15 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let db = firestore() let collRef = collectionRef(withDocuments: bookDocs) - let expectedResult: [String: Sendable?] = + let expectedResult: [String: Sendable] = ["awards": ["hugo": false, "nebula": false, "fakeAward": true]] let mergeMap: [String: Sendable] = ["fakeAward": true] let pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) .limit(1) - .select(Field("awards").mapMerge(mergeMap).as("awards")) + .select([Field("awards").mapMerge([mergeMap]).as("awards")]) let snapshot = try await pipeline.execute() XCTAssertEqual(snapshot.results.count, 1, "Should retrieve one document") @@ -2576,7 +2595,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(randomCol.path) .limit(1) - .select( + .select([ Constant(1_741_380_235).unixSecondsToTimestamp().as("unixSecondsToTimestamp"), Constant(1_741_380_235_123).unixMillisToTimestamp().as("unixMillisToTimestamp"), Constant(1_741_380_235_123_456).unixMicrosToTimestamp().as("unixMicrosToTimestamp"), @@ -2585,8 +2604,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { Constant(Timestamp(seconds: 1_741_380_235, nanoseconds: 123_456_789)) .timestampToUnixMillis().as("timestampToUnixMillis"), Constant(Timestamp(seconds: 1_741_380_235, nanoseconds: 123_456_789)) - .timestampToUnixMicros().as("timestampToUnixMicros") - ) + .timestampToUnixMicros().as("timestampToUnixMicros"), + ]) let snapshot = try await pipeline.execute() XCTAssertEqual( @@ -2622,21 +2641,25 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { .collection(randomCol.path) .limit(1) .select( - Constant(initialTimestamp).as("timestamp") + [ + Constant(initialTimestamp).as("timestamp"), + ] ) .select( - Field("timestamp").timestampAdd(.day, 10).as("plus10days"), - Field("timestamp").timestampAdd(.hour, 10).as("plus10hours"), - Field("timestamp").timestampAdd(.minute, 10).as("plus10minutes"), - Field("timestamp").timestampAdd(.second, 10).as("plus10seconds"), - Field("timestamp").timestampAdd(.microsecond, 10).as("plus10micros"), - Field("timestamp").timestampAdd(.millisecond, 10).as("plus10millis"), - Field("timestamp").timestampSub(.day, 10).as("minus10days"), - Field("timestamp").timestampSub(.hour, 10).as("minus10hours"), - Field("timestamp").timestampSub(.minute, 10).as("minus10minutes"), - Field("timestamp").timestampSub(.second, 10).as("minus10seconds"), - Field("timestamp").timestampSub(.microsecond, 10).as("minus10micros"), - Field("timestamp").timestampSub(.millisecond, 10).as("minus10millis") + [ + Field("timestamp").timestampAdd(10, .day).as("plus10days"), + Field("timestamp").timestampAdd(10, .hour).as("plus10hours"), + Field("timestamp").timestampAdd(10, .minute).as("plus10minutes"), + Field("timestamp").timestampAdd(10, .second).as("plus10seconds"), + Field("timestamp").timestampAdd(10, .microsecond).as("plus10micros"), + Field("timestamp").timestampAdd(10, .millisecond).as("plus10millis"), + Field("timestamp").timestampSub(10, .day).as("minus10days"), + Field("timestamp").timestampSub(10, .hour).as("minus10hours"), + Field("timestamp").timestampSub(10, .minute).as("minus10minutes"), + Field("timestamp").timestampSub(10, .second).as("minus10seconds"), + Field("timestamp").timestampSub(10, .microsecond).as("minus10micros"), + Field("timestamp").timestampSub(10, .millisecond).as("minus10millis"), + ] ) let snapshot = try await pipeline.execute() @@ -2675,10 +2698,14 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { .collection(randomCol.path) .limit(1) .select( - Constant(bytes).as("bytes") + [ + Constant(bytes).as("bytes"), + ] ) .select( - Field("bytes").byteLength().as("byteLength") + [ + Field("bytes").byteLength().as("byteLength"), + ] ) let snapshot = try await pipeline.execute() @@ -2706,10 +2733,12 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(randomCol.path) .limit(1) - .select(Constant(true).as("trueField")) + .select([Constant(true).as("trueField")]) .select( - Field("trueField"), - (!(Field("trueField").eq(true))).as("falseField") + [ + Field("trueField"), + (!(Field("trueField").equal(true))).as("falseField"), + ] ) let snapshot = try await pipeline.execute() @@ -2734,9 +2763,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .where(Field("title").eq("The Lord of the Rings")) + .where(Field("title").equal("The Lord of the Rings")) .limit(1) - .select(Field("title").replaceFirst("o", "0").as("newName")) + .select([Field("title").replaceFirst("o", with: "0").as("newName")]) let snapshot = try await pipeline.execute() TestHelper.compare( pipelineSnapshot: snapshot, @@ -2752,9 +2781,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .where(Field("title").eq("The Lord of the Rings")) + .where(Field("title").equal("The Lord of the Rings")) .limit(1) - .select(Field("title").replaceAll("o", "0").as("newName")) + .select([Field("title").replaceAll("o", with: "0").as("newName")]) let snapshot = try await pipeline.execute() TestHelper.compare( pipelineSnapshot: snapshot, @@ -2772,7 +2801,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(randomCol.path) .limit(1) - .select(Constant(5).bitAnd(12).as("result")) + .select([Constant(5).bitAnd(12).as("result")]) let snapshot = try await pipeline.execute() TestHelper.compare(pipelineSnapshot: snapshot, expected: [["result": 4]], enforceOrder: false) } @@ -2786,7 +2815,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(randomCol.path) .limit(1) - .select(Constant(5).bitOr(12).as("result")) + .select([Constant(5).bitOr(12).as("result")]) let snapshot = try await pipeline.execute() TestHelper.compare(pipelineSnapshot: snapshot, expected: [["result": 13]], enforceOrder: false) } @@ -2800,7 +2829,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(randomCol.path) .limit(1) - .select(Constant(5).bitXor(12).as("result")) + .select([Constant(5).bitXor(12).as("result")]) let snapshot = try await pipeline.execute() TestHelper.compare(pipelineSnapshot: snapshot, expected: [["result": 9]], enforceOrder: false) } @@ -2816,7 +2845,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(randomCol.path) .limit(1) - .select(Constant(bytesInput).bitNot().as("result")) + .select([Constant(bytesInput).bitNot().as("result")]) let snapshot = try await pipeline.execute() TestHelper.compare( pipelineSnapshot: snapshot, @@ -2836,7 +2865,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(randomCol.path) .limit(1) - .select(Constant(bytesInput).bitLeftShift(2).as("result")) + .select([Constant(bytesInput).bitLeftShift(2).as("result")]) let snapshot = try await pipeline.execute() TestHelper.compare( pipelineSnapshot: snapshot, @@ -2856,7 +2885,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(randomCol.path) .limit(1) - .select(Constant(bytesInput).bitRightShift(2).as("result")) + .select([Constant(bytesInput).bitRightShift(2).as("result")]) let snapshot = try await pipeline.execute() TestHelper.compare( pipelineSnapshot: snapshot, @@ -2872,9 +2901,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) .limit(1) - .select(Field("__path__").documentId().as("docId")) + .select([Field("__path__").documentId().as("docId")]) let snapshot = try await pipeline.execute() TestHelper.compare( pipelineSnapshot: snapshot, @@ -2890,9 +2919,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) .limit(1) - .select(Field("title").substr(9, 2).as("of")) + .select([Field("title").substr(position: 9, length: 2).as("of")]) let snapshot = try await pipeline.execute() TestHelper.compare(pipelineSnapshot: snapshot, expected: [["of": "of"]], enforceOrder: false) } @@ -2904,9 +2933,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .sort(Field("rating").descending()) + .sort([Field("rating").descending()]) .limit(1) - .select(Field("title").substr(9).as("of")) + .select([Field("title").substr(position: 9).as("of")]) let snapshot = try await pipeline.execute() TestHelper.compare( pipelineSnapshot: snapshot, @@ -2924,11 +2953,15 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { .collection(collRef.path) .limit(1) // Assuming we operate on the first book (book1) .select( - Field("tags").arrayConcat( - ["newTag1", "newTag2"], - [Field("tags")], - [Constant.nil] - ).as("modifiedTags") + [ + Field("tags").arrayConcat( + [ + ["newTag1", "newTag2"], + [Field("tags")], + [Constant.nil], + ] + ).as("modifiedTags"), + ] ) var snapshot = try await pipeline.execute() @@ -2949,11 +2982,15 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { .collection(collRef.path) .limit(1) // Assuming we operate on the first book (book1) .select( - Field("tags").arrayConcat( - Field("newTag1"), Field("newTag2"), - Field("tags"), - Constant.nil - ).as("modifiedTags") + [ + Field("tags").arrayConcat( + [ + Field("newTag1"), Field("newTag2"), + Field("tags"), + Constant.nil, + ] + ).as("modifiedTags"), + ] ) snapshot = try await pipeline.execute() @@ -2972,7 +3009,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .limit(1) - .select(Field("title").lowercased().as("lowercaseTitle")) + .select([Field("title").lowercased().as("lowercaseTitle")]) let snapshot = try await pipeline.execute() TestHelper.compare( pipelineSnapshot: snapshot, @@ -2989,7 +3026,7 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) .limit(1) - .select(Field("author").uppercased().as("uppercaseAuthor")) + .select([Field("author").uppercased().as("uppercaseAuthor")]) let snapshot = try await pipeline.execute() TestHelper.compare( pipelineSnapshot: snapshot, @@ -3005,8 +3042,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .addFields(Constant(" The Hitchhiker's Guide to the Galaxy ").as("spacedTitle")) - .select(Field("spacedTitle").trim().as("trimmedTitle"), Field("spacedTitle")) + .addFields([Constant(" The Hitchhiker's Guide to the Galaxy ").as("spacedTitle")]) + .select([Field("spacedTitle").trim().as("trimmedTitle"), Field("spacedTitle")]) .limit(1) let snapshot = try await pipeline.execute() TestHelper.compare( @@ -3027,9 +3064,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .where(Field("title").eq("1984")) + .where(Field("title").equal("1984")) .limit(1) - .select(Field("title").reverse().as("reverseTitle")) + .select([Field("title").reverse().as("reverseTitle")]) let snapshot = try await pipeline.execute() TestHelper.compare( pipelineSnapshot: snapshot, @@ -3079,10 +3116,12 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .select("title", "rating", "__name__") + .select(["title", "rating", "__name__"]) .sort( - Field("rating").descending(), - Field("__name__").ascending() + [ + Field("rating").descending(), + Field("__name__").ascending(), + ] ) var snapshot = try await pipeline.limit(Int32(pageSize)).execute() @@ -3099,9 +3138,9 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let lastDoc = snapshot.results.last! snapshot = try await pipeline.where( - (Field("rating").eq(lastDoc.get("rating")!) - && Field("rating").lt(lastDoc.get("rating")!)) - || Field("rating").lt(lastDoc.get("rating")!) + (Field("rating").equal(lastDoc.get("rating")!) + && Field("rating").lessThan(lastDoc.get("rating")!)) + || Field("rating").lessThan(lastDoc.get("rating")!) ).limit(Int32(pageSize)).execute() TestHelper.compare( @@ -3126,10 +3165,12 @@ class PipelineIntegrationTests: FSTIntegrationTestCase { let pipeline = db.pipeline() .collection(collRef.path) - .select("title", "rating", "__path__") + .select(["title", "rating", "__path__"]) .sort( - Field("rating").descending(), - Field("__path__").ascending() + [ + Field("rating").descending(), + Field("__path__").ascending(), + ] ) var snapshot = try await pipeline.offset(Int32(currPage) * Int32(pageSize)).limit(