Skip to content

Commit 30a09c5

Browse files
WaVEVtimgraham
authored andcommitted
add support for QuerySet.distinct()
1 parent 5a89366 commit 30a09c5

File tree

2 files changed

+21
-39
lines changed

2 files changed

+21
-39
lines changed

django_mongodb/compiler.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,9 @@ def execute_sql(
245245
try:
246246
query = self.build_query(
247247
# Avoid $project (columns=None) if unneeded.
248-
columns if self.query.annotations or not self.query.default_cols else None
248+
columns
249+
if self.query.annotations or not self.query.default_cols or self.query.distinct
250+
else None
249251
)
250252
except EmptyResultSet:
251253
return iter([]) if result_type == MULTI else None
@@ -331,13 +333,7 @@ def cursor_iter(self, cursor, chunk_size, columns):
331333

332334
def check_query(self):
333335
"""Check if the current query is supported by the database."""
334-
if self.query.distinct or getattr(
335-
# In the case of Query.distinct().count(), the distinct attribute
336-
# will be set on the inner_query.
337-
getattr(self.query, "inner_query", None),
338-
"distinct",
339-
None,
340-
):
336+
if self.query.distinct:
341337
# This is a heuristic to detect QuerySet.datetimes() and dates().
342338
# "datetimefield" and "datefield" are the names of the annotations
343339
# the methods use. A user could annotate with the same names which
@@ -346,7 +342,6 @@ def check_query(self):
346342
raise NotSupportedError("QuerySet.datetimes() is not supported on MongoDB.")
347343
if "datefield" in self.query.annotations:
348344
raise NotSupportedError("QuerySet.dates() is not supported on MongoDB.")
349-
raise NotSupportedError("QuerySet.distinct() is not supported on MongoDB.")
350345
if self.query.extra:
351346
if any(key.startswith("_prefetch_related_") for key in self.query.extra):
352347
raise NotSupportedError("QuerySet.prefetch_related() is not supported on MongoDB.")
@@ -365,7 +360,19 @@ def build_query(self, columns=None):
365360
)
366361
query.combinator_pipeline = self.get_combinator_queries()
367362
else:
368-
query.project_fields = self.get_project_fields(columns, ordering_fields)
363+
if self.query.distinct:
364+
# If query is distinct, build a $group stage for distinct
365+
# fields, then set project fields based on the grouped _id.
366+
distinct_fields = self.get_project_fields(
367+
columns, ordering_fields, force_expression=True
368+
)
369+
if not query.aggregation_pipeline:
370+
query.aggregation_pipeline = []
371+
query.aggregation_pipeline.append({"$group": {"_id": distinct_fields}})
372+
query.project_fields = {key: f"$_id.{key}" for key in distinct_fields}
373+
else:
374+
# Otherwise, project fields without grouping.
375+
query.project_fields = self.get_project_fields(columns, ordering_fields)
369376
# If columns is None, then get_project_fields() won't add
370377
# ordering_fields to $project. Use $addFields (extra_fields) instead.
371378
if columns is None:
@@ -750,7 +757,7 @@ def build_query(self, columns=None):
750757
# Avoid $project (columns=None) if unneeded.
751758
columns = (
752759
compiler.get_columns()
753-
if compiler.query.annotations or not compiler.query.default_cols
760+
if self.query.annotations or not self.query.default_cols or self.query.distinct
754761
else None
755762
)
756763
subquery = compiler.build_query(columns)

django_mongodb/features.py

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -231,39 +231,13 @@ def django_test_expected_failures(self):
231231
"datetimes.tests.DateTimesTests.test_related_model_traverse",
232232
"model_inheritance_regress.tests.ModelInheritanceTest.test_issue_7105",
233233
"queries.tests.Queries1Tests.test_ticket7155",
234+
"queries.tests.Queries1Tests.test_ticket7791",
235+
"queries.tests.Queries1Tests.test_tickets_6180_6203",
234236
"queries.tests.Queries1Tests.test_tickets_7087_12242",
235237
"timezones.tests.LegacyDatabaseTests.test_query_datetimes",
236238
"timezones.tests.NewDatabaseTests.test_query_datetimes",
237239
"timezones.tests.NewDatabaseTests.test_query_datetimes_in_other_timezone",
238240
},
239-
"QuerySet.distinct() is not supported.": {
240-
"aggregation.tests.AggregateTestCase.test_sum_distinct_aggregate",
241-
"aggregation_regress.tests.AggregationTests.test_annotate_distinct_aggregate",
242-
"aggregation_regress.tests.AggregationTests.test_conditional_aggregate_on_complex_condition",
243-
"aggregation_regress.tests.AggregationTests.test_distinct_conditional_aggregate",
244-
"lookup.tests.LookupTests.test_lookup_collision_distinct",
245-
"many_to_many.tests.ManyToManyTests.test_reverse_selects",
246-
"many_to_many.tests.ManyToManyTests.test_selects",
247-
"many_to_one.tests.ManyToOneTests.test_reverse_selects",
248-
"ordering.tests.OrderingTests.test_orders_nulls_first_on_filtered_subquery",
249-
"queries.tests.ExcludeTest17600.test_exclude_plain_distinct",
250-
"queries.tests.ExcludeTest17600.test_exclude_with_q_is_equal_to_plain_exclude",
251-
"queries.tests.ExcludeTest17600.test_exclude_with_q_is_equal_to_plain_exclude_variation",
252-
"queries.tests.ExcludeTest17600.test_exclude_with_q_object_distinct",
253-
"queries.tests.ExcludeTests.test_exclude_m2m_through",
254-
"queries.tests.ExistsSql.test_distinct_exists",
255-
"queries.tests.ExistsSql.test_sliced_distinct_exists",
256-
"queries.tests.ExistsSql.test_ticket_18414",
257-
"queries.tests.Queries1Tests.test_ticket4464",
258-
"queries.tests.Queries1Tests.test_ticket7096",
259-
"queries.tests.Queries1Tests.test_ticket7791",
260-
"queries.tests.Queries1Tests.test_tickets_1878_2939",
261-
"queries.tests.Queries1Tests.test_tickets_5321_7070",
262-
"queries.tests.Queries1Tests.test_tickets_5324_6704",
263-
"queries.tests.Queries1Tests.test_tickets_6180_6203",
264-
"queries.tests.Queries6Tests.test_distinct_ordered_sliced_subquery_aggregation",
265-
"update.tests.AdvancedTests.test_update_all",
266-
},
267241
"QuerySet.extra() is not supported.": {
268242
"aggregation.tests.AggregateTestCase.test_exists_extra_where_with_aggregate",
269243
"annotations.tests.NonAggregateAnnotationTestCase.test_column_field_ordering",
@@ -281,6 +255,7 @@ def django_test_expected_failures(self):
281255
"queries.test_qs_combinators.QuerySetSetOperationTests.test_union_multiple_models_with_values_list_and_order_by_extra_select",
282256
"queries.test_qs_combinators.QuerySetSetOperationTests.test_union_with_extra_and_values_list",
283257
"queries.tests.EscapingTests.test_ticket_7302",
258+
"queries.tests.Queries1Tests.test_tickets_1878_2939",
284259
"queries.tests.Queries5Tests.test_extra_select_literal_percent_s",
285260
"queries.tests.Queries5Tests.test_ticket7256",
286261
"queries.tests.ValuesQuerysetTests.test_extra_multiple_select_params_values_order_by",

0 commit comments

Comments
 (0)