Skip to content

Commit e42f36d

Browse files
committed
array_filters
1 parent 090e6e1 commit e42f36d

File tree

4 files changed

+84
-1
lines changed

4 files changed

+84
-1
lines changed

docs/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Development
88
===========
99
- (Fill this out as you fix issues and develop your features).
1010
- Fix for uuidRepresentation not read when provided in URI #2741
11+
- Add option to user array_filters https://www.mongodb.com/docs/manual/reference/operator/update/positional-filtered/
1112

1213
Changes in 0.27.0
1314
=================

docs/guide/querying.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,24 @@ and provide the pipeline as a list
252252

253253
.. versionadded:: 0.23.2
254254

255+
Update with Array Operator
256+
--------------------------------
257+
It is possible to update specific value in array by use array_filters (arrayFilters) operator.
258+
This is done by using ``__raw__`` keyword argument to the update method and provide the arrayFilters as a list.
259+
260+
`Update with Array Operator <https://www.mongodb.com/docs/manual/reference/operator/update/positional-filtered->`_
261+
::
262+
263+
# 'tags' field == ['test1', 'test2', 'test3']
264+
Page.objects().update(__raw__=
265+
{'$set': {"tags.$[element]": 'test11111'}},
266+
array_filters=[{"element": {'$eq': 'test2'}}],
267+
268+
# 'tags' field == ['test1', 'test11111', 'test3']
269+
270+
)
271+
272+
255273
Sorting/Ordering results
256274
========================
257275
It is possible to order the results by 1 or more keys using :meth:`~mongoengine.queryset.QuerySet.order_by`.

mongoengine/queryset/base.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,7 @@ def update(
530530
write_concern=None,
531531
read_concern=None,
532532
full_result=False,
533+
array_filters=None,
533534
**update,
534535
):
535536
"""Perform an atomic update on the fields matched by the query.
@@ -545,6 +546,7 @@ def update(
545546
:param read_concern: Override the read concern for the operation
546547
:param full_result: Return the associated ``pymongo.UpdateResult`` rather than just the number
547548
updated items
549+
:param array_filters: A list of filters specifying which array elements an update should apply.
548550
:param update: Django-style update keyword arguments
549551
550552
:returns the number of updated documents (unless ``full_result`` is True)
@@ -565,6 +567,8 @@ def update(
565567
for u in update["__raw__"]
566568
]
567569
else:
570+
if 'array_filters' in update:
571+
array_filters = update.pop('array_filters')
568572
update = transform.update(queryset._document, **update)
569573
# If doing an atomic upsert on an inheritable class
570574
# then ensure we add _cls to the update operation
@@ -580,7 +584,7 @@ def update(
580584
update_func = collection.update_one
581585
if multi:
582586
update_func = collection.update_many
583-
result = update_func(query, update, upsert=upsert)
587+
result = update_func(query, update, upsert=upsert, array_filters=array_filters)
584588
if full_result:
585589
return result
586590
elif result.raw_result:

tests/queryset/test_queryset.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,66 @@ class Blog(Document):
591591

592592
Blog.drop_collection()
593593

594+
def test_update_array_filters(self):
595+
"""Ensure that updating by array_filters works.
596+
"""
597+
598+
class Comment(EmbeddedDocument):
599+
comment_tags = ListField(StringField())
600+
601+
class Blog(Document):
602+
tags = ListField(StringField())
603+
comments = EmbeddedDocumentField(Comment)
604+
605+
Blog.drop_collection()
606+
607+
# update one
608+
Blog.objects.create(tags=['test1', 'test2', 'test3'])
609+
610+
Blog.objects().update_one(__raw__=
611+
{'$set': {"tags.$[element]": 'test11111'}},
612+
array_filters=[{"element": {'$eq': 'test2'}}],
613+
)
614+
testc_blogs = Blog.objects(tags="test11111")
615+
616+
assert testc_blogs.count() == 1
617+
618+
Blog.drop_collection()
619+
620+
# update one inner list
621+
comments = Comment(comment_tags=['test1', 'test2', 'test3'])
622+
Blog.objects.create(comments=comments)
623+
624+
Blog.objects().update_one(__raw__=
625+
{'$set': {"comments.comment_tags.$[element]": 'test11111'}},
626+
array_filters=[{"element": {'$eq': 'test2'}}],
627+
)
628+
testc_blogs = Blog.objects(comments__comment_tags="test11111")
629+
630+
assert testc_blogs.count() == 1
631+
632+
# update many
633+
Blog.drop_collection()
634+
635+
Blog.objects.create(tags=['test1', 'test2', 'test3', 'test_all'])
636+
Blog.objects.create(tags=['test4', 'test5', 'test6', 'test_all'])
637+
638+
Blog.objects().update(__raw__=
639+
{'$set': {"tags.$[element]": 'test11111'}},
640+
array_filters=[{"element": {'$eq': 'test2'}}],
641+
)
642+
testc_blogs = Blog.objects(tags="test11111")
643+
644+
assert testc_blogs.count() == 1
645+
646+
Blog.objects().update(__raw__=
647+
{'$set': {"tags.$[element]": 'test_all1234577'}},
648+
array_filters=[{"element": {'$eq': 'test_all'}}],
649+
)
650+
testc_blogs = Blog.objects(tags="test_all1234577")
651+
652+
assert testc_blogs.count() == 2
653+
594654
def test_update_using_positional_operator(self):
595655
"""Ensure that the list fields can be updated using the positional
596656
operator."""

0 commit comments

Comments
 (0)