Skip to content

Commit 6cf9766

Browse files
committed
add contained_by lookup
1 parent 039af42 commit 6cf9766

File tree

3 files changed

+44
-0
lines changed

3 files changed

+44
-0
lines changed

django_mongodb/fields/array.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,16 @@ def as_mql(self, compiler, connection):
248248
return {"$and": [{"$ne": [lhs_mql, None]}, {"$setIsSubset": [value, lhs_mql]}]}
249249

250250

251+
@ArrayField.register_lookup
252+
class ArrayContainedBy(ArrayRHSMixin, FieldGetDbPrepValueMixin, Lookup):
253+
lookup_name = "contained_by"
254+
255+
def as_mql(self, compiler, connection):
256+
lhs_mql = process_lhs(self, compiler, connection)
257+
value = process_rhs(self, compiler, connection)
258+
return {"$and": [{"$ne": [lhs_mql, None]}, {"$setIsSubset": [lhs_mql, value]}]}
259+
260+
251261
@ArrayField.register_lookup
252262
class ArrayExact(ArrayRHSMixin, Exact):
253263
pass

docs/source/fields.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,25 @@ data. It uses the ``$setIntersection`` operator. For example:
106106
>>> Post.objects.filter(tags__contains=["django", "thoughts"])
107107
<QuerySet [<Post: First post>]>
108108
109+
``contained_by``
110+
~~~~~~~~~~~~~~~~
111+
112+
This is the inverse of the :lookup:`contains <arrayfield.contains>` lookup -
113+
the objects returned will be those where the data is a subset of the values
114+
passed. It uses the ``$setIntersection`` operator. For example:
115+
116+
.. code-block:: pycon
117+
118+
>>> Post.objects.create(name="First post", tags=["thoughts", "django"])
119+
>>> Post.objects.create(name="Second post", tags=["thoughts"])
120+
>>> Post.objects.create(name="Third post", tags=["tutorial", "django"])
121+
122+
>>> Post.objects.filter(tags__contained_by=["thoughts", "django"])
123+
<QuerySet [<Post: First post>, <Post: Second post>]>
124+
125+
>>> Post.objects.filter(tags__contained_by=["thoughts", "django", "tutorial"])
126+
<QuerySet [<Post: First post>, <Post: Second post>, <Post: Third post>]>
127+
109128
.. fieldlookup:: arrayfield.overlap
110129

111130
``overlap``

tests/model_fields_/test_arrayfield.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,18 @@ def test_in_as_F_object(self):
336336
self.objs,
337337
)
338338

339+
def test_contained_by(self):
340+
self.assertSequenceEqual(
341+
NullableIntegerArrayModel.objects.filter(field__contained_by=[1, 2]),
342+
self.objs[:2],
343+
)
344+
345+
def test_contained_by_including_F_object(self):
346+
self.assertSequenceEqual(
347+
NullableIntegerArrayModel.objects.filter(field__contained_by=[models.F("order"), 2]),
348+
self.objs[:3],
349+
)
350+
339351
def test_contains(self):
340352
self.assertSequenceEqual(
341353
NullableIntegerArrayModel.objects.filter(field__contains=[2]),
@@ -370,6 +382,9 @@ def test_icontains(self):
370382
def test_contains_charfield(self):
371383
self.assertSequenceEqual(CharArrayModel.objects.filter(field__contains=["text"]), [])
372384

385+
def test_contained_by_charfield(self):
386+
self.assertSequenceEqual(CharArrayModel.objects.filter(field__contained_by=["text"]), [])
387+
373388
def test_overlap_charfield(self):
374389
self.assertSequenceEqual(CharArrayModel.objects.filter(field__overlap=["text"]), [])
375390

0 commit comments

Comments
 (0)