2929from pymongo import (common ,
3030 helpers ,
3131 message )
32- from pymongo .aggregation import _CollectionAggregationCommand
32+ from pymongo .aggregation import (_CollectionAggregationCommand ,
33+ _CollectionRawAggregationCommand )
3334from pymongo .bulk import BulkOperationBuilder , _Bulk
3435from pymongo .command_cursor import CommandCursor , RawBatchCommandCursor
3536from pymongo .common import ORDERED_TYPES
@@ -2258,11 +2259,8 @@ def options(self, session=None):
22582259
22592260 return options
22602261
2261- def _aggregate (self , pipeline , cursor_class , first_batch_size , session ,
2262+ def _aggregate (self , aggregation_command , pipeline , cursor_class , session ,
22622263 explicit_session , ** kwargs ):
2263- # Check if we use the $out stage
2264- dollar_out = pipeline and '$out' in pipeline [- 1 ]
2265-
22662264 # Remove things that are not command options.
22672265 use_cursor = True
22682266 if "useCursor" in kwargs :
@@ -2271,25 +2269,14 @@ def _aggregate(self, pipeline, cursor_class, first_batch_size, session,
22712269 "and will be removed in PyMongo 4.0" ,
22722270 DeprecationWarning , stacklevel = 2 )
22732271 use_cursor = common .validate_boolean (
2274- "useCursor" , kwargs .pop ("useCursor" ))
2275-
2276- # If the server does not support the "cursor" option we
2277- # ignore useCursor and batchSize.
2278- if use_cursor :
2279- if "cursor" not in kwargs :
2280- kwargs ["cursor" ] = {}
2281- # Ignore batchSize when the $out pipeline stage is used.
2282- # batchSize is meaningless in that case since the server
2283- # doesn't return results. This also avoids SERVER-23923.
2284- if first_batch_size is not None and not dollar_out :
2285- kwargs ["cursor" ]["batchSize" ] = first_batch_size
2286-
2287- cmd = _CollectionAggregationCommand (
2272+ "useCursor" , kwargs .pop ("useCursor" , True ))
2273+
2274+ cmd = aggregation_command (
22882275 self , cursor_class , pipeline , kwargs , explicit_session ,
2289- user_fields = {'cursor' : {'firstBatch' : 1 }})
2276+ user_fields = {'cursor' : {'firstBatch' : 1 }}, use_cursor = use_cursor )
22902277 return self .__database .client ._retryable_read (
22912278 cmd .get_cursor , self ._read_preference_for (session ), session ,
2292- retryable = not dollar_out )
2279+ retryable = not cmd . _performs_write )
22932280
22942281 def aggregate (self , pipeline , session = None , ** kwargs ):
22952282 """Perform an aggregation using the aggregation framework on this
@@ -2314,11 +2301,11 @@ def aggregate(self, pipeline, session=None, **kwargs):
23142301 - `useCursor` (bool): Deprecated. Will be removed in PyMongo 4.0.
23152302
23162303 The :meth:`aggregate` method obeys the :attr:`read_preference` of this
2317- :class:`Collection`. Please note that using the ``$out`` pipeline stage
2318- requires a read preference of
2304+ :class:`Collection`. Please note that using the ``$out`` and ``$merge``
2305+ pipeline stages requires a read preference of
23192306 :attr:`~pymongo.read_preferences.ReadPreference.PRIMARY` (the default).
2320- The server will raise an error if the ``$out`` pipeline stage is used
2321- with any other read preference.
2307+ The server will raise an error if the ``$out`` or ``$merge`` pipeline
2308+ stages are used with any other read preference.
23222309
23232310 .. note:: This method does not support the 'explain' option. Please
23242311 use :meth:`~pymongo.database.Database.command` instead. An
@@ -2338,6 +2325,8 @@ def aggregate(self, pipeline, session=None, **kwargs):
23382325 A :class:`~pymongo.command_cursor.CommandCursor` over the result
23392326 set.
23402327
2328+ .. versionchanged:: 3.9
2329+ Added support for the ``$merge`` pipeline stage.
23412330 .. versionchanged:: 3.9
23422331 Apply this collection's read concern to pipelines containing the
23432332 `$out` stage when connected to MongoDB >= 4.2.
@@ -2364,9 +2353,9 @@ def aggregate(self, pipeline, session=None, **kwargs):
23642353 https://docs.mongodb.com/manual/reference/command/aggregate
23652354 """
23662355 with self .__database .client ._tmp_session (session , close = False ) as s :
2367- return self ._aggregate (pipeline ,
2356+ return self ._aggregate (_CollectionAggregationCommand ,
2357+ pipeline ,
23682358 CommandCursor ,
2369- kwargs .get ('batchSize' ),
23702359 session = s ,
23712360 explicit_session = session is not None ,
23722361 ** kwargs )
@@ -2397,8 +2386,13 @@ def aggregate_raw_batches(self, pipeline, **kwargs):
23972386 if "session" in kwargs :
23982387 raise ConfigurationError (
23992388 "aggregate_raw_batches does not support sessions" )
2400- return self ._aggregate (pipeline , RawBatchCommandCursor , 0 ,
2401- None , False , ** kwargs )
2389+
2390+ return self ._aggregate (_CollectionRawAggregationCommand ,
2391+ pipeline ,
2392+ RawBatchCommandCursor ,
2393+ session = None ,
2394+ explicit_session = False ,
2395+ ** kwargs )
24022396
24032397 def watch (self , pipeline = None , full_document = 'default' , resume_after = None ,
24042398 max_await_time_ms = None , batch_size = None , collation = None ,
0 commit comments