@@ -1343,6 +1343,14 @@ def from_json(self, json_data):
13431343 def aggregate (self , pipeline , * suppl_pipeline , ** kwargs ):
13441344 """Perform an aggregate function based on your queryset params
13451345
1346+ If the queryset contains a query or skip/limit/sort or if the target Document class
1347+ uses inheritance, this method will add steps prior to the provided pipeline in an arbitrary order.
1348+ This may affect the performance or outcome of the aggregation, so use it consciously.
1349+
1350+ For complex/critical pipelines, we recommended to use the aggregation framework of Pymongo directly,
1351+ it is available through the collection object (YourDocument._collection.aggregate) and will guarantee
1352+ that you have full control on the pipeline.
1353+
13461354 :param pipeline: list of aggregation commands,
13471355 see: https://www.mongodb.com/docs/manual/core/aggregation-pipeline/
13481356 :param suppl_pipeline: unpacked list of pipeline (added to support deprecation of the old interface)
@@ -1380,7 +1388,18 @@ def aggregate(self, pipeline, *suppl_pipeline, **kwargs):
13801388 if self ._skip is not None :
13811389 initial_pipeline .append ({"$skip" : self ._skip })
13821390
1383- final_pipeline = initial_pipeline + user_pipeline
1391+ # geoNear and collStats must be the first stages in the pipeline if present
1392+ first_step = []
1393+ new_user_pipeline = []
1394+ for step_step in user_pipeline :
1395+ if "$geoNear" in step_step :
1396+ first_step .append (step_step )
1397+ elif "$collStats" in step_step :
1398+ first_step .append (step_step )
1399+ else :
1400+ new_user_pipeline .append (step_step )
1401+
1402+ final_pipeline = first_step + initial_pipeline + new_user_pipeline
13841403
13851404 collection = self ._collection
13861405 if self ._read_preference is not None or self ._read_concern is not None :
0 commit comments