-
Notifications
You must be signed in to change notification settings - Fork 1.6k
feat(firestore): support array functions #16128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8a02c16
bb7ddf5
975c7e0
35eba8d
dad5c0d
4e1e847
166f180
755e643
2bd5686
496d8e2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1490,6 +1490,250 @@ def last(self) -> "Expression": | |
| """ | ||
| return AggregateFunction("last", [self]) | ||
|
|
||
| @expose_as_static | ||
| def array_first(self) -> "Expression": | ||
| """Creates an expression that returns the first element of an array. | ||
|
|
||
| Example: | ||
| >>> # Select the first element of array 'colors' | ||
| >>> Field.of("colors").array_first() | ||
|
|
||
| Returns: | ||
| A new `Expression` representing the first element of the array. | ||
| """ | ||
| return FunctionExpression("array_first", [self]) | ||
|
|
||
| @expose_as_static | ||
| def array_last(self) -> "Expression": | ||
| """Creates an expression that returns the last element of an array. | ||
|
|
||
| Example: | ||
| >>> # Select the last element of array 'colors' | ||
| >>> Field.of("colors").array_last() | ||
|
|
||
| Returns: | ||
| A new `Expression` representing the last element of the array. | ||
| """ | ||
| return FunctionExpression("array_last", [self]) | ||
|
|
||
| @expose_as_static | ||
| def array_first_n(self, n: int | "Expression") -> "Expression": | ||
| """Creates an expression that returns the first `n` elements of an array. | ||
|
|
||
| Example: | ||
| >>> # Select the first 2 elements of array 'colors' | ||
| >>> Field.of("colors").array_first_n(2) | ||
|
|
||
| Returns: | ||
| A new `Expression` representing the first `n` elements of the array. | ||
| """ | ||
| return FunctionExpression( | ||
| "array_first_n", [self, self._cast_to_expr_or_convert_to_constant(n)] | ||
| ) | ||
|
|
||
| @expose_as_static | ||
| def array_last_n(self, n: int | "Expression") -> "Expression": | ||
| """Creates an expression that returns the last `n` elements of an array. | ||
|
|
||
| Example: | ||
| >>> # Select the last 2 elements of array 'colors' | ||
| >>> Field.of("colors").array_last_n(2) | ||
|
|
||
| Returns: | ||
| A new `Expression` representing the last `n` elements of the array. | ||
| """ | ||
| return FunctionExpression( | ||
| "array_last_n", [self, self._cast_to_expr_or_convert_to_constant(n)] | ||
| ) | ||
|
|
||
| @expose_as_static | ||
| def array_maximum(self) -> "Expression": | ||
| """Creates an expression that returns the maximum element of an array. | ||
|
|
||
| Example: | ||
| >>> # Select the maximum element of array 'scores' | ||
| >>> Field.of("scores").array_maximum() | ||
|
|
||
| Returns: | ||
| A new `Expression` representing the maximum element of the array. | ||
| """ | ||
| return FunctionExpression("maximum", [self]) | ||
|
|
||
| @expose_as_static | ||
| def array_minimum(self) -> "Expression": | ||
| """Creates an expression that returns the minimum element of an array. | ||
|
|
||
| Example: | ||
| >>> # Select the minimum element of array 'scores' | ||
| >>> Field.of("scores").array_minimum() | ||
|
|
||
| Returns: | ||
| A new `Expression` representing the minimum element of the array. | ||
| """ | ||
| return FunctionExpression("minimum", [self]) | ||
|
|
||
| @expose_as_static | ||
| def array_maximum_n(self, n: int | "Expression") -> "Expression": | ||
| """Creates an expression that returns the maximum `n` elements of an array. | ||
|
|
||
| Example: | ||
| >>> # Select the maximum 2 elements of array 'scores' | ||
| >>> Field.of("scores").array_maximum_n(2) | ||
|
|
||
| Note: | ||
| Returns the n largest non-null elements in the array, in descending | ||
| order. This does not use a stable sort, meaning the order of equivalent | ||
| elements is undefined. | ||
|
|
||
| Returns: | ||
| A new `Expression` representing the maximum `n` elements of the array. | ||
| """ | ||
| return FunctionExpression( | ||
| "maximum_n", [self, self._cast_to_expr_or_convert_to_constant(n)] | ||
| ) | ||
|
|
||
| @expose_as_static | ||
| def array_minimum_n(self, n: int | "Expression") -> "Expression": | ||
| """Creates an expression that returns the minimum `n` elements of an array. | ||
|
|
||
| Example: | ||
| >>> # Select the minimum 2 elements of array 'scores' | ||
| >>> Field.of("scores").array_minimum_n(2) | ||
|
|
||
| Note: | ||
| Returns the n smallest non-null elements in the array, in ascending | ||
| order. This does not use a stable sort, meaning the order of equivalent | ||
| elements is undefined. | ||
|
|
||
| Returns: | ||
| A new `Expression` representing the minimum `n` elements of the array. | ||
| """ | ||
| return FunctionExpression( | ||
| "minimum_n", [self, self._cast_to_expr_or_convert_to_constant(n)] | ||
| ) | ||
|
|
||
| @expose_as_static | ||
| def array_slice( | ||
| self, offset: int | "Expression", length: int | "Expression" | None = None | ||
| ) -> "Expression": | ||
| """Ccreates an expression that returns a slice of an array starting from the specified | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| offset with a given length. | ||
|
|
||
| Example: | ||
| >>> # Slice array 'scores' starting at index 1 with length 2 | ||
| >>> Field.of("scores").array_slice(1, 2) | ||
|
|
||
| Args: | ||
| offset: the 0-based index of the first element to include. | ||
| length: The number of elements to include in the slice. If omitted, slices to the end. | ||
|
|
||
| Returns: | ||
| A new `Expression` representing the slice of the array. | ||
| """ | ||
| args = [self, self._cast_to_expr_or_convert_to_constant(offset)] | ||
| if length is not None: | ||
| args.append(self._cast_to_expr_or_convert_to_constant(length)) | ||
| return FunctionExpression("array_slice", args) | ||
|
|
||
| @expose_as_static | ||
| def array_index_of(self, search: "Expression" | CONSTANT_TYPE) -> "Expression": | ||
| """Creates an expression that returns the index of a value in an array. | ||
|
|
||
| Returns -1 if the value is not found. | ||
|
|
||
| Example: | ||
| >>> # Get the index of "comedy" in the 'tags' array | ||
| >>> Field.of("tags").array_index_of("comedy") | ||
|
|
||
| Args: | ||
| search: The element (expression or constant) to find the index of. | ||
|
|
||
| Returns: | ||
| A new `Expression` representing the 'array_index_of' value. | ||
| """ | ||
| return FunctionExpression( | ||
| "array_index_of", | ||
| [ | ||
| self, | ||
| self._cast_to_expr_or_convert_to_constant(search), | ||
| self._cast_to_expr_or_convert_to_constant("first"), | ||
| ], | ||
| ) | ||
|
|
||
| @expose_as_static | ||
| def array_index_of_all(self, search: "Expression" | CONSTANT_TYPE) -> "Expression": | ||
| """Creates an expression that returns all indices of a value in an array. | ||
| Returns an empty array if the value is not found. | ||
|
|
||
| Example: | ||
| >>> # Get all indices of "comedy" in the 'tags' array | ||
| >>> Field.of("tags").array_index_of_all("comedy") | ||
|
|
||
| Args: | ||
| search: The element (expression or constant) to find the indices of. | ||
|
|
||
| Returns: | ||
| A new `Expression` representing the 'array_index_of_all' value. | ||
| """ | ||
| return FunctionExpression( | ||
| "array_index_of_all", | ||
| [self, self._cast_to_expr_or_convert_to_constant(search)], | ||
| ) | ||
|
|
||
| @expose_as_static | ||
| def array_transform( | ||
| self, element_alias: str, body: "Expression", index_alias: str | None = None | ||
| ) -> "Expression": | ||
|
Comment on lines
+1684
to
+1686
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The method signature for To improve clarity and prevent misuse, consider making def array_transform(
self, element_alias: str, body: "Expression", *, index_alias: str | None = None
) -> "Expression": |
||
| """Creates an expression that transforms elements of an array. | ||
|
|
||
| Example: | ||
| >>> # Transform each element by adding 1. | ||
| >>> Field.of("nums").array_transform("e", Field.of("e").add(1)) | ||
| >>> | ||
| >>> # Transform each element by adding its index to it. | ||
| >>> Field.of("nums").array_transform("e", Field.of("e").add(Field.of("i")), index_alias="i") | ||
|
|
||
| Args: | ||
| element_alias: The variable name to use for the current element within the body expression. | ||
| body: The expression to apply to each element. | ||
| index_alias: The variable name to use for the current index within the body expression. | ||
|
|
||
| Returns: | ||
| A new `Expression` applying the transformation to the array elements. | ||
| """ | ||
| args = [ | ||
| self, | ||
| self._cast_to_expr_or_convert_to_constant(element_alias), | ||
| ] | ||
| if index_alias is not None: | ||
| args.append(self._cast_to_expr_or_convert_to_constant(index_alias)) | ||
|
|
||
| args.append(self._cast_to_expr_or_convert_to_constant(body)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The This also applies to args.append(body) |
||
|
|
||
| return FunctionExpression("array_transform", args) | ||
|
|
||
| @expose_as_static | ||
| def array_filter(self, element_alias: str, body: "Expression") -> "Expression": | ||
| """ | ||
| Takes an array, evaluates a boolean expression on each element, and returns a new | ||
| array containing only the elements for which the expression evaluates to True. | ||
|
|
||
| Args: | ||
| element_alias: Element variable name. | ||
| body: Boolean expression applied to each element. | ||
|
|
||
| Returns: | ||
| Expression: The created FunctionExpression AST node. | ||
| """ | ||
|
Comment on lines
+1717
to
+1727
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The docstring for
Please update it for consistency. For example: """
Takes an array and returns a new array containing only the elements for which
the given boolean expression evaluates to True.
Example:
>>> # Filter for numbers greater than 5
>>> Field.of("numbers").array_filter("num", Field.of("num").greater_than(5))
Args:
element_alias: Element variable name.
body: Boolean expression applied to each element.
Returns:
A new `Expression` representing the filtered array.
""" |
||
| return FunctionExpression( | ||
| "array_filter", | ||
| [ | ||
| self, | ||
| self._cast_to_expr_or_convert_to_constant(element_alias), | ||
| self._cast_to_expr_or_convert_to_constant(body), | ||
| ], | ||
| ) | ||
|
|
||
| @expose_as_static | ||
| def unix_micros_to_timestamp(self) -> "Expression": | ||
| """Creates an expression that converts a number of microseconds since the epoch (1970-01-01 | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function name used for
array_maximumis"maximum". This is inconsistent with other new array functions likearray_firstwhich uses"array_first". For consistency, this should probably be"array_maximum".This same issue applies to:
array_minimum(uses"minimum")array_maximum_n(uses"maximum_n")array_minimum_n(uses"minimum_n")Please update them all to use the
array_prefix for consistency and to prevent potential backend errors.