Skip to content

Commit f65c7f1

Browse files
committed
Remove $facet when an ArrayField is ued as rhs.
1 parent 8da8ef7 commit f65c7f1

File tree

5 files changed

+51
-85
lines changed

5 files changed

+51
-85
lines changed

django_mongodb_backend/fields/array.py

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -310,37 +310,19 @@ class ArrayOverlap(ArrayRHSMixin, FieldGetDbPrepValueMixin, Lookup):
310310

311311
def get_subquery_wrapping_pipeline(self, compiler, connection, field_name, expr):
312312
return [
313+
{"$project": {"tmp_name": expr.as_mql(compiler, connection, as_expr=True)}},
313314
{
314-
"$facet": {
315-
"group": [
316-
{"$project": {"tmp_name": expr.as_mql(compiler, connection, as_expr=True)}},
317-
{
318-
"$unwind": "$tmp_name",
319-
},
320-
{
321-
"$group": {
322-
"_id": None,
323-
"tmp_name": {"$addToSet": "$tmp_name"},
324-
}
325-
},
326-
]
327-
}
315+
"$unwind": "$tmp_name",
328316
},
329317
{
330-
"$project": {
331-
field_name: {
332-
"$ifNull": [
333-
{
334-
"$getField": {
335-
"input": {"$arrayElemAt": ["$group", 0]},
336-
"field": "tmp_name",
337-
}
338-
},
339-
[],
340-
]
341-
}
318+
"$group": {
319+
"_id": None,
320+
"tmp_name": {"$addToSet": "$tmp_name"},
342321
}
343322
},
323+
{"$unionWith": {"pipeline": [{"$documents": [{"tmp_name": []}]}]}},
324+
{"$limit": 1},
325+
{"$project": {field_name: "$tmp_name"}},
344326
]
345327

346328
def as_mql_expr(self, compiler, connection):

django_mongodb_backend/fields/embedded_model_array.py

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -150,44 +150,27 @@ def get_subquery_wrapping_pipeline(self, compiler, connection, field_name, expr)
150150
# structure of EmbeddedModelArrayField on the RHS behaves similar to
151151
# ArrayField.
152152
return [
153+
{"$project": {"tmp_name": expr.as_mql(compiler, connection, as_expr=True)}},
154+
# To concatenate all the values from the RHS subquery,
155+
# use an $unwind followed by a $group.
153156
{
154-
"$facet": {
155-
"gathered_data": [
156-
{"$project": {"tmp_name": expr.as_mql(compiler, connection, as_expr=True)}},
157-
# To concatenate all the values from the RHS subquery,
158-
# use an $unwind followed by a $group.
159-
{
160-
"$unwind": "$tmp_name",
161-
},
162-
# The $group stage collects values into an array using
163-
# $addToSet. The use of {_id: null} results in a
164-
# single grouped array. However, because arrays from
165-
# multiple documents are aggregated, the result is a
166-
# list of lists.
167-
{
168-
"$group": {
169-
"_id": None,
170-
"tmp_name": {"$addToSet": "$tmp_name"},
171-
}
172-
},
173-
]
174-
}
157+
"$unwind": "$tmp_name",
175158
},
159+
# The $group stage collects values into an array using
160+
# $addToSet. The use of {_id: null} results in a
161+
# single grouped array. However, because arrays from
162+
# multiple documents are aggregated, the result is a
163+
# list of lists.
176164
{
177-
"$project": {
178-
field_name: {
179-
"$ifNull": [
180-
{
181-
"$getField": {
182-
"input": {"$arrayElemAt": ["$gathered_data", 0]},
183-
"field": "tmp_name",
184-
}
185-
},
186-
[],
187-
]
188-
}
165+
"$group": {
166+
"_id": None,
167+
"tmp_name": {"$addToSet": "$tmp_name"},
189168
}
190169
},
170+
# Add a dummy document in case of empty result.
171+
{"$unionWith": {"pipeline": [{"$documents": [{"tmp_name": []}]}]}},
172+
{"$limit": 1},
173+
{"$project": {field_name: "$tmp_name"}},
191174
]
192175

193176

django_mongodb_backend/lookups.py

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -56,34 +56,15 @@ def inner(self, compiler, connection):
5656
def get_subquery_wrapping_pipeline(self, compiler, connection, field_name, expr): # noqa: ARG001
5757
return [
5858
{
59-
"$facet": {
60-
"group": [
61-
{
62-
"$group": {
63-
"_id": None,
64-
"tmp_name": {
65-
"$addToSet": expr.as_mql(compiler, connection, as_expr=True)
66-
},
67-
}
68-
}
69-
]
70-
}
71-
},
72-
{
73-
"$project": {
74-
field_name: {
75-
"$ifNull": [
76-
{
77-
"$getField": {
78-
"input": {"$arrayElemAt": ["$group", 0]},
79-
"field": "tmp_name",
80-
}
81-
},
82-
[],
83-
]
84-
}
59+
"$group": {
60+
"_id": None,
61+
# use a temporal name in order to support field_name="_id"
62+
"tmp_name": {"$addToSet": expr.as_mql(compiler, connection, as_expr=True)},
8563
}
8664
},
65+
{"$unionWith": {"pipeline": [{"$documents": [{"tmp_name": []}]}]}},
66+
{"$limit": 1},
67+
{"$project": {field_name: "$tmp_name"}},
8768
]
8869

8970

tests/model_fields_/test_arrayfield.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,21 @@ def test_overlap_values(self):
634634
self.objs[:3],
635635
)
636636

637+
def test_overlap_empty_values(self):
638+
qs = NullableIntegerArrayModel.objects.filter(order__lt=-30)
639+
self.assertCountEqual(
640+
NullableIntegerArrayModel.objects.filter(
641+
field__overlap=qs.values_list("field"),
642+
),
643+
[],
644+
)
645+
self.assertCountEqual(
646+
NullableIntegerArrayModel.objects.filter(
647+
field__overlap=qs.values("field"),
648+
),
649+
[],
650+
)
651+
637652
def test_index(self):
638653
self.assertSequenceEqual(
639654
NullableIntegerArrayModel.objects.filter(field__0=2), self.objs[1:3]

tests/model_fields_/test_embedded_model_array.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,11 @@ def test_subquery_in_lookup(self):
520520
result = Exhibit.objects.filter(sections__number__in=subquery)
521521
self.assertCountEqual(result, [self.wonders, self.new_discoveries, self.egypt])
522522

523+
def test_subquery_empty_in_lookup(self):
524+
subquery = Audit.objects.filter(section_number=10).values_list("section_number", flat=True)
525+
result = Exhibit.objects.filter(sections__number__in=subquery)
526+
self.assertCountEqual(result, [])
527+
523528
def test_array_as_rhs(self):
524529
result = Exhibit.objects.filter(main_section__number__in=models.F("sections__number"))
525530
self.assertCountEqual(result, [self.new_discoveries])

0 commit comments

Comments
 (0)