Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 25 additions & 15 deletions Firestore/Swift/Source/ExpressionImplementation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,14 @@ public extension Expression {
return FunctionExpression(functionName: "array_get", args: [self, offsetExpression])
}

func arrayMaximum() -> FunctionExpression {
return FunctionExpression(functionName: "maximum", args: [self])
}

func arrayMinimum() -> FunctionExpression {
return FunctionExpression(functionName: "minimum", args: [self])
}

func greaterThan(_ other: Expression) -> BooleanExpression {
return BooleanExpression(functionName: "greater_than", args: [self, other])
}
Expand Down Expand Up @@ -638,6 +646,14 @@ public extension Expression {
return FunctionExpression(functionName: "join", args: [self, Constant(delimiter)])
}

func split(delimiter: String) -> FunctionExpression {
return FunctionExpression(functionName: "split", args: [self, Constant(delimiter)])
}

func split(delimiter: Expression) -> FunctionExpression {
return FunctionExpression(functionName: "split", args: [self, delimiter])
}

func length() -> FunctionExpression {
return FunctionExpression(functionName: "length", args: [self])
}
Expand Down Expand Up @@ -725,6 +741,10 @@ public extension Expression {
return FunctionExpression(functionName: "trim", args: [self, value])
}

func trim() -> FunctionExpression {
return FunctionExpression(functionName: "trim", args: [self])
}

func stringConcat(_ strings: [Expression]) -> FunctionExpression {
return FunctionExpression(functionName: "string_concat", args: [self] + strings)
}
Expand Down Expand Up @@ -789,20 +809,6 @@ public extension Expression {
return FunctionExpression(functionName: "map_merge", args: [self] + maps)
}

func mapSet(key: Expression, value: Sendable) -> FunctionExpression {
return FunctionExpression(
functionName: "map_set",
args: [self, key, Helper.sendableToExpr(value)]
)
}

func mapSet(key: String, value: Sendable) -> FunctionExpression {
return FunctionExpression(
functionName: "map_set",
args: [self, Helper.sendableToExpr(key), Helper.sendableToExpr(value)]
)
}

// --- Added Aggregate Operations (on Expr) ---

