Skip to content

Commit 8af8878

Browse files
authored
Merge pull request #2578 from idoshr/Update_with_Aggregation_Pipeline
Update with aggregation pipeline
2 parents f9a303f + 6be253f commit 8af8878

File tree

4 files changed

+67
-4
lines changed

4 files changed

+67
-4
lines changed

docs/guide/querying.rst

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,35 @@ However, this doesn't map well to the syntax so you can also use a capital S ins
218218

219219
Raw queries
220220
-----------
221-
It is possible to provide a raw :mod:`PyMongo` query as a query parameter, which will
222-
be integrated directly into the query. This is done using the ``__raw__``
221+
It is possible to provide a raw :mod:`PyMongo` query as a query parameter or update as a update parameter , which will
222+
be integrated directly into the query or update. This is done using the ``__raw__``
223223
keyword argument::
224224

225225
Page.objects(__raw__={'tags': 'coding'})
226226

227+
# or for update
228+
229+
Page.objects(__raw__={'tags': 'coding'}).update(__raw__={'$set': {'tags': 'coding'}})
230+
231+
Page.objects(tags='coding').update(__raw__={'$set': {'tags': 'coding'}})
232+
233+
.. versionadded:: 0.4
234+
235+
236+
Update with Aggregation Pipeline
237+
-----------
238+
It is possible to provide a raw :mod:`PyMongo` aggregation update parameter, which will
239+
be integrated directly into the update. This is done by using ``__raw__`` field and value of array
240+
pipeline
241+
`Update with Aggregation Pipeline <https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/#update-with-aggregation->`_
242+
keyword argument::
243+
244+
# 'tags' field is set to 'coding is fun'
245+
Page.objects(tags='coding').update(__raw__=[
246+
{"$set": {"tags": {"$concat": ["$tags", "is fun"]}}}
247+
],
248+
)
249+
227250
.. versionadded:: 0.4
228251

229252
Sorting/Ordering results

mongoengine/queryset/base.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -551,8 +551,13 @@ def update(
551551

552552
queryset = self.clone()
553553
query = queryset._query
554-
update = transform.update(queryset._document, **update)
555-
554+
if "__raw__" in update and isinstance(update["__raw__"], list):
555+
update = [
556+
transform.update(queryset._document, **{"__raw__": u})
557+
for u in update["__raw__"]
558+
]
559+
else:
560+
update = transform.update(queryset._document, **update)
556561
# If doing an atomic upsert on an inheritable class
557562
# then ensure we add _cls to the update operation
558563
if upsert and "_cls" in query:

tests/queryset/test_queryset.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
queryset_manager,
2626
)
2727
from tests.utils import (
28+
requires_mongodb_gte_42,
2829
requires_mongodb_gte_44,
2930
requires_mongodb_lt_42,
3031
)
@@ -2217,6 +2218,36 @@ class BlogPost(Document):
22172218
post.reload()
22182219
assert post.tags == ["code", "mongodb"]
22192220

2221+
@requires_mongodb_gte_42
2222+
def test_aggregation_update(self):
2223+
"""Ensure that the 'aggregation_update' update works correctly."""
2224+
2225+
class BlogPost(Document):
2226+
slug = StringField()
2227+
tags = ListField(StringField())
2228+
2229+
BlogPost.drop_collection()
2230+
2231+
post = BlogPost(slug="test")
2232+
post.save()
2233+
2234+
BlogPost.objects(slug="test").update(
2235+
__raw__=[{"$set": {"slug": {"$concat": ["$slug", " ", "$slug"]}}}],
2236+
)
2237+
post.reload()
2238+
assert post.slug == "test test"
2239+
2240+
BlogPost.objects(slug="test test").update(
2241+
__raw__=[
2242+
{"$set": {"slug": {"$concat": ["$slug", " ", "it"]}}}, # test test it
2243+
{
2244+
"$set": {"slug": {"$concat": ["When", " ", "$slug"]}}
2245+
}, # When test test it
2246+
],
2247+
)
2248+
post.reload()
2249+
assert post.slug == "When test test it"
2250+
22202251
def test_add_to_set_each(self):
22212252
class Item(Document):
22222253
name = StringField(required=True)

tests/utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ def requires_mongodb_lt_42(func):
3737
return _decorated_with_ver_requirement(func, (4, 2), oper=operator.lt)
3838

3939

40+
def requires_mongodb_gte_42(func):
41+
return _decorated_with_ver_requirement(func, (4, 2), oper=operator.ge)
42+
43+
4044
def requires_mongodb_gte_44(func):
4145
return _decorated_with_ver_requirement(func, (4, 4), oper=operator.ge)
4246

0 commit comments

Comments
 (0)