Skip to content
This repository was archived by the owner on Mar 2, 2026. It is now read-only.

Commit 474e15d

Browse files
Merge branch 'pipeline-preview' into explain_stats
2 parents 6a40bf3 + 2d3ed73 commit 474e15d

4 files changed

Lines changed: 187 additions & 22 deletions

File tree

google/cloud/firestore_v1/pipeline_expressions.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ def sqrt(self) -> "Expression":
396396
return Function("sqrt", [self])
397397

398398
@expose_as_static
399-
def logical_maximum(self, other: Expression | CONSTANT_TYPE) -> "Expression":
399+
def logical_maximum(self, *others: Expression | CONSTANT_TYPE) -> "Expression":
400400
"""Creates an expression that returns the larger value between this expression
401401
and another expression or constant, based on Firestore's value type ordering.
402402
@@ -406,23 +406,23 @@ def logical_maximum(self, other: Expression | CONSTANT_TYPE) -> "Expression":
406406
Example:
407407
>>> # Returns the larger value between the 'discount' field and the 'cap' field.
408408
>>> Field.of("discount").logical_maximum(Field.of("cap"))
409-
>>> # Returns the larger value between the 'value' field and 10.
410-
>>> Field.of("value").logical_maximum(10)
409+
>>> # Returns the larger value between the 'value' field and some ints
410+
>>> Field.of("value").logical_maximum(10, 20, 30)
411411
412412
Args:
413-
other: The other expression or constant value to compare with.
413+
others: The other expression or constant values to compare with.
414414
415415
Returns:
416416
A new `Expression` representing the logical maximum operation.
417417
"""
418418
return Function(
419419
"maximum",
420-
[self, self._cast_to_expr_or_convert_to_constant(other)],
420+
[self] + [self._cast_to_expr_or_convert_to_constant(o) for o in others],
421421
infix_name_override="logical_maximum",
422422
)
423423

424424
@expose_as_static
425-
def logical_minimum(self, other: Expression | CONSTANT_TYPE) -> "Expression":
425+
def logical_minimum(self, *others: Expression | CONSTANT_TYPE) -> "Expression":
426426
"""Creates an expression that returns the smaller value between this expression
427427
and another expression or constant, based on Firestore's value type ordering.
428428
@@ -432,18 +432,18 @@ def logical_minimum(self, other: Expression | CONSTANT_TYPE) -> "Expression":
432432
Example:
433433
>>> # Returns the smaller value between the 'discount' field and the 'floor' field.
434434
>>> Field.of("discount").logical_minimum(Field.of("floor"))
435-
>>> # Returns the smaller value between the 'value' field and 10.
436-
>>> Field.of("value").logical_minimum(10)
435+
>>> # Returns the smaller value between the 'value' field and some ints
436+
>>> Field.of("value").logical_minimum(10, 20, 30)
437437
438438
Args:
439-
other: The other expression or constant value to compare with.
439+
others: The other expression or constant values to compare with.
440440
441441
Returns:
442442
A new `Expression` representing the logical minimum operation.
443443
"""
444444
return Function(
445445
"minimum",
446-
[self, self._cast_to_expr_or_convert_to_constant(other)],
446+
[self] + [self._cast_to_expr_or_convert_to_constant(o) for o in others],
447447
infix_name_override="logical_minimum",
448448
)
449449

@@ -629,6 +629,25 @@ def not_equal_any(
629629
],
630630
)
631631

