Skip to content

Commit caf8158

Browse files
committed
add abs()
1 parent 7c7eb75 commit caf8158

File tree

8 files changed

+53
-12
lines changed

8 files changed

+53
-12
lines changed

Firestore/Swift/Source/ExpressionImplementation.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ public extension Expression {
2525

2626
// MARK: Arithmetic Operators
2727

28+
func abs() -> FunctionExpression {
29+
return FunctionExpression("abs", [self])
30+
}
31+
2832
func add(_ value: Expression) -> FunctionExpression {
2933
return FunctionExpression("add", [self, value])
3034
}

Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/Constant.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
#endif // SWIFT_PACKAGE
2020

2121
///
22-
/// A `Constant` is an `Expression` that represents a fixed, literal value within a Firestore pipeline.
22+
/// A `Constant` is an `Expression` that represents a fixed, literal value within a Firestore
23+
/// pipeline.
2324
///
2425
/// `Constant`s are used to introduce literal values into a query, which can be useful for:
2526
/// - Comparing a field to a specific value in a `where` clause.

Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/Expression.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ public protocol Expression: Sendable {
3636

3737
// --- Added Mathematical Operations ---
3838

39+
/// Creates an expression that returns the absolute value of the number.
40+
///
41+
/// ```swift
42+
/// // Get the absolute value of the "amount" field.
43+
/// Field("amount").abs()
44+
/// ```
45+
///
46+
/// - Returns: A new `FunctionExpression` representing the absolute value of the number.
47+
func abs() -> FunctionExpression
48+
3949
/// Creates an expression that adds another expression to this expression.
4050
/// To add multiple expressions, chain calls to this method.
4151
/// Assumes `self` and the parameter evaluate to compatible types for addition (e.g., numbers, or

Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/FunctionExpressions/ArrayExpression.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,4 @@ public class ArrayExpression: FunctionExpression, @unchecked Sendable {
4040

4141
super.init("array", result)
4242
}
43-
}
43+
}

Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/FunctionExpressions/MapExpression.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,4 @@ public class MapExpression: FunctionExpression, @unchecked Sendable {
3838

3939
super.init("map", result)
4040
}
41-
}
41+
}

Firestore/Swift/Source/SwiftAPI/Pipeline/Ordering.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public struct Ordering: @unchecked Sendable {
2020
let bridge: OrderingBridge
2121

2222
init(expression: Expression, direction: Direction) {
23-
self.expr = expression
23+
expr = expression
2424
self.direction = direction
2525
bridge = OrderingBridge(expr: expression.toBridge(), direction: direction.rawValue)
2626
}

Firestore/Swift/Source/SwiftAPI/Pipeline/RealtimePipelineSource.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// limitations under the License.
1414

1515
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
16-
internal struct RealtimePipelineSource: @unchecked Sendable {
16+
struct RealtimePipelineSource: @unchecked Sendable {
1717
let db: Firestore
1818
let factory: ([Stage], Firestore) -> RealtimePipeline
1919

@@ -22,26 +22,26 @@ internal struct RealtimePipelineSource: @unchecked Sendable {
2222
self.factory = factory
2323
}
2424

25-
internal func collection(_ path: String) -> RealtimePipeline {
25+
func collection(_ path: String) -> RealtimePipeline {
2626
return factory([CollectionSource(collection: db.collection(path), db: db)], db)
2727
}
2828

29-
internal func collection(_ coll: CollectionReference) -> RealtimePipeline {
29+
func collection(_ coll: CollectionReference) -> RealtimePipeline {
3030
return factory([CollectionSource(collection: coll, db: db)], db)
3131
}
3232

33-
internal func collectionGroup(_ collectionId: String) -> RealtimePipeline {
33+
func collectionGroup(_ collectionId: String) -> RealtimePipeline {
3434
return factory(
3535
[CollectionGroupSource(collectionId: collectionId)],
3636
db
3737
)
3838
}
3939

40-
internal func documents(_ docs: [DocumentReference]) -> RealtimePipeline {
40+
func documents(_ docs: [DocumentReference]) -> RealtimePipeline {
4141
return factory([DocumentsSource(docs: docs, db: db)], db)
4242
}
4343

44-
internal func documents(_ paths: [String]) -> RealtimePipeline {
44+
func documents(_ paths: [String]) -> RealtimePipeline {
4545
let docs = paths.map { db.document($0) }
4646
return factory([DocumentsSource(docs: docs, db: db)], db)
4747
}

Firestore/Swift/Tests/Integration/PipelineTests.swift

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -823,8 +823,8 @@ class PipelineIntegrationTests: FSTIntegrationTestCase {
823823
XCTFail("No result for min/max/count/countAll aggregation")
824824
}
825825
}
826-
827-
// Hide this test due to `.countIf()` design is incomplete.
826+
827+
// Hide this test due to `.countIf()` design is incomplete.
828828
// func testReturnsCountIfAccumulation() async throws {
829829
// let collRef = collectionRef(withDocuments: bookDocs)
830830
// let db = collRef.firestore
@@ -1949,6 +1949,32 @@ class PipelineIntegrationTests: FSTIntegrationTestCase {
19491949
}
19501950
}
19511951

1952+
func testAbsWorks() async throws {
1953+
let collRef = collectionRef(withDocuments: [
1954+
"doc1": ["value": -10],
1955+
"doc2": ["value": 5],
1956+
"doc3": ["value": 0],
1957+
])
1958+
let db = collRef.firestore
1959+
1960+
let pipeline = db.pipeline()
1961+
.collection(collRef.path)
1962+
.select([
1963+
Field("value").abs().as("absValue"),
1964+
])
1965+
.sort([Field("absValue").ascending()])
1966+
1967+
let snapshot = try await pipeline.execute()
1968+
1969+
let expectedResults: [[String: Sendable]] = [
1970+
["absValue": 0],
1971+
["absValue": 5],
1972+
["absValue": 10],
1973+
]
1974+
1975+
TestHelper.compare(pipelineSnapshot: snapshot, expected: expectedResults, enforceOrder: true)
1976+
}
1977+
19521978
func testComparisonOperators() async throws {
19531979
let collRef = collectionRef(withDocuments: bookDocs)
19541980
let db = collRef.firestore

0 commit comments

Comments
 (0)