Skip to content

Commit 4b4adab

Browse files
WaVEVtimgraham
authored andcommitted
INTPYTHON-833 Remove $facet when an ArrayField is used as rhs
1 parent e37a2da commit 4b4adab

File tree

4 files changed

+34
-15
lines changed

4 files changed

+34
-15
lines changed

django_mongodb_backend/aggregates.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Variance,
88
)
99
from django.db.models.expressions import Case, Value, When
10+
from django.db.models.functions.comparison import Coalesce
1011
from django.db.models.lookups import IsNull
1112
from django.db.models.sql.where import WhereNode
1213

@@ -69,6 +70,8 @@ def count(self, compiler, connection, resolve_inner_expression=False):
6970
if resolve_inner_expression:
7071
return lhs_mql
7172
return {"$sum": lhs_mql}
73+
# Normalize empty documents (introduced upstream) to an empty set fallback.
74+
agg_expression = Coalesce(agg_expression, [])
7275
# If distinct=True or resolve_inner_expression=False, sum the size of the
7376
# set.
7477
return {"$size": agg_expression.as_mql(compiler, connection, as_expr=True)}

django_mongodb_backend/compiler.py

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ def __init__(self, *args, **kwargs):
3838
self.subqueries = []
3939
# Atlas search stage.
4040
self.search_pipeline = []
41+
# Does the aggregation have no GROUP BY fields and needs wrapping?
42+
self.wrap_for_global_aggregation = False
43+
# The MQL equivalent to a SQL HAVING clause.
44+
self.having_match_mql = None
4145

4246
def _get_group_alias_column(self, expr, annotation_group_idx):
4347
"""Generate a dummy field for use in the ids fields in $group."""
@@ -234,21 +238,10 @@ def _build_aggregation_pipeline(self, ids, group):
234238
"""Build the aggregation pipeline for grouping."""
235239
pipeline = []
236240
if not ids:
237-
group["_id"] = None
238-
pipeline.append({"$facet": {"group": [{"$group": group}]}})
239-
pipeline.append(
240-
{
241-
"$addFields": {
242-
key: {
243-
"$getField": {
244-
"input": {"$arrayElemAt": ["$group", 0]},
245-
"field": key,
246-
}
247-
}
248-
for key in group
249-
}
250-
}
251-
)
241+
pipeline.append({"$group": {"_id": None, **group}})
242+
# Apply a global aggregation if there are no ids and no having
243+
# clause.
244+
self.wrap_for_global_aggregation = not bool(self.having)
252245
else:
253246
group["_id"] = ids
254247
pipeline.append({"$group": group})

django_mongodb_backend/query.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ def __init__(self, compiler):
5656
# $lookup stage that encapsulates the pipeline for performing a nested
5757
# subquery.
5858
self.subquery_lookup = None
59+
self.wrap_for_global_aggregation = compiler.wrap_for_global_aggregation
5960

6061
def __repr__(self):
6162
return f"<MongoQuery: {self.match_mql!r} ORDER {self.ordering!r}>"
@@ -91,6 +92,23 @@ def get_pipeline(self):
9192
pipeline.append({"$match": self.match_mql})
9293
if self.aggregation_pipeline:
9394
pipeline.extend(self.aggregation_pipeline)
95+
if self.wrap_for_global_aggregation:
96+
pipeline.extend(
97+
[
98+
# Workaround for https://jira.mongodb.org/browse/SERVER-114196:
99+
# $$NOW becomes unavailable after $unionWith, so it must be
100+
# stored beforehand to ensure it remains accessible later
101+
# in the pipeline.
102+
{"$addFields": {"__now": "$$NOW"}},
103+
# Add an empty extra document to handle default values on
104+
# empty results.
105+
{"$unionWith": {"pipeline": [{"$documents": [{}]}]}},
106+
# Limiting to one document ensures the original result
107+
# takes precedence when present, otherwise the injected
108+
# empty document is used.
109+
{"$limit": 1},
110+
]
111+
)
94112
if self.project_fields:
95113
pipeline.append({"$project": self.project_fields})
96114
if self.combinator_pipeline:

docs/releases/6.0.x.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ Bug fixes
1717

1818
- ...
1919

20+
Performance improvements
21+
------------------------
22+
23+
- Removed usage of ``$facet`` from aggregate queries.
24+
2025
6.0.0
2126
=====
2227

0 commit comments

Comments
 (0)