From 2a8213cf5ff647529960c84c528bbddd2677ccae Mon Sep 17 00:00:00 2001 From: Linchin Date: Thu, 5 Mar 2026 00:26:49 +0000 Subject: [PATCH 01/17] feat: literals pipeline stage --- .../cloud/firestore_v1/base_pipeline.py | 65 +++++++++++++++++++ .../cloud/firestore_v1/pipeline_stages.py | 17 +++++ .../tests/system/pipeline_e2e/general.yaml | 21 +++++- .../tests/unit/v1/test_pipeline.py | 1 + .../tests/unit/v1/test_pipeline_stages.py | 29 +++++++++ 5 files changed, 132 insertions(+), 1 deletion(-) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py index fac7f8bc4bce..fe42785a9aad 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py @@ -275,6 +275,71 @@ def find_nearest( stages.FindNearest(field, vector, distance_measure, options) ) + def literals(self, *documents: str | Selectable) -> "_BasePipeline": + """ + Returns documents from a fixed set of predefined document objects. + + This stage is commonly used for testing other stages in isolation, + though it can also be used as inputs to join conditions. + + Example: + >>> from google.cloud.firestore_v1.pipeline_expressions import Constant + >>> documents = [ + ... {"name": "joe", "age": 10}, + ... {"name": "bob", "age": 30}, + ... {"name": "alice", "age": 40} + ... ] + >>> pipeline = client.pipeline() + ... .literals(Constant.of(documents)) + ... .where(field("age").lessThan(35)) + + Output documents: + ```json + [ + {"name": "joe", "age": 10}, + {"name": "bob", "age": 30} + ] + ``` + + Behavior: + The `literals(...)` stage can only be used as the first stage in a pipeline (or + sub-pipeline). The order of documents returned from the `literals` matches the + order in which they are defined. + + While literal values are the most common, it is also possible to pass in + expressions, which will be evaluated and returned, making it possible to test + out different query / expression behavior without first needing to create some + test data. + + For example, the following shows how to quickly test out the `length(...)` + function on some constant test sets: + + Example: + >>> from google.cloud.firestore_v1.pipeline_expressions import Constant + >>> documents = [ + ... {"x": Constant.of("foo-bar-baz").char_length()}, + ... {"x": Constant.of("bar").char_length()} + ... ] + >>> pipeline = client.pipeline().literals(Constant.of(documents)) + + Output documents: + ```json + [ + {"x": 11}, + {"x": 3} + ] + ``` + + Args: + documents: A `str` or `Selectable` expression. If a `str`, it's + treated as a field path to an array of documents. + If a `Selectable`, it's usually a `Constant` + containing an array of documents (as dictionaries). + Returns: + A new Pipeline object with this stage appended to the stage list. + """ + return self._append(stages.Literals(*documents)) + def replace_with( self, field: Selectable, diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py index b00d923c673c..cd48960da1de 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py @@ -342,6 +342,23 @@ def _pb_args(self): return [Value(integer_value=self.limit)] +class Literals(Stage): + """Returns documents from a fixed set of predefined document objects.""" + + def __init__(self, *documents: str | Selectable): + super().__init__("literals") + self.documents = documents + + def _pb_args(self): + args = [] + for doc in self.documents: + if hasattr(doc, "_to_pb"): + args.append(doc._to_pb()) + else: + args.append(encode_value(doc)) + return args + + class Offset(Stage): """Skips a specified number of documents.""" diff --git a/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml b/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml index 46a10cd4d1af..bcd81916be84 100644 --- a/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml +++ b/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml @@ -684,4 +684,23 @@ tests: - args: - fieldReferenceValue: awards - stringValue: full_replace - name: replace_with \ No newline at end of file + name: replace_with + - description: literals + pipeline: + - Literals: + - title: "The Hitchhiker's Guide to the Galaxy" + author: "Douglas Adams" + assert_results: + - title: "The Hitchhiker's Guide to the Galaxy" + author: "Douglas Adams" + assert_proto: + pipeline: + stages: + - args: + - mapValue: + fields: + author: + stringValue: "Douglas Adams" + title: + stringValue: "The Hitchhiker's Guide to the Galaxy" + name: literals \ No newline at end of file diff --git a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline.py b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline.py index 5953398709a3..d264f714ddaa 100644 --- a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline.py +++ b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline.py @@ -417,6 +417,7 @@ def test_pipeline_execute_stream_equivalence(): ("aggregate", (Field.of("n").as_("alias"),), stages.Aggregate), ("distinct", ("field_name",), stages.Distinct), ("distinct", (Field.of("n"), "second"), stages.Distinct), + ("literals", (Field.of("a"),), stages.Literals), ], ) def test_pipeline_methods(method, args, result_cls): diff --git a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py index b32a6e5d3f13..e3693c8415d6 100644 --- a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py +++ b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py @@ -517,6 +517,35 @@ def test_to_pb(self): assert len(result.options) == 0 +class TestLiterals: + def _make_one(self, *args, **kwargs): + return stages.Literals(*args, **kwargs) + + def test_ctor(self): + val1 = Constant.of({"a": 1}) + val2 = Constant.of({"b": 2}) + instance = self._make_one(val1, val2) + assert instance.documents == (val1, val2) + assert instance.name == "literals" + + def test_repr(self): + val1 = Constant.of({"a": 1}) + instance = self._make_one(val1) + repr_str = repr(instance) + assert repr_str == "Literals(documents=(Constant.of({'a': 1}),))" + + def test_to_pb(self): + val1 = Constant.of({"a": 1}) + val2 = Constant.of({"b": 2}) + instance = self._make_one(val1, val2) + result = instance._to_pb() + assert result.name == "literals" + assert len(result.args) == 2 + assert result.args[0].map_value.fields["a"].integer_value == 1 + assert result.args[1].map_value.fields["b"].integer_value == 2 + assert len(result.options) == 0 + + class TestOffset: def _make_one(self, *args, **kwargs): return stages.Offset(*args, **kwargs) From 28acee915967602e06f179e164242c6aa5cdf7df Mon Sep 17 00:00:00 2001 From: Linchin Date: Fri, 6 Mar 2026 21:37:01 +0000 Subject: [PATCH 02/17] add dict as supported type and update docstring --- .../google/cloud/firestore_v1/base_pipeline.py | 2 +- .../google/cloud/firestore_v1/pipeline_stages.py | 2 +- .../tests/unit/v1/test_pipeline_stages.py | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py index fe42785a9aad..02e2ceecaa52 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py @@ -275,7 +275,7 @@ def find_nearest( stages.FindNearest(field, vector, distance_measure, options) ) - def literals(self, *documents: str | Selectable) -> "_BasePipeline": + def literals(self, *documents: Selectable | dict) -> "_BasePipeline": """ Returns documents from a fixed set of predefined document objects. diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py index cd48960da1de..9ccb1d5aaa69 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py @@ -345,7 +345,7 @@ def _pb_args(self): class Literals(Stage): """Returns documents from a fixed set of predefined document objects.""" - def __init__(self, *documents: str | Selectable): + def __init__(self, *documents: Selectable | dict): super().__init__("literals") self.documents = documents diff --git a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py index e3693c8415d6..82fe3e1881b2 100644 --- a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py +++ b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py @@ -523,20 +523,20 @@ def _make_one(self, *args, **kwargs): def test_ctor(self): val1 = Constant.of({"a": 1}) - val2 = Constant.of({"b": 2}) + val2 = {"b": 2} instance = self._make_one(val1, val2) assert instance.documents == (val1, val2) assert instance.name == "literals" def test_repr(self): val1 = Constant.of({"a": 1}) - instance = self._make_one(val1) + instance = self._make_one(val1, {"b": 2}) repr_str = repr(instance) - assert repr_str == "Literals(documents=(Constant.of({'a': 1}),))" + assert repr_str == "Literals(documents=(Constant.of({'a': 1}), {'b': 2}))" def test_to_pb(self): val1 = Constant.of({"a": 1}) - val2 = Constant.of({"b": 2}) + val2 = {"b": 2} instance = self._make_one(val1, val2) result = instance._to_pb() assert result.name == "literals" From a2e11ed359427538ee8a2027c846cbba7006c940 Mon Sep 17 00:00:00 2001 From: Linchin Date: Mon, 9 Mar 2026 19:13:40 +0000 Subject: [PATCH 03/17] correct docstring and type annotation --- .../google/cloud/firestore_v1/base_pipeline.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py index 02e2ceecaa52..4db636fe9d41 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py @@ -275,7 +275,7 @@ def find_nearest( stages.FindNearest(field, vector, distance_measure, options) ) - def literals(self, *documents: Selectable | dict) -> "_BasePipeline": + def literals(self, *documents: Expression | dict) -> "_BasePipeline": """ Returns documents from a fixed set of predefined document objects. @@ -290,7 +290,7 @@ def literals(self, *documents: Selectable | dict) -> "_BasePipeline": ... {"name": "alice", "age": 40} ... ] >>> pipeline = client.pipeline() - ... .literals(Constant.of(documents)) + ... .literals(documents) ... .where(field("age").lessThan(35)) Output documents: @@ -320,7 +320,7 @@ def literals(self, *documents: Selectable | dict) -> "_BasePipeline": ... {"x": Constant.of("foo-bar-baz").char_length()}, ... {"x": Constant.of("bar").char_length()} ... ] - >>> pipeline = client.pipeline().literals(Constant.of(documents)) + >>> pipeline = client.pipeline().literals(documents) Output documents: ```json @@ -331,10 +331,8 @@ def literals(self, *documents: Selectable | dict) -> "_BasePipeline": ``` Args: - documents: A `str` or `Selectable` expression. If a `str`, it's - treated as a field path to an array of documents. - If a `Selectable`, it's usually a `Constant` - containing an array of documents (as dictionaries). + documents: One or more documents to be returned by this stage. Each can be a `dict` + or an `Expression`. Returns: A new Pipeline object with this stage appended to the stage list. """ From a97cb134040b0e429b83bfd60d6ae79b5c9dd57c Mon Sep 17 00:00:00 2001 From: Linchin Date: Mon, 9 Mar 2026 19:14:55 +0000 Subject: [PATCH 04/17] type annotation --- .../google/cloud/firestore_v1/pipeline_stages.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py index 9ccb1d5aaa69..30d2fd8e936a 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py @@ -345,7 +345,7 @@ def _pb_args(self): class Literals(Stage): """Returns documents from a fixed set of predefined document objects.""" - def __init__(self, *documents: Selectable | dict): + def __init__(self, *documents: Expression | dict): super().__init__("literals") self.documents = documents From cc1b737571cdc660ed6cdee9e407814900d91b81 Mon Sep 17 00:00:00 2001 From: Linchin Date: Mon, 9 Mar 2026 20:33:45 +0000 Subject: [PATCH 05/17] add more unit tests --- packages/google-cloud-firestore/tests/unit/v1/test_pipeline.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline.py b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline.py index d264f714ddaa..5ec2e9ac1123 100644 --- a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline.py +++ b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline.py @@ -418,6 +418,8 @@ def test_pipeline_execute_stream_equivalence(): ("distinct", ("field_name",), stages.Distinct), ("distinct", (Field.of("n"), "second"), stages.Distinct), ("literals", (Field.of("a"),), stages.Literals), + ("literals", ({"name": "joe"}, {"name": "bob"}), stages.Literals), + ("literals", (Field.of("a"), {"name": "joe"}), stages.Literals), ], ) def test_pipeline_methods(method, args, result_cls): From 386122314ff9b244be18f81a7a4a482ec0540762 Mon Sep 17 00:00:00 2001 From: Linchin Date: Mon, 9 Mar 2026 20:35:46 +0000 Subject: [PATCH 06/17] correct docstring --- .../google/cloud/firestore_v1/base_pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py index 4db636fe9d41..5b7f234f7d0f 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py @@ -331,7 +331,7 @@ def literals(self, *documents: Expression | dict) -> "_BasePipeline": ``` Args: - documents: One or more documents to be returned by this stage. Each can be a `dict` + *documents: One or more documents to be returned by this stage. Each can be a `dict` or an `Expression`. Returns: A new Pipeline object with this stage appended to the stage list. From 7bad13cbf5b8636a422565accc6054911a5cc6fc Mon Sep 17 00:00:00 2001 From: Linchin Date: Mon, 9 Mar 2026 21:11:38 +0000 Subject: [PATCH 07/17] add cases to system test --- .../tests/system/pipeline_e2e/general.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml b/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml index bcd81916be84..37fbbdd54dc6 100644 --- a/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml +++ b/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml @@ -690,6 +690,10 @@ tests: - Literals: - title: "The Hitchhiker's Guide to the Galaxy" author: "Douglas Adams" + - Constant: + value: + genre: "Science Fiction" + year: 1979 assert_results: - title: "The Hitchhiker's Guide to the Galaxy" author: "Douglas Adams" @@ -703,4 +707,10 @@ tests: stringValue: "Douglas Adams" title: stringValue: "The Hitchhiker's Guide to the Galaxy" + - mapValue: + fields: + genre: + stringValue: "Science Fiction" + year: + integerValue: '1979' name: literals \ No newline at end of file From 36e45bf5e8ff95d7477d7636727658d78b8566d9 Mon Sep 17 00:00:00 2001 From: Linchin Date: Tue, 10 Mar 2026 21:08:08 +0000 Subject: [PATCH 08/17] fix system test --- .../tests/system/pipeline_e2e/general.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml b/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml index 37fbbdd54dc6..d4aa80ba8d7b 100644 --- a/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml +++ b/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml @@ -697,6 +697,8 @@ tests: assert_results: - title: "The Hitchhiker's Guide to the Galaxy" author: "Douglas Adams" + - genre: "Science Fiction" + year: 1979 assert_proto: pipeline: stages: From 816498e3a1278624af166812e273646d196068ad Mon Sep 17 00:00:00 2001 From: Linchin Date: Wed, 11 Mar 2026 20:36:14 +0000 Subject: [PATCH 09/17] move literals to pipeline_source.py --- .../cloud/firestore_v1/base_pipeline.py | 62 ------------------ .../cloud/firestore_v1/pipeline_source.py | 64 +++++++++++++++++++ .../tests/unit/v1/test_pipeline.py | 3 - .../tests/unit/v1/test_pipeline_source.py | 11 ++++ 4 files changed, 75 insertions(+), 65 deletions(-) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py index 5b7f234f7d0f..fb7bbdbbc81e 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py @@ -275,68 +275,6 @@ def find_nearest( stages.FindNearest(field, vector, distance_measure, options) ) - def literals(self, *documents: Expression | dict) -> "_BasePipeline": - """ - Returns documents from a fixed set of predefined document objects. - - This stage is commonly used for testing other stages in isolation, - though it can also be used as inputs to join conditions. - - Example: - >>> from google.cloud.firestore_v1.pipeline_expressions import Constant - >>> documents = [ - ... {"name": "joe", "age": 10}, - ... {"name": "bob", "age": 30}, - ... {"name": "alice", "age": 40} - ... ] - >>> pipeline = client.pipeline() - ... .literals(documents) - ... .where(field("age").lessThan(35)) - - Output documents: - ```json - [ - {"name": "joe", "age": 10}, - {"name": "bob", "age": 30} - ] - ``` - - Behavior: - The `literals(...)` stage can only be used as the first stage in a pipeline (or - sub-pipeline). The order of documents returned from the `literals` matches the - order in which they are defined. - - While literal values are the most common, it is also possible to pass in - expressions, which will be evaluated and returned, making it possible to test - out different query / expression behavior without first needing to create some - test data. - - For example, the following shows how to quickly test out the `length(...)` - function on some constant test sets: - - Example: - >>> from google.cloud.firestore_v1.pipeline_expressions import Constant - >>> documents = [ - ... {"x": Constant.of("foo-bar-baz").char_length()}, - ... {"x": Constant.of("bar").char_length()} - ... ] - >>> pipeline = client.pipeline().literals(documents) - - Output documents: - ```json - [ - {"x": 11}, - {"x": 3} - ] - ``` - - Args: - *documents: One or more documents to be returned by this stage. Each can be a `dict` - or an `Expression`. - Returns: - A new Pipeline object with this stage appended to the stage list. - """ - return self._append(stages.Literals(*documents)) def replace_with( self, diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py index 0a7c6e7fbdab..d647f7985c9e 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py @@ -32,6 +32,7 @@ from google.cloud.firestore_v1.base_document import BaseDocumentReference from google.cloud.firestore_v1.base_query import BaseQuery from google.cloud.firestore_v1.client import Client + from google.cloud.firestore_v1.pipeline_expressions import Expression PipelineType = TypeVar("PipelineType", bound=_BasePipeline) @@ -108,3 +109,66 @@ def documents(self, *docs: "BaseDocumentReference") -> PipelineType: a new pipeline instance targeting the specified documents """ return self._create_pipeline(stages.Documents.of(*docs)) + + def literals(self, *documents: "Expression" | dict) -> PipelineType: + """ + Returns documents from a fixed set of predefined document objects. + + This stage is commonly used for testing other stages in isolation, + though it can also be used as inputs to join conditions. + + Example: + >>> from google.cloud.firestore_v1.pipeline_expressions import Constant + >>> documents = [ + ... {"name": "joe", "age": 10}, + ... {"name": "bob", "age": 30}, + ... {"name": "alice", "age": 40} + ... ] + >>> pipeline = client.pipeline() + ... .literals(documents) + ... .where(field("age").lessThan(35)) + + Output documents: + ```json + [ + {"name": "joe", "age": 10}, + {"name": "bob", "age": 30} + ] + ``` + + Behavior: + The `literals(...)` stage can only be used as the first stage in a pipeline (or + sub-pipeline). The order of documents returned from the `literals` matches the + order in which they are defined. + + While literal values are the most common, it is also possible to pass in + expressions, which will be evaluated and returned, making it possible to test + out different query / expression behavior without first needing to create some + test data. + + For example, the following shows how to quickly test out the `length(...)` + function on some constant test sets: + + Example: + >>> from google.cloud.firestore_v1.pipeline_expressions import Constant + >>> documents = [ + ... {"x": Constant.of("foo-bar-baz").char_length()}, + ... {"x": Constant.of("bar").char_length()} + ... ] + >>> pipeline = client.pipeline().literals(documents) + + Output documents: + ```json + [ + {"x": 11}, + {"x": 3} + ] + ``` + + Args: + *documents: One or more documents to be returned by this stage. Each can be a `dict` + or an `Expression`. + Returns: + A new Pipeline object with this stage appended to the stage list. + """ + return self._create_pipeline(stages.Literals(*documents)) diff --git a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline.py b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline.py index 5ec2e9ac1123..5953398709a3 100644 --- a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline.py +++ b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline.py @@ -417,9 +417,6 @@ def test_pipeline_execute_stream_equivalence(): ("aggregate", (Field.of("n").as_("alias"),), stages.Aggregate), ("distinct", ("field_name",), stages.Distinct), ("distinct", (Field.of("n"), "second"), stages.Distinct), - ("literals", (Field.of("a"),), stages.Literals), - ("literals", ({"name": "joe"}, {"name": "bob"}), stages.Literals), - ("literals", (Field.of("a"), {"name": "joe"}), stages.Literals), ], ) def test_pipeline_methods(method, args, result_cls): diff --git a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_source.py b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_source.py index de31d47f70eb..94da65a61690 100644 --- a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_source.py +++ b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_source.py @@ -110,6 +110,17 @@ def test_documents(self): assert first_stage.paths[1] == "/a/2" assert first_stage.paths[2] == "/a/3" + def test_literals(self): + from google.cloud.firestore_v1.pipeline_expressions import Field + + instance = self._make_client().pipeline() + documents = (Field.of("a"), {"name": "joe"}) + ppl = instance.literals(*documents) + assert isinstance(ppl, self._expected_pipeline_type) + assert len(ppl.stages) == 1 + first_stage = ppl.stages[0] + assert isinstance(first_stage, stages.Literals) + class TestPipelineSourceWithAsyncClient(TestPipelineSource): """ From 09f59f6a875e60a9917a382d58e6b084c87741e3 Mon Sep 17 00:00:00 2001 From: Linchin Date: Tue, 17 Mar 2026 23:41:36 +0000 Subject: [PATCH 10/17] correct system test --- .../tests/system/pipeline_e2e/general.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml b/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml index d4aa80ba8d7b..5b9f67662270 100644 --- a/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml +++ b/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml @@ -690,10 +690,8 @@ tests: - Literals: - title: "The Hitchhiker's Guide to the Galaxy" author: "Douglas Adams" - - Constant: - value: - genre: "Science Fiction" - year: 1979 + - genre: "Science Fiction" + year: 1979 assert_results: - title: "The Hitchhiker's Guide to the Galaxy" author: "Douglas Adams" From 11c22ee441faf15eb26cf1f575235c2b659b6061 Mon Sep 17 00:00:00 2001 From: Linchin Date: Wed, 18 Mar 2026 00:19:25 +0000 Subject: [PATCH 11/17] type annotation and add unit tests --- .../cloud/firestore_v1/pipeline_source.py | 2 +- .../cloud/firestore_v1/pipeline_stages.py | 3 +- .../tests/unit/v1/test_pipeline_stages.py | 54 +++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py index d647f7985c9e..3f5f73ddc708 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py @@ -110,7 +110,7 @@ def documents(self, *docs: "BaseDocumentReference") -> PipelineType: """ return self._create_pipeline(stages.Documents.of(*docs)) - def literals(self, *documents: "Expression" | dict) -> PipelineType: + def literals(self, *documents: dict[str, "Expression" | CONSTANT_TYPE]) -> PipelineType: """ Returns documents from a fixed set of predefined document objects. diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py index 30d2fd8e936a..555f98198ebd 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py @@ -29,6 +29,7 @@ AggregateFunction, AliasedExpression, BooleanExpression, + CONSTANT_TYPE, Expression, Field, Ordering, @@ -345,7 +346,7 @@ def _pb_args(self): class Literals(Stage): """Returns documents from a fixed set of predefined document objects.""" - def __init__(self, *documents: Expression | dict): + def __init__(self, *documents: dict[str, "Expression" | CONSTANT_TYPE]): super().__init__("literals") self.documents = documents diff --git a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py index 82fe3e1881b2..fb997395174d 100644 --- a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py +++ b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py @@ -545,6 +545,60 @@ def test_to_pb(self): assert result.args[1].map_value.fields["b"].integer_value == 2 assert len(result.options) == 0 + def test_ctor_extended_types(self): + import datetime + from google.cloud.firestore_v1._helpers import GeoPoint + from google.cloud.firestore_v1.vector import Vector + + doc = { + "a": 1, + "b": "string", + "c": 3.14, + "d": True, + "e": None, + "f": b"bytes", + "g": datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc), + "h": GeoPoint(1.0, 2.0), + "i": Vector([1.0, 2.0]) + } + instance = self._make_one(doc) + assert instance.documents == (doc,) + assert instance.name == "literals" + + def test_to_pb_extended_types(self): + import datetime + from google.cloud.firestore_v1._helpers import GeoPoint + from google.cloud.firestore_v1.vector import Vector + + doc = { + "a": 1, + "b": "string", + "c": 3.14, + "d": True, + "e": None, + "f": b"bytes", + "g": datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc), + "h": GeoPoint(1.0, 2.0), + "i": Vector([1.0, 2.0]) + } + instance = self._make_one(doc) + result = instance._to_pb() + assert result.name == "literals" + assert len(result.args) == 1 + + fields = result.args[0].map_value.fields + assert fields["a"].integer_value == 1 + assert fields["b"].string_value == "string" + assert fields["c"].double_value == 3.14 + assert fields["d"].boolean_value is True + assert fields["e"].null_value == 0 + assert fields["f"].bytes_value == b"bytes" + assert fields["g"].timestamp_value == datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc) + assert fields["h"].geo_point_value.latitude == 1.0 + assert fields["h"].geo_point_value.longitude == 2.0 + assert fields["i"].map_value.fields["value"].array_value.values[0].double_value == 1.0 + assert fields["i"].map_value.fields["value"].array_value.values[1].double_value == 2.0 + class TestOffset: def _make_one(self, *args, **kwargs): From 941101fd3fae184026e74b6b6f64f1c3c2cdb568 Mon Sep 17 00:00:00 2001 From: Linchin Date: Wed, 18 Mar 2026 18:21:55 +0000 Subject: [PATCH 12/17] docstring --- .../google/cloud/firestore_v1/pipeline_source.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py index 3f5f73ddc708..638cd6f36000 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py @@ -125,7 +125,7 @@ def literals(self, *documents: dict[str, "Expression" | CONSTANT_TYPE]) -> Pipel ... {"name": "alice", "age": 40} ... ] >>> pipeline = client.pipeline() - ... .literals(documents) + ... .literals(*documents) ... .where(field("age").lessThan(35)) Output documents: @@ -155,7 +155,7 @@ def literals(self, *documents: dict[str, "Expression" | CONSTANT_TYPE]) -> Pipel ... {"x": Constant.of("foo-bar-baz").char_length()}, ... {"x": Constant.of("bar").char_length()} ... ] - >>> pipeline = client.pipeline().literals(documents) + >>> pipeline = client.pipeline().literals(*documents) Output documents: ```json From ccb159adeae0f3a992be10795c4b06d8388247d4 Mon Sep 17 00:00:00 2001 From: Linchin Date: Wed, 18 Mar 2026 18:38:52 +0000 Subject: [PATCH 13/17] type annotation --- .../google/cloud/firestore_v1/pipeline_source.py | 2 +- .../google/cloud/firestore_v1/pipeline_stages.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py index 638cd6f36000..65913348a38e 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py @@ -110,7 +110,7 @@ def documents(self, *docs: "BaseDocumentReference") -> PipelineType: """ return self._create_pipeline(stages.Documents.of(*docs)) - def literals(self, *documents: dict[str, "Expression" | CONSTANT_TYPE]) -> PipelineType: + def literals(self, *documents: dict[str, Expression | CONSTANT_TYPE]) -> PipelineType: """ Returns documents from a fixed set of predefined document objects. diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py index 555f98198ebd..2704b7be496c 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py @@ -346,7 +346,7 @@ def _pb_args(self): class Literals(Stage): """Returns documents from a fixed set of predefined document objects.""" - def __init__(self, *documents: dict[str, "Expression" | CONSTANT_TYPE]): + def __init__(self, *documents: dict[str, Expression | CONSTANT_TYPE]): super().__init__("literals") self.documents = documents From 6aae5039f470549eb9d67c8c22e44fa063f928cd Mon Sep 17 00:00:00 2001 From: Linchin Date: Wed, 18 Mar 2026 19:02:59 +0000 Subject: [PATCH 14/17] add more system test --- .../tests/system/pipeline_e2e/general.yaml | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml b/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml index 5b9f67662270..1b03beb4a3c1 100644 --- a/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml +++ b/packages/google-cloud-firestore/tests/system/pipeline_e2e/general.yaml @@ -713,4 +713,34 @@ tests: stringValue: "Science Fiction" year: integerValue: '1979' - name: literals \ No newline at end of file + name: literals + - description: literals_with_expression_input + pipeline: + - Literals: + - res: + FunctionExpression.string_concat: + - Constant: "A" + - Constant: "B" + - Select: + - res + assert_results: + - res: "AB" + assert_proto: + pipeline: + stages: + - args: + - mapValue: + fields: + res: + functionValue: + args: + - stringValue: "A" + - stringValue: "B" + name: string_concat + name: literals + - args: + - mapValue: + fields: + res: + fieldReferenceValue: res + name: select \ No newline at end of file From fa30d5ff8eb8e6faeb694e528b5e173a759fb6c4 Mon Sep 17 00:00:00 2001 From: Linchin Date: Wed, 18 Mar 2026 19:03:19 +0000 Subject: [PATCH 15/17] update to accept Expression --- .../cloud/firestore_v1/pipeline_stages.py | 8 ++- .../tests/unit/v1/test_pipeline_stages.py | 58 +++++++++++++------ 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py index 2704b7be496c..fad397790624 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py @@ -356,7 +356,13 @@ def _pb_args(self): if hasattr(doc, "_to_pb"): args.append(doc._to_pb()) else: - args.append(encode_value(doc)) + encoded_doc = {} + for k, v in doc.items(): + if hasattr(v, "_to_pb"): + encoded_doc[k] = v._to_pb() + else: + encoded_doc[k] = encode_value(v) + args.append(Value(map_value={"fields": encoded_doc})) return args diff --git a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py index fb997395174d..a7e335e904d1 100644 --- a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py +++ b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py @@ -528,23 +528,6 @@ def test_ctor(self): assert instance.documents == (val1, val2) assert instance.name == "literals" - def test_repr(self): - val1 = Constant.of({"a": 1}) - instance = self._make_one(val1, {"b": 2}) - repr_str = repr(instance) - assert repr_str == "Literals(documents=(Constant.of({'a': 1}), {'b': 2}))" - - def test_to_pb(self): - val1 = Constant.of({"a": 1}) - val2 = {"b": 2} - instance = self._make_one(val1, val2) - result = instance._to_pb() - assert result.name == "literals" - assert len(result.args) == 2 - assert result.args[0].map_value.fields["a"].integer_value == 1 - assert result.args[1].map_value.fields["b"].integer_value == 2 - assert len(result.options) == 0 - def test_ctor_extended_types(self): import datetime from google.cloud.firestore_v1._helpers import GeoPoint @@ -565,6 +548,32 @@ def test_ctor_extended_types(self): assert instance.documents == (doc,) assert instance.name == "literals" + def test_ctor_w_expressions(self): + from google.cloud.firestore_v1.pipeline_expressions import FunctionExpression + + expr = FunctionExpression("string_concat", [Constant("A"), Constant("B")]) + doc = {"res": expr} + instance = self._make_one(doc) + assert instance.documents == (doc,) + assert instance.name == "literals" + + def test_repr(self): + val1 = Constant.of({"a": 1}) + instance = self._make_one(val1, {"b": 2}) + repr_str = repr(instance) + assert repr_str == "Literals(documents=(Constant.of({'a': 1}), {'b': 2}))" + + def test_to_pb(self): + val1 = Constant.of({"a": 1}) + val2 = {"b": 2} + instance = self._make_one(val1, val2) + result = instance._to_pb() + assert result.name == "literals" + assert len(result.args) == 2 + assert result.args[0].map_value.fields["a"].integer_value == 1 + assert result.args[1].map_value.fields["b"].integer_value == 2 + assert len(result.options) == 0 + def test_to_pb_extended_types(self): import datetime from google.cloud.firestore_v1._helpers import GeoPoint @@ -599,6 +608,21 @@ def test_to_pb_extended_types(self): assert fields["i"].map_value.fields["value"].array_value.values[0].double_value == 1.0 assert fields["i"].map_value.fields["value"].array_value.values[1].double_value == 2.0 + def test_to_pb_w_expression(self): + from google.cloud.firestore_v1.pipeline_expressions import FunctionExpression + + expr = FunctionExpression("string_concat", [Constant("A"), Constant("B")]) + doc = {"res": expr} + instance = self._make_one(doc) + result = instance._to_pb() + assert result.name == "literals" + assert len(result.args) == 1 + + fields = result.args[0].map_value.fields + assert fields["res"].function_value.name == "string_concat" + assert fields["res"].function_value.args[0].string_value == "A" + assert fields["res"].function_value.args[1].string_value == "B" + class TestOffset: def _make_one(self, *args, **kwargs): From db98e449161b962806f516486c4fa01b2ccfccbc Mon Sep 17 00:00:00 2001 From: Linchin Date: Wed, 18 Mar 2026 19:06:59 +0000 Subject: [PATCH 16/17] lint --- .../cloud/firestore_v1/base_pipeline.py | 1 - .../cloud/firestore_v1/pipeline_source.py | 6 ++-- .../tests/unit/v1/test_pipeline_stages.py | 30 ++++++++++++------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py index fb7bbdbbc81e..fac7f8bc4bce 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_pipeline.py @@ -275,7 +275,6 @@ def find_nearest( stages.FindNearest(field, vector, distance_measure, options) ) - def replace_with( self, field: Selectable, diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py index 65913348a38e..f65283b55e6d 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py @@ -32,7 +32,7 @@ from google.cloud.firestore_v1.base_document import BaseDocumentReference from google.cloud.firestore_v1.base_query import BaseQuery from google.cloud.firestore_v1.client import Client - from google.cloud.firestore_v1.pipeline_expressions import Expression + from google.cloud.firestore_v1.pipeline_expressions import CONSTANT_TYPE, Expression PipelineType = TypeVar("PipelineType", bound=_BasePipeline) @@ -110,7 +110,9 @@ def documents(self, *docs: "BaseDocumentReference") -> PipelineType: """ return self._create_pipeline(stages.Documents.of(*docs)) - def literals(self, *documents: dict[str, Expression | CONSTANT_TYPE]) -> PipelineType: + def literals( + self, *documents: dict[str, Expression | CONSTANT_TYPE] + ) -> PipelineType: """ Returns documents from a fixed set of predefined document objects. diff --git a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py index a7e335e904d1..0a3090865ee0 100644 --- a/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py +++ b/packages/google-cloud-firestore/tests/unit/v1/test_pipeline_stages.py @@ -532,7 +532,7 @@ def test_ctor_extended_types(self): import datetime from google.cloud.firestore_v1._helpers import GeoPoint from google.cloud.firestore_v1.vector import Vector - + doc = { "a": 1, "b": "string", @@ -542,7 +542,7 @@ def test_ctor_extended_types(self): "f": b"bytes", "g": datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc), "h": GeoPoint(1.0, 2.0), - "i": Vector([1.0, 2.0]) + "i": Vector([1.0, 2.0]), } instance = self._make_one(doc) assert instance.documents == (doc,) @@ -550,7 +550,7 @@ def test_ctor_extended_types(self): def test_ctor_w_expressions(self): from google.cloud.firestore_v1.pipeline_expressions import FunctionExpression - + expr = FunctionExpression("string_concat", [Constant("A"), Constant("B")]) doc = {"res": expr} instance = self._make_one(doc) @@ -578,7 +578,7 @@ def test_to_pb_extended_types(self): import datetime from google.cloud.firestore_v1._helpers import GeoPoint from google.cloud.firestore_v1.vector import Vector - + doc = { "a": 1, "b": "string", @@ -588,13 +588,13 @@ def test_to_pb_extended_types(self): "f": b"bytes", "g": datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc), "h": GeoPoint(1.0, 2.0), - "i": Vector([1.0, 2.0]) + "i": Vector([1.0, 2.0]), } instance = self._make_one(doc) result = instance._to_pb() assert result.name == "literals" assert len(result.args) == 1 - + fields = result.args[0].map_value.fields assert fields["a"].integer_value == 1 assert fields["b"].string_value == "string" @@ -602,22 +602,30 @@ def test_to_pb_extended_types(self): assert fields["d"].boolean_value is True assert fields["e"].null_value == 0 assert fields["f"].bytes_value == b"bytes" - assert fields["g"].timestamp_value == datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc) + assert fields["g"].timestamp_value == datetime.datetime( + 2025, 1, 1, tzinfo=datetime.timezone.utc + ) assert fields["h"].geo_point_value.latitude == 1.0 assert fields["h"].geo_point_value.longitude == 2.0 - assert fields["i"].map_value.fields["value"].array_value.values[0].double_value == 1.0 - assert fields["i"].map_value.fields["value"].array_value.values[1].double_value == 2.0 + assert ( + fields["i"].map_value.fields["value"].array_value.values[0].double_value + == 1.0 + ) + assert ( + fields["i"].map_value.fields["value"].array_value.values[1].double_value + == 2.0 + ) def test_to_pb_w_expression(self): from google.cloud.firestore_v1.pipeline_expressions import FunctionExpression - + expr = FunctionExpression("string_concat", [Constant("A"), Constant("B")]) doc = {"res": expr} instance = self._make_one(doc) result = instance._to_pb() assert result.name == "literals" assert len(result.args) == 1 - + fields = result.args[0].map_value.fields assert fields["res"].function_value.name == "string_concat" assert fields["res"].function_value.args[0].string_value == "A" From 4a85ee6bfe70018b4a609268817ef69175b14acb Mon Sep 17 00:00:00 2001 From: Linchin Date: Wed, 18 Mar 2026 20:55:48 +0000 Subject: [PATCH 17/17] lint --- .../google/cloud/firestore_v1/pipeline_source.py | 4 ++-- .../google/cloud/firestore_v1/pipeline_stages.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py index f65283b55e6d..215f78559b6f 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_source.py @@ -19,7 +19,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Generic, TypeVar +from typing import Mapping, Generic, TypeVar, TYPE_CHECKING from google.cloud.firestore_v1 import pipeline_stages as stages from google.cloud.firestore_v1._helpers import DOCUMENT_PATH_DELIMITER @@ -111,7 +111,7 @@ def documents(self, *docs: "BaseDocumentReference") -> PipelineType: return self._create_pipeline(stages.Documents.of(*docs)) def literals( - self, *documents: dict[str, Expression | CONSTANT_TYPE] + self, *documents: Mapping[str, Expression | CONSTANT_TYPE] ) -> PipelineType: """ Returns documents from a fixed set of predefined document objects. diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py index fad397790624..dff76277cb84 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/pipeline_stages.py @@ -21,7 +21,7 @@ from abc import ABC, abstractmethod from enum import Enum -from typing import TYPE_CHECKING, Optional, Sequence +from typing import TYPE_CHECKING, Mapping, Optional, Sequence from google.cloud.firestore_v1._helpers import encode_value from google.cloud.firestore_v1.base_vector_query import DistanceMeasure @@ -346,9 +346,9 @@ def _pb_args(self): class Literals(Stage): """Returns documents from a fixed set of predefined document objects.""" - def __init__(self, *documents: dict[str, Expression | CONSTANT_TYPE]): + def __init__(self, *documents: Mapping[str, Expression | CONSTANT_TYPE]): super().__init__("literals") - self.documents = documents + self.documents = documents # type: ignore def _pb_args(self): args = []