632+
@expose_as_static
633+
def array_get(self, offset: Expression | int) -> "Function":
634+
"""
635+
Creates an expression that indexes into an array from the beginning or end and returns the
636+
element. A negative offset starts from the end.
637+
638+
Example:
639+
>>> Array([1,2,3]).array_get(0)
640+
641+
Args:
642+
offset: the index of the element to return
643+
644+
Returns:
645+
A new `Expression` representing the `array_get` operation.
646+
"""
647+
return Function(
648+
"array_get", [self, self._cast_to_expr_or_convert_to_constant(offset)]
649+
)
650+
632651
@expose_as_static
633652
def array_contains(
634653
self, element: Expression | CONSTANT_TYPE

tests/system/pipeline_e2e/array.yaml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,3 +386,79 @@ tests:
386386
name: array
387387
name: array_concat
388388
name: select
389+
- description: testArrayGet
390+
pipeline:
391+
- Collection: books
392+
- Where:
393+
- Function.equal:
394+
- Field: title
395+
- Constant: "The Hitchhiker's Guide to the Galaxy"
396+
- Select:
397+
- AliasedExpression:
398+
- Function.array_get:
399+
- Field: tags
400+
- Constant: 0
401+
- "firstTag"
402+
assert_results:
403+
- firstTag: "comedy"
404+
assert_proto:
405+
pipeline:
406+
stages:
407+
- args:
408+
- referenceValue: /books
409+
name: collection
410+
- args:
411+
- functionValue:
412+
args:
413+
- fieldReferenceValue: title
414+
- stringValue: "The Hitchhiker's Guide to the Galaxy"
415+
name: equal
416+
name: where
417+
- args:
418+
- mapValue:
419+
fields:
420+
firstTag:
421+
functionValue:
422+
args:
423+
- fieldReferenceValue: tags
424+
- integerValue: '0'
425+
name: array_get
426+
name: select
427+
- description: testArrayGet_NegativeOffset
428+
pipeline:
429+
- Collection: books
430+
- Where:
431+
- Function.equal:
432+
- Field: title
433+
- Constant: "The Hitchhiker's Guide to the Galaxy"
434+
- Select:
435+
- AliasedExpression:
436+
- Function.array_get:
437+
- Field: tags
438+
- Constant: -1
439+
- "lastTag"
440+
assert_results:
441+
- lastTag: "adventure"
442+
assert_proto:
443+
pipeline:
444+
stages:
445+
- args:
446+
- referenceValue: /books
447+
name: collection
448+
- args:
449+
- functionValue:
450+
args:
451+
- fieldReferenceValue: title
452+
- stringValue: "The Hitchhiker's Guide to the Galaxy"
453+
name: equal
454+
name: where
455+
- args:
456+
- mapValue:
457+
fields:
458+
lastTag:
459+
functionValue:
460+
args:
461+
- fieldReferenceValue: tags
462+
- integerValue: '-1'
463+
name: array_get
464+
name: select

tests/system/pipeline_e2e/logical.yaml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,64 @@ tests:
464464
- doubleValue: 4.5
465465
name: maximum
466466
name: select
467+
- description: testLogicalMinMaxWithMultipleInputs
468+
pipeline:
469+
- Collection: books
470+
- Where:
471+
- Function.equal:
472+
- Field: author
473+
- Constant: Douglas Adams
474+
- Select:
475+
- AliasedExpression:
476+
- Function.logical_maximum:
477+
- Field: rating
478+
- Constant: 4.5
479+
- Constant: 3.0
480+
- Constant: 5.0
481+
- "max_rating"
482+
- AliasedExpression:
483+
- Function.logical_minimum:
484+
- Field: published
485+
- Constant: 1900
486+
- Constant: 2000
487+
- Constant: 1984
488+
- "min_published"
489+
assert_results:
490+
- max_rating: 5.0
491+
min_published: 1900
492+
assert_proto:
493+
pipeline:
494+
stages:
495+
- args:
496+
- referenceValue: /books
497+
name: collection
498+
- args:
499+
- functionValue:
500+
args:
501+
- fieldReferenceValue: author
502+
- stringValue: Douglas Adams
503+
name: equal
504+
name: where
505+
- args:
506+
- mapValue:
507+
fields:
508+
min_published:
509+
functionValue:
510+
args:
511+
- fieldReferenceValue: published
512+
- integerValue: '1900'
513+
- integerValue: '2000'
514+
- integerValue: '1984'
515+
name: minimum
516+
max_rating:
517+
functionValue:
518+
args:
519+
- fieldReferenceValue: rating
520+
- doubleValue: 4.5
521+
- doubleValue: 3.0
522+
- doubleValue: 5.0
523+
name: maximum
524+
name: select
467525
- description: testGreaterThanOrEqual
468526
pipeline:
469527
- Collection: books

tests/unit/v1/test_pipeline_expressions.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,16 @@ def test_or(self):
717717
assert instance.params == [arg1, arg2]
718718
assert repr(instance) == "Or(Arg1, Arg2)"
719719

720+
def test_array_get(self):
721+
arg1 = self._make_arg("ArrayField")
722+
arg2 = self._make_arg("Offset")
723+
instance = Expression.array_get(arg1, arg2)
724+
assert instance.name == "array_get"
725+
assert instance.params == [arg1, arg2]
726+
assert repr(instance) == "ArrayField.array_get(Offset)"
727+
infix_istance = arg1.array_get(arg2)
728+
assert infix_istance == instance
729+
720730
def test_array_contains(self):
721731
arg1 = self._make_arg("ArrayField")
722732
arg2 = self._make_arg("Element")
@@ -989,23 +999,25 @@ def test_divide(self):
989999
assert infix_instance == instance
9901000

9911001
def test_logical_maximum(self):
992-
arg1 = self._make_arg("Left")
993-
arg2 = self._make_arg("Right")
994-
instance = Expression.logical_maximum(arg1, arg2)
1002+
arg1 = self._make_arg("A1")
1003+
arg2 = self._make_arg("A2")
1004+
arg3 = self._make_arg("A3")
1005+
instance = Expression.logical_maximum(arg1, arg2, arg3)
9951006
assert instance.name == "maximum"
996-
assert instance.params == [arg1, arg2]
997-
assert repr(instance) == "Left.logical_maximum(Right)"
998-
infix_instance = arg1.logical_maximum(arg2)
1007+
assert instance.params == [arg1, arg2, arg3]
1008+
assert repr(instance) == "A1.logical_maximum(A2, A3)"
1009+
infix_instance = arg1.logical_maximum(arg2, arg3)
9991010
assert infix_instance == instance
10001011

10011012
def test_logical_minimum(self):
1002-
arg1 = self._make_arg("Left")
1003-
arg2 = self._make_arg("Right")
1004-
instance = Expression.logical_minimum(arg1, arg2)
1013+
arg1 = self._make_arg("A1")
1014+
arg2 = self._make_arg("A2")
1015+
arg3 = self._make_arg("A3")
1016+
instance = Expression.logical_minimum(arg1, arg2, arg3)
10051017
assert instance.name == "minimum"
1006-
assert instance.params == [arg1, arg2]
1007-
assert repr(instance) == "Left.logical_minimum(Right)"
1008-
infix_instance = arg1.logical_minimum(arg2)
1018+
assert instance.params == [arg1, arg2, arg3]
1019+
assert repr(instance) == "A1.logical_minimum(A2, A3)"
1020+
infix_instance = arg1.logical_minimum(arg2, arg3)
10091021
assert infix_instance == instance
10101022

10111023
def test_to_lower(self):

0 commit comments

Comments
 (0)