This is an automated email from the ASF dual-hosted git repository.

shahar1 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new c42540fca87 Fix MongoToS3Operator aggregate-pipeline detection before 
rendering (#70330)
c42540fca87 is described below

commit c42540fca87dbc4b366ee8b75487c82320b3a346
Author: Parman Mohammadalizadeh <[email protected]>
AuthorDate: Fri Jul 24 17:51:22 2026 +0330

    Fix MongoToS3Operator aggregate-pipeline detection before rendering (#70330)
    
    mongo_query is a template field, rendered after __init__ runs. Caching
    is_pipeline = isinstance(self.mongo_query, list) in the constructor inspects
    the un-rendered value, so a query that resolves to a list only after
    templating would be sent through find() instead of aggregate(). Decide the
    aggregate-vs-find path in execute() from the rendered value instead.
---
 .../providers/amazon/aws/transfers/mongo_to_s3.py  |  8 ++++---
 .../unit/amazon/aws/transfers/test_mongo_to_s3.py  | 26 +++++++++++++++++++++-
 .../ci/prek/validate_operators_init_exemptions.txt |  1 -
 3 files changed, 30 insertions(+), 5 deletions(-)

diff --git 
a/providers/amazon/src/airflow/providers/amazon/aws/transfers/mongo_to_s3.py 
b/providers/amazon/src/airflow/providers/amazon/aws/transfers/mongo_to_s3.py
index e53668ad3ac..d8e2929bbea 100644
--- a/providers/amazon/src/airflow/providers/amazon/aws/transfers/mongo_to_s3.py
+++ b/providers/amazon/src/airflow/providers/amazon/aws/transfers/mongo_to_s3.py
@@ -84,9 +84,7 @@ class MongoToS3Operator(BaseOperator):
         self.mongo_db = mongo_db
         self.mongo_collection = mongo_collection
 
-        # Grab query and determine if we need to run an aggregate pipeline
         self.mongo_query = mongo_query
-        self.is_pipeline = isinstance(self.mongo_query, list)
         self.mongo_projection = mongo_projection
 
         self.s3_bucket = s3_bucket
@@ -99,8 +97,12 @@ class MongoToS3Operator(BaseOperator):
         """Is written to depend on transform method."""
         s3_conn = S3Hook(self.aws_conn_id)
 
+        # mongo_query is a template field; decide aggregate-vs-find from the 
rendered value
+        # here rather than in __init__, where it would inspect the un-rendered 
value.
+        is_pipeline = isinstance(self.mongo_query, list)
+
         # Grab collection and execute query according to whether or not it is 
a pipeline
-        if self.is_pipeline:
+        if is_pipeline:
             results: CommandCursor[Any] | Cursor = 
MongoHook(self.mongo_conn_id).aggregate(
                 mongo_collection=self.mongo_collection,
                 aggregate_query=cast("list", self.mongo_query),
diff --git 
a/providers/amazon/tests/unit/amazon/aws/transfers/test_mongo_to_s3.py 
b/providers/amazon/tests/unit/amazon/aws/transfers/test_mongo_to_s3.py
index b2138d88769..61876def7a6 100644
--- a/providers/amazon/tests/unit/amazon/aws/transfers/test_mongo_to_s3.py
+++ b/providers/amazon/tests/unit/amazon/aws/transfers/test_mongo_to_s3.py
@@ -111,7 +111,7 @@ class TestMongoToS3Operator:
         ti.dag_run = dag_run
         render_template_fields(ti, self.mock_operator)
         expected_rendered_template = {"$lt": "2017-01-01T00:00:00+00:00Z"}
-        assert expected_rendered_template == getattr(self.mock_operator, 
"mongo_query")
+        assert expected_rendered_template == self.mock_operator.mongo_query
 
     @mock.patch("airflow.providers.amazon.aws.transfers.mongo_to_s3.MongoHook")
     @mock.patch("airflow.providers.amazon.aws.transfers.mongo_to_s3.S3Hook")
@@ -140,6 +140,30 @@ class TestMongoToS3Operator:
             string_data=s3_doc_str, key=S3_KEY, bucket_name=S3_BUCKET, 
replace=False, compression=COMPRESSION
         )
 
+    @mock.patch("airflow.providers.amazon.aws.transfers.mongo_to_s3.MongoHook")
+    @mock.patch("airflow.providers.amazon.aws.transfers.mongo_to_s3.S3Hook")
+    def test_execute_runs_aggregate_when_query_renders_to_list(self, 
mock_s3_hook, mock_mongo_hook):
+        """
+        mongo_query is a template field, so whether to run an aggregate 
pipeline is decided from
+        the rendered value in execute(), not from the un-rendered value in 
__init__. A query that
+        resolves to a list after templating must take the aggregate path.
+        """
+        operator = self.mock_operator
+        # Simulate templating resolving mongo_query to a list (an aggregate 
pipeline) after __init__.
+        operator.mongo_query = [{"$match": {"foo": "bar"}}]
+        mock_mongo_hook.return_value.aggregate.return_value = 
iter(MOCK_MONGO_RETURN)
+        mock_s3_hook.return_value.load_string.return_value = True
+
+        operator.execute(None)
+
+        mock_mongo_hook.return_value.aggregate.assert_called_once_with(
+            mongo_collection=MONGO_COLLECTION,
+            aggregate_query=[{"$match": {"foo": "bar"}}],
+            mongo_db=None,
+            allowDiskUse=False,
+        )
+        mock_mongo_hook.return_value.find.assert_not_called()
+
     @mock.patch("airflow.providers.amazon.aws.transfers.mongo_to_s3.MongoHook")
     @mock.patch("airflow.providers.amazon.aws.transfers.mongo_to_s3.S3Hook")
     def test_execute_compress(self, mock_s3_hook, mock_mongo_hook):
diff --git a/scripts/ci/prek/validate_operators_init_exemptions.txt 
b/scripts/ci/prek/validate_operators_init_exemptions.txt
index a4846a7da2e..a1c334b20c2 100644
--- a/scripts/ci/prek/validate_operators_init_exemptions.txt
+++ b/scripts/ci/prek/validate_operators_init_exemptions.txt
@@ -19,7 +19,6 @@ 
providers/amazon/src/airflow/providers/amazon/aws/operators/sagemaker.py::SageMa
 
providers/amazon/src/airflow/providers/amazon/aws/operators/step_function.py::StepFunctionStartExecutionOperator
 
providers/amazon/src/airflow/providers/amazon/aws/transfers/base.py::AwsToAwsBaseOperator
 
providers/amazon/src/airflow/providers/amazon/aws/transfers/gcs_to_s3.py::GCSToS3Operator
-providers/amazon/src/airflow/providers/amazon/aws/transfers/mongo_to_s3.py::MongoToS3Operator
 
providers/amazon/src/airflow/providers/amazon/aws/transfers/s3_to_redshift.py::S3ToRedshiftOperator
 
providers/anthropic/src/airflow/providers/anthropic/operators/agent.py::AnthropicAgentSessionOperator
 
providers/apache/hive/src/airflow/providers/apache/hive/sensors/hive_partition.py::HivePartitionSensor

Reply via email to