From b480548dcab4cd458544bc7166894b688a2c2e48 Mon Sep 17 00:00:00 2001 From: Emanuel Lupi Date: Thu, 20 Nov 2025 01:28:23 -0300 Subject: [PATCH 1/2] Remove $facet in top level group stages --- django_mongodb_backend/aggregates.py | 1 + django_mongodb_backend/compiler.py | 18 ++++-------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/django_mongodb_backend/aggregates.py b/django_mongodb_backend/aggregates.py index fb41ce4fc..9921e34ab 100644 --- a/django_mongodb_backend/aggregates.py +++ b/django_mongodb_backend/aggregates.py @@ -52,6 +52,7 @@ def count(self, compiler, connection, resolve_inner_expression=False): # If distinct=True or resolve_inner_expression=False, sum the size of the # set. lhs_mql = process_lhs(self, compiler, connection, as_expr=True) + lhs_mql = {"$ifNull": [lhs_mql, []]} # None shouldn't be counted, so subtract 1 if it's present. exits_null = {"$cond": {"if": {"$in": [{"$literal": None}, lhs_mql]}, "then": -1, "else": 0}} return {"$add": [{"$size": lhs_mql}, exits_null]} diff --git a/django_mongodb_backend/compiler.py b/django_mongodb_backend/compiler.py index cb867221e..6feb7b13e 100644 --- a/django_mongodb_backend/compiler.py +++ b/django_mongodb_backend/compiler.py @@ -235,20 +235,10 @@ def _build_aggregation_pipeline(self, ids, group): pipeline = [] if not ids: group["_id"] = None - pipeline.append({"$facet": {"group": [{"$group": group}]}}) - pipeline.append( - { - "$addFields": { - key: { - "$getField": { - "input": {"$arrayElemAt": ["$group", 0]}, - "field": key, - } - } - for key in group - } - } - ) + pipeline.append({"$group": group}) + # It may be a bug, $$NOW has to be called to be reachable in the rest of the pipeline. + pipeline.append({"$set": {"__now": "$$NOW"}}) + pipeline.append({"$unionWith": {"pipeline": [{"$documents": [{}]}]}}) else: group["_id"] = ids pipeline.append({"$group": group}) From 273957fd5953d4e9b4b195fde8b255794519e783 Mon Sep 17 00:00:00 2001 From: Emanuel Lupi Date: Tue, 25 Nov 2025 00:22:27 -0300 Subject: [PATCH 2/2] Wrap query for global aggregation --- django_mongodb_backend/compiler.py | 8 +++----- django_mongodb_backend/query.py | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/django_mongodb_backend/compiler.py b/django_mongodb_backend/compiler.py index 6feb7b13e..1388e9ba7 100644 --- a/django_mongodb_backend/compiler.py +++ b/django_mongodb_backend/compiler.py @@ -38,6 +38,7 @@ def __init__(self, *args, **kwargs): self.subqueries = [] # Atlas search stage. self.search_pipeline = [] + self.wrap_for_global_aggregation = False def _get_group_alias_column(self, expr, annotation_group_idx): """Generate a dummy field for use in the ids fields in $group.""" @@ -234,11 +235,8 @@ def _build_aggregation_pipeline(self, ids, group): """Build the aggregation pipeline for grouping.""" pipeline = [] if not ids: - group["_id"] = None - pipeline.append({"$group": group}) - # It may be a bug, $$NOW has to be called to be reachable in the rest of the pipeline. - pipeline.append({"$set": {"__now": "$$NOW"}}) - pipeline.append({"$unionWith": {"pipeline": [{"$documents": [{}]}]}}) + pipeline.append({"$group": {"_id": None, **group}}) + self.wrap_for_global_aggregation = True else: group["_id"] = ids pipeline.append({"$group": group}) diff --git a/django_mongodb_backend/query.py b/django_mongodb_backend/query.py index 5b4f0ec51..85cf0c774 100644 --- a/django_mongodb_backend/query.py +++ b/django_mongodb_backend/query.py @@ -56,6 +56,7 @@ def __init__(self, compiler): # $lookup stage that encapsulates the pipeline for performing a nested # subquery. self.subquery_lookup = None + self.wrap_for_global_aggregation = compiler.wrap_for_global_aggregation def __repr__(self): return f"" @@ -91,6 +92,22 @@ def get_pipeline(self): pipeline.append({"$match": self.match_mql}) if self.aggregation_pipeline: pipeline.extend(self.aggregation_pipeline) + if self.wrap_for_global_aggregation: + pipeline = [ + {"$collStats": {}}, + { + "$lookup": { + "from": self.compiler.collection_name, + "as": "wrapped", + "pipeline": pipeline, + } + }, + { + "$replaceWith": { + "$cond": [{"$eq": ["$wrapped", []]}, {}, {"$first": "$wrapped"}] + } + }, + ] if self.project_fields: pipeline.append({"$project": self.project_fields}) if self.combinator_pipeline: