diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_expressions.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_expressions.py index 376d785901fc..f54c9059aa51 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_expressions.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_expressions.py @@ -405,6 +405,20 @@ def sqrt(self) -> "Expression": """ return FunctionExpression("sqrt", [self]) + @expose_as_static + def trunc(self, places: "Expression" | None = None) -> "Expression": + """Function to truncate a numeric expression to the nearest whole number towards zero. + + Example: + >>> # Truncate the 'value' field to 2 decimal places. + >>> Field.of("value").trunc(PipelineSource.literals(2)) + + Returns: + A new `Expression` representing the truncated value. + """ + params = [self, places] if places is not None else [self] + return FunctionExpression("trunc", params) + @expose_as_static def logical_maximum(self, *others: Expression | CONSTANT_TYPE) -> "Expression": """Creates an expression that returns the larger value between this expression @@ -2029,3 +2043,15 @@ class CurrentTimestamp(FunctionExpression): def __init__(self): super().__init__("current_timestamp", [], use_infix_repr=False) + + +class Rand(FunctionExpression): + """Creates an expression that generates a random number between 0.0 and 1.0 but not + including 1.0. + + Returns: + A new `Expression` representing the rand operation. + """ + + def __init__(self): + super().__init__("rand", [], use_infix_repr=False) diff --git a/packages/google-cloud-firestore/tests/system/pipeline_e2e/math.yaml b/packages/google-cloud-firestore/tests/system/pipeline_e2e/math.yaml index 4d35f746d3f4..5b05c24aeb71 100644 --- a/packages/google-cloud-firestore/tests/system/pipeline_e2e/math.yaml +++ b/packages/google-cloud-firestore/tests/system/pipeline_e2e/math.yaml @@ -134,6 +134,83 @@ tests: - fieldReferenceValue: rating name: sqrt name: select + - description: testTrunc + pipeline: + - Collection: books + - Where: + - FunctionExpression.equal_any: + - Field: title + - - Constant: "To Kill a Mockingbird" # rating 4.2 + - Constant: "Pride and Prejudice" # rating 4.5 + - Constant: "The Lord of the Rings" # rating 4.7 + - Select: + - title + - AliasedExpression: + - FunctionExpression.trunc: + - Field: rating + - "trunc_rating" + - AliasedExpression: + - FunctionExpression.trunc: + - Field: rating + - Constant: 1 + - "trunc_rating_with_places" + - Sort: + - Ordering: + - Field: title + - ASCENDING + assert_results: + - title: "Pride and Prejudice" + trunc_rating: 4.0 + trunc_rating_with_places: 4.5 + - title: "The Lord of the Rings" + trunc_rating: 4.0 + trunc_rating_with_places: 4.7 + - title: "To Kill a Mockingbird" + trunc_rating: 4.0 + trunc_rating_with_places: 4.2 + assert_proto: + pipeline: + stages: + - args: + - referenceValue: /books + name: collection + - args: + - functionValue: + args: + - fieldReferenceValue: title + - functionValue: + args: + - stringValue: "To Kill a Mockingbird" + - stringValue: "Pride and Prejudice" + - stringValue: "The Lord of the Rings" + name: array + name: equal_any + name: where + - args: + - mapValue: + fields: + title: + fieldReferenceValue: title + trunc_rating: + functionValue: + args: + - fieldReferenceValue: rating + name: trunc + trunc_rating_with_places: + functionValue: + args: + - fieldReferenceValue: rating + - integerValue: '1' + name: trunc + name: select + - args: + - mapValue: + fields: + direction: + stringValue: ascending + expression: + fieldReferenceValue: title + name: sort - description: testRoundFunctionExpressionessions pipeline: - Collection: books @@ -306,4 +383,49 @@ tests: - fieldReferenceValue: rating - integerValue: '2' name: mod + name: select + - description: testRand + pipeline: + - Collection: books + - Limit: 1 + - Select: + - AliasedExpression: + - And: + - FunctionExpression.greater_than_or_equal: + - Rand: [] + - Constant: 0.0 + - FunctionExpression.less_than: + - Rand: [] + - Constant: 1.0 + - "is_valid_rand" + assert_results: + - is_valid_rand: true + assert_proto: + pipeline: + stages: + - args: + - referenceValue: /books + name: collection + - args: + - integerValue: '1' + name: limit + - args: + - mapValue: + fields: + is_valid_rand: + functionValue: + name: and + args: + - functionValue: + name: greater_than_or_equal + args: + - functionValue: + name: rand + - doubleValue: 0.0 + - functionValue: + name: less_than + args: + - functionValue: + name: rand + - doubleValue: 1.0 name: select \ No newline at end of file diff --git a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_expressions.py b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_expressions.py index fe7beb460502..eff8512346ca 100644 --- a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_expressions.py +++ b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_expressions.py @@ -1418,6 +1418,23 @@ def test_sqrt(self): infix_instance = arg1.sqrt() assert infix_instance == instance + def test_trunc(self): + arg1 = self._make_arg("Value") + instance = Expression.trunc(arg1) + assert instance.name == "trunc" + assert instance.params == [arg1] + assert repr(instance) == "Value.trunc()" + infix_instance = arg1.trunc() + assert infix_instance == instance + + places = self._make_arg("Places") + instance_with_places = Expression.trunc(arg1, places) + assert instance_with_places.name == "trunc" + assert instance_with_places.params == [arg1, places] + assert repr(instance_with_places) == "Value.trunc(Places)" + infix_instance_with_places = arg1.trunc(places) + assert infix_instance_with_places == instance_with_places + def test_array_length(self): arg1 = self._make_arg("Array") instance = Expression.array_length(arg1) @@ -1583,3 +1600,9 @@ def test_maximum(self): assert repr(instance) == "Value.maximum()" infix_instance = arg1.maximum() assert infix_instance == instance + + def test_rand(self): + instance = expr.Rand() + assert instance.name == "rand" + assert instance.params == [] + assert repr(instance) == "Rand()"