Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,9 @@ def array_get(self, offset: Expression | int) -> "FunctionExpression":
Creates an expression that indexes into an array from the beginning or end and returns the
element. A negative offset starts from the end.

If the expression is evaluated against a non-array type, it evaluates to an error. See `offset`
for an alternative that evaluates to unset instead.

Example:
>>> Array([1,2,3]).array_get(0)

Expand All @@ -774,6 +777,26 @@ def array_get(self, offset: Expression | int) -> "FunctionExpression":
"array_get", [self, self._cast_to_expr_or_convert_to_constant(offset)]
)

@expose_as_static
def offset(self, offset: Expression | int) -> "FunctionExpression":
"""
Creates an expression that indexes into an array from the beginning or end and returns the
element. A negative offset starts from the end.
If the expression is evaluated against a non-array type, it evaluates to unset.

Example:
>>> Array([1,2,3]).offset(0)

Args:
offset: the index of the element to return

Returns:
A new `Expression` representing the `offset` operation.
"""
return FunctionExpression(
"offset", [self, self._cast_to_expr_or_convert_to_constant(offset)]
)

@expose_as_static
def array_contains(
self, element: Expression | CONSTANT_TYPE
Expand Down Expand Up @@ -1006,6 +1029,75 @@ def exists(self) -> "BooleanExpression":
"""
return BooleanExpression("exists", [self])

@expose_as_static
def coalesce(self, *others: Expression | CONSTANT_TYPE) -> "Expression":
"""Creates an expression that evaluates to the first non-null, non-error value.

Example:
>>> # Return the "preferredName" field if it exists.
>>> # Otherwise, check the "fullName" field.
>>> # Otherwise, return the literal string "Anonymous".
>>> Field.of("preferredName").coalesce(Field.of("fullName"), "Anonymous")

>>> # Equivalent static call:
>>> Expression.coalesce(Field.of("preferredName"), Field.of("fullName"), "Anonymous")

Args:
*others: Additional expressions or constants to evaluate if the current
expression evaluates to null or error.

Returns:
An Expression representing the coalesce operation.
"""
args = [self]
args.extend(
[Expression._cast_to_expr_or_convert_to_constant(x) for x in others]
)
return FunctionExpression("coalesce", args)

@expose_as_static
def switch_on(
self, result: Expression | CONSTANT_TYPE, *others: Expression | CONSTANT_TYPE
) -> "Expression":
"""Creates an expression that evaluates to the result corresponding to the first true condition.

This function behaves like a `switch` statement. It accepts an alternating sequence of
conditions and their corresponding results. If an odd number of arguments is provided, the
final argument serves as a default fallback result. If no default is provided and no condition
evaluates to true, it throws an error.

Example:
>>> # Return "Pending" if status is 1, "Active" if status is 2, otherwise "Unknown"
>>> Field.of("status").equal(1).switch_on(
... "Pending", Field.of("status").equal(2), "Active", "Unknown"
... )

Args:
result: The result to return if this condition is true.
*others: Additional alternating conditions and results, optionally followed by a default value.

Returns:
An Expression representing the switchOn operation.
"""
args = [self, Expression._cast_to_expr_or_convert_to_constant(result)]
args.extend(
[Expression._cast_to_expr_or_convert_to_constant(x) for x in others]
)
return FunctionExpression("switch_on", args)

@expose_as_static
def parent(self) -> "Expression":
"""Creates an expression that returns the parent document of a document reference.

Example:
>>> # Get the parent document of a document reference.
>>> Field.of("__path__").parent()

Returns:
An Expression representing the parent operation.
"""
return FunctionExpression("parent", [self])

@expose_as_static
def sum(self) -> "Expression":
"""Creates an aggregation that calculates the sum of a numeric field across multiple stage inputs.
Expand Down Expand Up @@ -1366,6 +1458,7 @@ def join(self, delimeter: Expression | str) -> "Expression":
@expose_as_static
def map_get(self, key: str | Constant[str]) -> "Expression":
"""Accesses a value from the map produced by evaluating this expression.
If the expression is evaluated against a non-map type, it evaluates to an error.

Example:
>>> Map({"city": "London"}).map_get("city")
Expand Down Expand Up @@ -2808,6 +2901,22 @@ def __init__(self, *conditions: "BooleanExpression"):
super().__init__("or", conditions, use_infix_repr=False)


class Nor(BooleanExpression):
"""
Represents an expression that performs a logical 'NOR' operation on multiple filter conditions.

Example:
>>> # Check if neither the 'age' field is greater than 18 nor the 'city' field is "London"
>>> Nor(Field.of("age").greater_than(18), Field.of("city").equal("London"))

Args:
*conditions: The filter conditions to 'NOR' together.
"""

def __init__(self, *conditions: "BooleanExpression"):
super().__init__("nor", conditions, use_infix_repr=False)


class Xor(BooleanExpression):
"""
Represents an expression that performs a logical 'XOR' (exclusive OR) operation on multiple filter conditions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,60 @@ tests:
- integerValue: '0'
name: array_get
name: select
- description: testArrayGet_NonArray
pipeline:
- Collection: books
- Where:
- FunctionExpression.equal:
- Field: title
- Constant: "The Hitchhiker's Guide to the Galaxy"
- Select:
- AliasedExpression:
- FunctionExpression.is_error:
- FunctionExpression.array_get:
- Field: title
- Constant: 0
- "isError"
assert_results:
- isError: true
assert_proto:
pipeline:
stages:
- args:
- referenceValue: /books
name: collection
- args:
- functionValue:
args:
- fieldReferenceValue: title
- stringValue: "The Hitchhiker's Guide to the Galaxy"
name: equal
name: where
- args:
- mapValue:
fields:
isError:
functionValue:
args:
- functionValue:
args:
- fieldReferenceValue: title
- integerValue: '0'
name: array_get
name: is_error
name: select
- description: testOffset_NonArray
pipeline:
- Collection: books
- Where:
- FunctionExpression.equal:
- Field: title
- Constant: "The Hitchhiker's Guide to the Galaxy"
- Where:
- FunctionExpression.offset:
- Field: title
- Constant: 0
assert_count: 0
- description: testArrayGet_NegativeOffset
pipeline:
- Collection: books
Expand Down Expand Up @@ -462,6 +516,44 @@ tests:
- integerValue: '-1'
name: array_get
name: select
- description: testOffset
pipeline:
- Collection: books
- Where:
- FunctionExpression.equal:
- Field: title
- Constant: "The Hitchhiker's Guide to the Galaxy"
- Select:
- AliasedExpression:
- FunctionExpression.offset:
- Field: tags
- Constant: -1
- "lastTag"
assert_results:
- lastTag: "adventure"
assert_proto:
pipeline:
stages:
- args:
- referenceValue: /books
name: collection
- args:
- functionValue:
args:
- fieldReferenceValue: title
- stringValue: "The Hitchhiker's Guide to the Galaxy"
name: equal
name: where
- args:
- mapValue:
fields:
lastTag:
functionValue:
args:
- fieldReferenceValue: tags
- integerValue: '-1'
name: offset
name: select
- description: testArrayFirst
pipeline:
- Collection: books
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -765,3 +765,59 @@ tests:
res:
fieldReferenceValue: res
name: select
- description: testCoalesce
pipeline:
- Literals:
- res:
FunctionExpression.coalesce:
- Constant: null
- Constant: "B"
- Select:
- res
assert_results:
- res: "B"
- description: testSwitchOn
pipeline:
- Literals:
- res:
FunctionExpression.switch_on:
- FunctionExpression.equal:
- Constant: 1
- Constant: 2
- Constant: "A"
- FunctionExpression.equal:
- Constant: 1
- Constant: 1
- Constant: "B"
- Constant: "C"
- Select:
- res
assert_results:
- res: "B"
- description: testParent
pipeline:
- Collection: books
- Limit: 1
- Select:
- AliasedExpression:
- FunctionExpression.parent:
- Field: __path__
- res
assert_proto:
pipeline:
stages:
- args:
- referenceValue: /books
name: collection
- args:
- integerValue: '1'
name: limit
- args:
- mapValue:
fields:
res:
functionValue:
args:
- fieldReferenceValue: __path__
name: parent
name: select
Original file line number Diff line number Diff line change
Expand Up @@ -759,3 +759,35 @@ tests:
- "value_or_default"
assert_results:
- value_or_default: "1984"
- description: whereByNorCondition
pipeline:
- Collection: books
- Where:
- Nor:
- FunctionExpression.greater_than:
- Field: rating
- Constant: 4.5
- FunctionExpression.equal:
- Field: genre
- Constant: Science Fiction
assert_proto:
pipeline:
stages:
- args:
- referenceValue: /books
name: collection
- args:
- functionValue:
args:
- functionValue:
args:
- fieldReferenceValue: rating
- doubleValue: 4.5
name: greater_than
- functionValue:
args:
- fieldReferenceValue: genre
- stringValue: Science Fiction
name: equal
name: nor
name: where
Loading
Loading