func countDistinct() -> AggregateFunction {
Expand Down Expand Up @@ -935,7 +941,7 @@ public extension Expression {
return FunctionExpression(functionName: "timestamp_to_unix_seconds", args: [self])
}

func timestampTruncate(granularity: TimeUnit) -> FunctionExpression {
func timestampTruncate(granularity: TimeGranularity) -> FunctionExpression {
return FunctionExpression(
functionName: "timestamp_trunc",
args: [self, Helper.sendableToExpr(granularity.rawValue)]
Expand Down Expand Up @@ -1017,4 +1023,8 @@ public extension Expression {
let exprs = [self] + values.map { Helper.sendableToExpr($0) }
return FunctionExpression(functionName: "concat", args: exprs)
}

func type() -> FunctionExpression {
return FunctionExpression(functionName: "type", args: [self])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,30 @@ public protocol Expression: Sendable {
/// - Returns: A new `FunctionExpression` representing the "arrayGet" operation.
func arrayGet(_ offsetExpression: Expression) -> FunctionExpression

/// Creates an expression that returns the maximum element of an array.
///
/// Assumes `self` evaluates to an array.
///
/// ```swift
/// // Get the maximum value in the "scores" array.
/// Field("scores").arrayMaximum()
/// ```
///
/// - Returns: A new `FunctionExpression` representing the maximum element of the array.
func arrayMaximum() -> FunctionExpression

/// Creates an expression that returns the minimum element of an array.
///
/// Assumes `self` evaluates to an array.
///
/// ```swift
/// // Get the minimum value in the "scores" array.
/// Field("scores").arrayMinimum()
/// ```
///
/// - Returns: A new `FunctionExpression` representing the minimum element of the array.
func arrayMinimum() -> FunctionExpression

/// Creates a `BooleanExpression` that returns `true` if this expression is greater
/// than the given expression.
///
Expand Down Expand Up @@ -704,6 +728,18 @@ public protocol Expression: Sendable {
/// - Returns: A new `FunctionExpression` representing the joined string.
func join(delimiter: String) -> FunctionExpression

/// Creates an expression that splits a string into an array of substrings based on a delimiter.
///
/// - Parameter delimiter: The string to split on.
/// - Returns: A new `FunctionExpression` representing the array of substrings.
func split(delimiter: String) -> FunctionExpression

/// Creates an expression that splits a string into an array of substrings based on a delimiter.
///
/// - Parameter delimiter: An expression that evaluates to a string or bytes to split on.
/// - Returns: A new `FunctionExpression` representing the array of substrings.
func split(delimiter: Expression) -> FunctionExpression

/// Creates an expression that returns the length of a string.
///
/// ```swift
Expand Down Expand Up @@ -909,6 +945,18 @@ public protocol Expression: Sendable {
/// - Returns: A new `FunctionExpression` representing the uppercase string.
func toUpper() -> FunctionExpression

/// Creates an expression that removes leading and trailing whitespace from a string.
///
/// Assumes `self` evaluates to a string.
///
/// ```swift
/// // Trim leading/trailing whitespace from the "comment" field.
/// Field("comment").trim()
/// ```
///
/// - Returns: A new `FunctionExpression` representing the trimmed string.
func trim() -> FunctionExpression

/// Creates an expression that removes leading and trailing occurrences of specified characters
/// from a string (from `self`).
/// Assumes `self` evaluates to a string, and `value` evaluates to a string.
Expand Down Expand Up @@ -1103,34 +1151,6 @@ public protocol Expression: Sendable {
/// - Returns: A new `FunctionExpression` representing the "map_merge" operation.
func mapMerge(_ maps: [Expression]) -> FunctionExpression

/// Creates an expression that adds or updates a specified field in a map.
/// Assumes `self` evaluates to a Map, `key` evaluates to a string, and `value` can be
/// any type.
///
/// ```swift
/// // Set a field using a key from another field
/// Field("config").mapSet(key: Field("keyName"), value: Field("keyValue"))
/// ```
///
/// - Parameter key: An `Expression` (evaluating to a string) representing the key of
/// the field to set or update.
/// - Parameter value: The `Expression` representing the value to set for the field.
/// - Returns: A new `FunctionExpression` representing the map with the updated field.
func mapSet(key: Expression, value: Sendable) -> FunctionExpression

/// Creates an expression that adds or updates a specified field in a map.
/// Assumes `self` evaluates to a Map.
///
/// ```swift
/// // Set the "status" field to "active" in the "order" map
/// Field("order").mapSet(key: "status", value: "active")
/// ```
///
/// - Parameter key: The literal string key of the field to set or update.
/// - Parameter value: The `Sendable` literal value to set for the field.
/// - Returns: A new `FunctionExpression` representing the map with the updated field.
func mapSet(key: String, value: Sendable) -> FunctionExpression

// MARK: Aggregations

/// Creates an aggregation that counts the number of distinct values of this expression.
Expand Down Expand Up @@ -1452,19 +1472,23 @@ public protocol Expression: Sendable {
/// Field("timestamp").timestampTruncate(granularity: .day)
/// ```
///
/// - Parameter granularity: A `TimeUnit` enum representing the truncation unit.
/// - Parameter granularity: A `TimeGranularity` representing the truncation unit.
/// - Returns: A new `FunctionExpression` representing the truncated timestamp.
func timestampTruncate(granularity: TimeUnit) -> FunctionExpression
func timestampTruncate(granularity: TimeGranularity) -> FunctionExpression

/// Creates an expression that truncates a timestamp to a specified granularity.
/// Assumes `self` evaluates to a Timestamp, and `granularity` is a literal string.
/// Assumes `self` evaluates to a Timestamp.
///
/// ```swift
/// // Truncate "timestamp" field to the nearest day using a literal string.
/// Field("timestamp").timestampTruncate(granularity: "day")
///
/// // Truncate "timestamp" field to the nearest day using an expression.
/// Field("timestamp").timestampTruncate(granularity: Field("granularity_field"))
/// ```
///
/// - Parameter granularity: A `Sendable` literal string specifying the truncation unit.
/// - Parameter granularity: A `Sendable` literal string or an `Expression` that evaluates to a
/// string, specifying the truncation unit.
/// - Returns: A new `FunctionExpression` representing the truncated timestamp.
func timestampTruncate(granularity: Sendable) -> FunctionExpression

Expand Down Expand Up @@ -1619,4 +1643,14 @@ public protocol Expression: Sendable {
/// - Parameter values: The values to concatenate.
/// - Returns: A new `FunctionExpression` representing the concatenated result.
func concat(_ values: [Sendable]) -> FunctionExpression

/// Creates an expression that returns the type of the expression.
///
/// ```swift
/// // Get the type of the "rating" field.
/// Field("rating").type()
/// ```
///
/// - Returns: A new `FunctionExpression` representing the type of the expression as a string.
func type() -> FunctionExpression
}
82 changes: 82 additions & 0 deletions Firestore/Swift/Source/SwiftAPI/Pipeline/TimeGranularity.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// 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 struct TimeGranularity: Sendable, Equatable, Hashable {
enum Kind: String {
case microsecond
case millisecond
case second
case minute
case hour
case day
case week
case weekMonday = "week(monday)"
case weekTuesday = "week(tuesday)"
case weekWednesday = "week(wednesday)"
case weekThursday = "week(thursday)"
case weekFriday = "week(friday)"
case weekSaturday = "week(saturday)"
case weekSunday = "week(sunday)"
case isoweek
case month
case quarter
case year
case isoyear
}

public static let microsecond = TimeGranularity(kind: .microsecond)
public static let millisecond = TimeGranularity(kind: .millisecond)
public static let second = TimeGranularity(kind: .second)
public static let minute = TimeGranularity(kind: .minute)
public static let hour = TimeGranularity(kind: .hour)
/// The day in the Gregorian calendar year that contains the value to truncate.
public static let day = TimeGranularity(kind: .day)
/// The first day in the week that contains the value to truncate. Weeks begin on Sundays. WEEK is
/// equivalent to WEEK(SUNDAY).
public static let week = TimeGranularity(kind: .week)
/// The first day in the week that contains the value to truncate. Weeks begin on Monday.
public static let weekMonday = TimeGranularity(kind: .weekMonday)
/// The first day in the week that contains the value to truncate. Weeks begin on Tuesday.
public static let weekTuesday = TimeGranularity(kind: .weekTuesday)
/// The first day in the week that contains the value to truncate. Weeks begin on Wednesday.
public static let weekWednesday = TimeGranularity(kind: .weekWednesday)
/// The first day in the week that contains the value to truncate. Weeks begin on Thursday.
public static let weekThursday = TimeGranularity(kind: .weekThursday)
/// The first day in the week that contains the value to truncate. Weeks begin on Friday.
public static let weekFriday = TimeGranularity(kind: .weekFriday)
/// The first day in the week that contains the value to truncate. Weeks begin on Saturday.
public static let weekSaturday = TimeGranularity(kind: .weekSaturday)
/// The first day in the week that contains the value to truncate. Weeks begin on Sunday.
public static let weekSunday = TimeGranularity(kind: .weekSunday)
/// The first day in the ISO 8601 week that contains the value to truncate. The ISO week begins on
/// Monday. The first ISO week of each ISO year contains the first Thursday of the corresponding
/// Gregorian calendar year.
public static let isoweek = TimeGranularity(kind: .isoweek)
/// The first day in the month that contains the value to truncate.
public static let month = TimeGranularity(kind: .month)
/// The first day in the quarter that contains the value to truncate.
public static let quarter = TimeGranularity(kind: .quarter)
/// The first day in the year that contains the value to truncate.
public static let year = TimeGranularity(kind: .year)
/// The first day in the ISO 8601 week-numbering year that contains the value to truncate. The ISO
/// year is the Monday of the first week where Thursday belongs to the corresponding Gregorian
/// calendar year.
public static let isoyear = TimeGranularity(kind: .isoyear)

public let rawValue: String

init(kind: Kind) {
rawValue = kind.rawValue
}
}
Loading
Loading