Skip to content

Commit 5a2cc6b

Browse files
cherylEnkiduwu-hui
andauthored
Add missing expressions (#15457)
Co-authored-by: wu-hui <wu.hui.github@gmail.com>
1 parent 81cce2d commit 5a2cc6b

File tree

5 files changed

+534
-370
lines changed

5 files changed

+534
-370
lines changed

Firestore/Swift/Source/ExpressionImplementation.swift

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,14 @@ public extension Expression {
523523
return FunctionExpression(functionName: "array_get", args: [self, offsetExpression])
524524
}
525525

526+
func arrayMaximum() -> FunctionExpression {
527+
return FunctionExpression(functionName: "maximum", args: [self])
528+
}
529+
530+
func arrayMinimum() -> FunctionExpression {
531+
return FunctionExpression(functionName: "minimum", args: [self])
532+
}
533+
526534
func greaterThan(_ other: Expression) -> BooleanExpression {
527535
return BooleanExpression(functionName: "greater_than", args: [self, other])
528536
}
@@ -622,6 +630,14 @@ public extension Expression {
622630
return FunctionExpression(functionName: "join", args: [self, Constant(delimiter)])
623631
}
624632

633+
func split(delimiter: String) -> FunctionExpression {
634+
return FunctionExpression(functionName: "split", args: [self, Constant(delimiter)])
635+
}
636+
637+
func split(delimiter: Expression) -> FunctionExpression {
638+
return FunctionExpression(functionName: "split", args: [self, delimiter])
639+
}
640+
625641
func length() -> FunctionExpression {
626642
return FunctionExpression(functionName: "length", args: [self])
627643
}
@@ -709,6 +725,10 @@ public extension Expression {
709725
return FunctionExpression(functionName: "trim", args: [self, value])
710726
}
711727

728+
func trim() -> FunctionExpression {
729+
return FunctionExpression(functionName: "trim", args: [self])
730+
}
731+
712732
func stringConcat(_ strings: [Expression]) -> FunctionExpression {
713733
return FunctionExpression(functionName: "string_concat", args: [self] + strings)
714734
}
@@ -773,20 +793,6 @@ public extension Expression {
773793
return FunctionExpression(functionName: "map_merge", args: [self] + maps)
774794
}
775795

776-
func mapSet(key: Expression, value: Sendable) -> FunctionExpression {
777-
return FunctionExpression(
778-
functionName: "map_set",
779-
args: [self, key, Helper.sendableToExpr(value)]
780-
)
781-
}
782-
783-
func mapSet(key: String, value: Sendable) -> FunctionExpression {
784-
return FunctionExpression(
785-
functionName: "map_set",
786-
args: [self, Helper.sendableToExpr(key), Helper.sendableToExpr(value)]
787-
)
788-
}
789-
790796
// --- Added Aggregate Operations (on Expr) ---
791797

792798
func countDistinct() -> AggregateFunction {
@@ -919,7 +925,7 @@ public extension Expression {
919925
return FunctionExpression(functionName: "timestamp_to_unix_seconds", args: [self])
920926
}
921927

922-
func timestampTruncate(granularity: TimeUnit) -> FunctionExpression {
928+
func timestampTruncate(granularity: TimeGranularity) -> FunctionExpression {
923929
return FunctionExpression(
924930
functionName: "timestamp_trunc",
925931
args: [self, Helper.sendableToExpr(granularity.rawValue)]
@@ -1001,4 +1007,8 @@ public extension Expression {
10011007
let exprs = [self] + values.map { Helper.sendableToExpr($0) }
10021008
return FunctionExpression(functionName: "concat", args: exprs)
10031009
}
1010+
1011+
func type() -> FunctionExpression {
1012+
return FunctionExpression(functionName: "type", args: [self])
1013+
}
10041014
}

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

Lines changed: 72 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,30 @@ public protocol Expression: Sendable {
461461
/// - Returns: A new `FunctionExpression` representing the "arrayGet" operation.
462462
func arrayGet(_ offsetExpression: Expression) -> FunctionExpression
463463

464+
/// Creates an expression that returns the maximum element of an array.
465+
///
466+
/// Assumes `self` evaluates to an array.
467+
///
468+
/// ```swift
469+
/// // Get the maximum value in the "scores" array.
470+
/// Field("scores").arrayMaximum()
471+
/// ```
472+
///
473+
/// - Returns: A new `FunctionExpression` representing the maximum element of the array.
474+
func arrayMaximum() -> FunctionExpression
475+
476+
/// Creates an expression that returns the minimum element of an array.
477+
///
478+
/// Assumes `self` evaluates to an array.
479+
///
480+
/// ```swift
481+
/// // Get the minimum value in the "scores" array.
482+
/// Field("scores").arrayMinimum()
483+
/// ```
484+
///
485+
/// - Returns: A new `FunctionExpression` representing the minimum element of the array.
486+
func arrayMinimum() -> FunctionExpression
487+
464488
/// Creates a `BooleanExpression` that returns `true` if this expression is greater
465489
/// than the given expression.
466490
///
@@ -681,6 +705,18 @@ public protocol Expression: Sendable {
681705
/// - Returns: A new `FunctionExpression` representing the joined string.
682706
func join(delimiter: String) -> FunctionExpression
683707

708+
/// Creates an expression that splits a string into an array of substrings based on a delimiter.
709+
///
710+
/// - Parameter delimiter: The string to split on.
711+
/// - Returns: A new `FunctionExpression` representing the array of substrings.
712+
func split(delimiter: String) -> FunctionExpression
713+
714+
/// Creates an expression that splits a string into an array of substrings based on a delimiter.
715+
///
716+
/// - Parameter delimiter: An expression that evaluates to a string or bytes to split on.
717+
/// - Returns: A new `FunctionExpression` representing the array of substrings.
718+
func split(delimiter: Expression) -> FunctionExpression
719+
684720
/// Creates an expression that returns the length of a string.
685721
///
686722
/// ```swift
@@ -886,6 +922,18 @@ public protocol Expression: Sendable {
886922
/// - Returns: A new `FunctionExpression` representing the uppercase string.
887923
func toUpper() -> FunctionExpression
888924

925+
/// Creates an expression that removes leading and trailing whitespace from a string.
926+
///
927+
/// Assumes `self` evaluates to a string.
928+
///
929+
/// ```swift
930+
/// // Trim leading/trailing whitespace from the "comment" field.
931+
/// Field("comment").trim()
932+
/// ```
933+
///
934+
/// - Returns: A new `FunctionExpression` representing the trimmed string.
935+
func trim() -> FunctionExpression
936+
889937
/// Creates an expression that removes leading and trailing occurrences of specified characters
890938
/// from a string (from `self`).
891939
/// Assumes `self` evaluates to a string, and `value` evaluates to a string.
@@ -961,8 +1009,8 @@ public protocol Expression: Sendable {
9611009
/// - Returns: A new `FunctionExpression` representing the reversed string.
9621010
func stringReverse() -> FunctionExpression
9631011

964-
/// Creates an expression that calculates the length of this expression in bytes.
965-
/// Assumes `self` evaluates to a string.
1012+
/// Creates an expression that calculates the length of this string or bytes expression in bytes.
1013+
/// Assumes `self` evaluates to a string or bytes.
9661014
///
9671015
/// ```swift
9681016
/// // Calculate the length of the "myString" field in bytes.
@@ -975,9 +1023,9 @@ public protocol Expression: Sendable {
9751023
/// - Returns: A new `FunctionExpression` representing the length in bytes.
9761024
func byteLength() -> FunctionExpression
9771025

978-
/// Creates an expression that returns a substring of this expression using
1026+
/// Creates an expression that returns a substring of this expression (String or Bytes) using
9791027
/// literal integers for position and optional length.
980-
/// Indexing is 0-based. Assumes `self` evaluates to a string.
1028+
/// Indexing is 0-based. Assumes `self` evaluates to a string or bytes.
9811029
///
9821030
/// ```swift
9831031
/// // Get substring from index 5 with length 10
@@ -992,9 +1040,9 @@ public protocol Expression: Sendable {
9921040
/// - Returns: A new `FunctionExpression` representing the substring.
9931041
func substring(position: Int, length: Int?) -> FunctionExpression
9941042

995-
/// Creates an expression that returns a substring of this expression using
1043+
/// Creates an expression that returns a substring of this expression (String or Bytes) using
9961044
/// expressions for position and optional length.
997-
/// Indexing is 0-based. Assumes `self` evaluates to a string, and parameters evaluate to
1045+
/// Indexing is 0-based. Assumes `self` evaluates to a string or bytes, and parameters evaluate to
9981046
/// integers.
9991047
///
10001048
/// ```swift
@@ -1080,34 +1128,6 @@ public protocol Expression: Sendable {
10801128
/// - Returns: A new `FunctionExpression` representing the "map_merge" operation.
10811129
func mapMerge(_ maps: [Expression]) -> FunctionExpression
10821130

1083-
/// Creates an expression that adds or updates a specified field in a map.
1084-
/// Assumes `self` evaluates to a Map, `key` evaluates to a string, and `value` can be
1085-
/// any type.
1086-
///
1087-
/// ```swift
1088-
/// // Set a field using a key from another field
1089-
/// Field("config").mapSet(key: Field("keyName"), value: Field("keyValue"))
1090-
/// ```
1091-
///
1092-
/// - Parameter key: An `Expression` (evaluating to a string) representing the key of
1093-
/// the field to set or update.
1094-
/// - Parameter value: The `Expression` representing the value to set for the field.
1095-
/// - Returns: A new `FunctionExpression` representing the map with the updated field.
1096-
func mapSet(key: Expression, value: Sendable) -> FunctionExpression
1097-
1098-
/// Creates an expression that adds or updates a specified field in a map.
1099-
/// Assumes `self` evaluates to a Map.
1100-
///
1101-
/// ```swift
1102-
/// // Set the "status" field to "active" in the "order" map
1103-
/// Field("order").mapSet(key: "status", value: "active")
1104-
/// ```
1105-
///
1106-
/// - Parameter key: The literal string key of the field to set or update.
1107-
/// - Parameter value: The `Sendable` literal value to set for the field.
1108-
/// - Returns: A new `FunctionExpression` representing the map with the updated field.
1109-
func mapSet(key: String, value: Sendable) -> FunctionExpression
1110-
11111131
// MARK: Aggregations
11121132

11131133
/// Creates an aggregation that counts the number of distinct values of this expression.
@@ -1429,19 +1449,23 @@ public protocol Expression: Sendable {
14291449
/// Field("timestamp").timestampTruncate(granularity: .day)
14301450
/// ```
14311451
///
1432-
/// - Parameter granularity: A `TimeUnit` enum representing the truncation unit.
1452+
/// - Parameter granularity: A `TimeGranularity` representing the truncation unit.
14331453
/// - Returns: A new `FunctionExpression` representing the truncated timestamp.
1434-
func timestampTruncate(granularity: TimeUnit) -> FunctionExpression
1454+
func timestampTruncate(granularity: TimeGranularity) -> FunctionExpression
14351455

14361456
/// Creates an expression that truncates a timestamp to a specified granularity.
1437-
/// Assumes `self` evaluates to a Timestamp, and `granularity` is a literal string.
1457+
/// Assumes `self` evaluates to a Timestamp.
14381458
///
14391459
/// ```swift
14401460
/// // Truncate "timestamp" field to the nearest day using a literal string.
14411461
/// Field("timestamp").timestampTruncate(granularity: "day")
1462+
///
1463+
/// // Truncate "timestamp" field to the nearest day using an expression.
1464+
/// Field("timestamp").timestampTruncate(granularity: Field("granularity_field"))
14421465
/// ```
14431466
///
1444-
/// - Parameter granularity: A `Sendable` literal string specifying the truncation unit.
1467+
/// - Parameter granularity: A `Sendable` literal string or an `Expression` that evaluates to a
1468+
/// string, specifying the truncation unit.
14451469
/// - Returns: A new `FunctionExpression` representing the truncated timestamp.
14461470
func timestampTruncate(granularity: Sendable) -> FunctionExpression
14471471

@@ -1596,4 +1620,14 @@ public protocol Expression: Sendable {
15961620
/// - Parameter values: The values to concatenate.
15971621
/// - Returns: A new `FunctionExpression` representing the concatenated result.
15981622
func concat(_ values: [Sendable]) -> FunctionExpression
1623+
1624+
/// Creates an expression that returns the type of the expression.
1625+
///
1626+
/// ```swift
1627+
/// // Get the type of the "rating" field.
1628+
/// Field("rating").type()
1629+
/// ```
1630+
///
1631+
/// - Returns: A new `FunctionExpression` representing the type of the expression as a string.
1632+
func type() -> FunctionExpression
15991633
}

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ public struct Pipeline: @unchecked Sendable {
103103
}
104104
}
105105

106+
/// Creates a new `Pipeline` instance in a faulted state.
107+
///
108+
/// This function is used to propagate an error through the pipeline chain. When a stage
109+
/// fails to initialize or if a preceding stage has already failed, this method is called
110+
/// to create a new pipeline that holds the error message. The `stages` array is cleared,
111+
/// and the `errorMessage` is set.
112+
///
113+
/// The stored error is eventually thrown by the `execute()` method.
114+
///
115+
/// - Parameter message: The error message to store in the pipeline.
116+
/// - Returns: A new `Pipeline` instance with the specified error message.
106117
private func withError(_ message: String) -> Pipeline {
107118
return Pipeline(stages: [], db: db, errorMessage: message)
108119
}
@@ -127,7 +138,7 @@ public struct Pipeline: @unchecked Sendable {
127138
/// - Throws: An error if the pipeline execution fails on the backend.
128139
/// - Returns: A `Pipeline.Snapshot` containing the result of the pipeline execution.
129140
public func execute() async throws -> Pipeline.Snapshot {
130-
// Check if any Error exist during Stage contruction
141+
// Check if any errors occurred during stage construction.
131142
if let errorMessage = errorMessage {
132143
throw NSError(
133144
domain: "com.google.firebase.firestore",
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
public struct TimeGranularity: Sendable, Equatable, Hashable {
16+
enum Kind: String {
17+
case microsecond
18+
case millisecond
19+
case second
20+
case minute
21+
case hour
22+
case day
23+
case week
24+
case weekMonday = "week(monday)"
25+
case weekTuesday = "week(tuesday)"
26+
case weekWednesday = "week(wednesday)"
27+
case weekThursday = "week(thursday)"
28+
case weekFriday = "week(friday)"
29+
case weekSaturday = "week(saturday)"
30+
case weekSunday = "week(sunday)"
31+
case isoweek
32+
case month
33+
case quarter
34+
case year
35+
case isoyear
36+
}
37+
38+
public static let microsecond = TimeGranularity(kind: .microsecond)
39+
public static let millisecond = TimeGranularity(kind: .millisecond)
40+
public static let second = TimeGranularity(kind: .second)
41+
public static let minute = TimeGranularity(kind: .minute)
42+
public static let hour = TimeGranularity(kind: .hour)
43+
/// The day in the Gregorian calendar year that contains the value to truncate.
44+
public static let day = TimeGranularity(kind: .day)
45+
/// The first day in the week that contains the value to truncate. Weeks begin on Sundays. WEEK is
46+
/// equivalent to WEEK(SUNDAY).
47+
public static let week = TimeGranularity(kind: .week)
48+
/// The first day in the week that contains the value to truncate. Weeks begin on Monday.
49+
public static let weekMonday = TimeGranularity(kind: .weekMonday)
50+
/// The first day in the week that contains the value to truncate. Weeks begin on Tuesday.
51+
public static let weekTuesday = TimeGranularity(kind: .weekTuesday)
52+
/// The first day in the week that contains the value to truncate. Weeks begin on Wednesday.
53+
public static let weekWednesday = TimeGranularity(kind: .weekWednesday)
54+
/// The first day in the week that contains the value to truncate. Weeks begin on Thursday.
55+
public static let weekThursday = TimeGranularity(kind: .weekThursday)
56+
/// The first day in the week that contains the value to truncate. Weeks begin on Friday.
57+
public static let weekFriday = TimeGranularity(kind: .weekFriday)
58+
/// The first day in the week that contains the value to truncate. Weeks begin on Saturday.
59+
public static let weekSaturday = TimeGranularity(kind: .weekSaturday)
60+
/// The first day in the week that contains the value to truncate. Weeks begin on Sunday.
61+
public static let weekSunday = TimeGranularity(kind: .weekSunday)
62+
/// The first day in the ISO 8601 week that contains the value to truncate. The ISO week begins on
63+
/// Monday. The first ISO week of each ISO year contains the first Thursday of the corresponding
64+
/// Gregorian calendar year.
65+
public static let isoweek = TimeGranularity(kind: .isoweek)
66+
/// The first day in the month that contains the value to truncate.
67+
public static let month = TimeGranularity(kind: .month)
68+
/// The first day in the quarter that contains the value to truncate.
69+
public static let quarter = TimeGranularity(kind: .quarter)
70+
/// The first day in the year that contains the value to truncate.
71+
public static let year = TimeGranularity(kind: .year)
72+
/// The first day in the ISO 8601 week-numbering year that contains the value to truncate. The ISO
73+
/// year is the Monday of the first week where Thursday belongs to the corresponding Gregorian
74+
/// calendar year.
75+
public static let isoyear = TimeGranularity(kind: .isoyear)
76+
77+
public let rawValue: String
78+
79+
init(kind: Kind) {
80+
rawValue = kind.rawValue
81+
}
82+
}

0 commit comments

Comments
 (0)