parent
09c32a63ce
commit
14be7ba2e2
@ -5,6 +5,7 @@ Changelog
|
|||||||
Changes in dev
|
Changes in dev
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
- Added support for the positional operator
|
||||||
- Updated geo index checking to be recursive and check in embedded documents
|
- Updated geo index checking to be recursive and check in embedded documents
|
||||||
- Updated default collection naming convention
|
- Updated default collection naming convention
|
||||||
- Added Document Mixin support
|
- Added Document Mixin support
|
||||||
|
@ -23,7 +23,7 @@ fetch documents from the database::
|
|||||||
Filtering queries
|
Filtering queries
|
||||||
=================
|
=================
|
||||||
The query may be filtered by calling the
|
The query may be filtered by calling the
|
||||||
:class:`~mongoengine.queryset.QuerySet` object with field lookup keyword
|
:class:`~mongoengine.queryset.QuerySet` object with field lookup keyword
|
||||||
arguments. The keys in the keyword arguments correspond to fields on the
|
arguments. The keys in the keyword arguments correspond to fields on the
|
||||||
:class:`~mongoengine.Document` you are querying::
|
:class:`~mongoengine.Document` you are querying::
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ Available operators are as follows:
|
|||||||
* ``nin`` -- value is not in list (a list of values should be provided)
|
* ``nin`` -- value is not in list (a list of values should be provided)
|
||||||
* ``mod`` -- ``value % x == y``, where ``x`` and ``y`` are two provided values
|
* ``mod`` -- ``value % x == y``, where ``x`` and ``y`` are two provided values
|
||||||
* ``all`` -- every item in list of values provided is in array
|
* ``all`` -- every item in list of values provided is in array
|
||||||
* ``size`` -- the size of the array is
|
* ``size`` -- the size of the array is
|
||||||
* ``exists`` -- value for field exists
|
* ``exists`` -- value for field exists
|
||||||
|
|
||||||
The following operators are available as shortcuts to querying with regular
|
The following operators are available as shortcuts to querying with regular
|
||||||
@ -163,9 +163,9 @@ To retrieve a result that should be unique in the collection, use
|
|||||||
and :class:`~mongoengine.queryset.MultipleObjectsReturned` if more than one
|
and :class:`~mongoengine.queryset.MultipleObjectsReturned` if more than one
|
||||||
document matched the query.
|
document matched the query.
|
||||||
|
|
||||||
A variation of this method exists,
|
A variation of this method exists,
|
||||||
:meth:`~mongoengine.queryset.Queryset.get_or_create`, that will create a new
|
:meth:`~mongoengine.queryset.Queryset.get_or_create`, that will create a new
|
||||||
document with the query arguments if no documents match the query. An
|
document with the query arguments if no documents match the query. An
|
||||||
additional keyword argument, :attr:`defaults` may be provided, which will be
|
additional keyword argument, :attr:`defaults` may be provided, which will be
|
||||||
used as default values for the new document, in the case that it should need
|
used as default values for the new document, in the case that it should need
|
||||||
to be created::
|
to be created::
|
||||||
@ -240,7 +240,7 @@ Javascript code that is executed on the database server.
|
|||||||
Counting results
|
Counting results
|
||||||
----------------
|
----------------
|
||||||
Just as with limiting and skipping results, there is a method on
|
Just as with limiting and skipping results, there is a method on
|
||||||
:class:`~mongoengine.queryset.QuerySet` objects --
|
:class:`~mongoengine.queryset.QuerySet` objects --
|
||||||
:meth:`~mongoengine.queryset.QuerySet.count`, but there is also a more Pythonic
|
:meth:`~mongoengine.queryset.QuerySet.count`, but there is also a more Pythonic
|
||||||
way of achieving this::
|
way of achieving this::
|
||||||
|
|
||||||
@ -309,11 +309,11 @@ Advanced queries
|
|||||||
================
|
================
|
||||||
Sometimes calling a :class:`~mongoengine.queryset.QuerySet` object with keyword
|
Sometimes calling a :class:`~mongoengine.queryset.QuerySet` object with keyword
|
||||||
arguments can't fully express the query you want to use -- for example if you
|
arguments can't fully express the query you want to use -- for example if you
|
||||||
need to combine a number of constraints using *and* and *or*. This is made
|
need to combine a number of constraints using *and* and *or*. This is made
|
||||||
possible in MongoEngine through the :class:`~mongoengine.queryset.Q` class.
|
possible in MongoEngine through the :class:`~mongoengine.queryset.Q` class.
|
||||||
A :class:`~mongoengine.queryset.Q` object represents part of a query, and
|
A :class:`~mongoengine.queryset.Q` object represents part of a query, and
|
||||||
can be initialised using the same keyword-argument syntax you use to query
|
can be initialised using the same keyword-argument syntax you use to query
|
||||||
documents. To build a complex query, you may combine
|
documents. To build a complex query, you may combine
|
||||||
:class:`~mongoengine.queryset.Q` objects using the ``&`` (and) and ``|`` (or)
|
:class:`~mongoengine.queryset.Q` objects using the ``&`` (and) and ``|`` (or)
|
||||||
operators. To use a :class:`~mongoengine.queryset.Q` object, pass it in as the
|
operators. To use a :class:`~mongoengine.queryset.Q` object, pass it in as the
|
||||||
first positional argument to :attr:`Document.objects` when you filter it by
|
first positional argument to :attr:`Document.objects` when you filter it by
|
||||||
@ -434,7 +434,7 @@ Atomic updates
|
|||||||
==============
|
==============
|
||||||
Documents may be updated atomically by using the
|
Documents may be updated atomically by using the
|
||||||
:meth:`~mongoengine.queryset.QuerySet.update_one` and
|
:meth:`~mongoengine.queryset.QuerySet.update_one` and
|
||||||
:meth:`~mongoengine.queryset.QuerySet.update` methods on a
|
:meth:`~mongoengine.queryset.QuerySet.update` methods on a
|
||||||
:meth:`~mongoengine.queryset.QuerySet`. There are several different "modifiers"
|
:meth:`~mongoengine.queryset.QuerySet`. There are several different "modifiers"
|
||||||
that you may use with these methods:
|
that you may use with these methods:
|
||||||
|
|
||||||
@ -450,7 +450,7 @@ that you may use with these methods:
|
|||||||
* ``pull_all`` -- remove several values from a list
|
* ``pull_all`` -- remove several values from a list
|
||||||
* ``add_to_set`` -- add value to a list only if its not in the list already
|
* ``add_to_set`` -- add value to a list only if its not in the list already
|
||||||
|
|
||||||
The syntax for atomic updates is similar to the querying syntax, but the
|
The syntax for atomic updates is similar to the querying syntax, but the
|
||||||
modifier comes before the field, not after it::
|
modifier comes before the field, not after it::
|
||||||
|
|
||||||
>>> post = BlogPost(title='Test', page_views=0, tags=['database'])
|
>>> post = BlogPost(title='Test', page_views=0, tags=['database'])
|
||||||
@ -467,3 +467,19 @@ modifier comes before the field, not after it::
|
|||||||
>>> post.reload()
|
>>> post.reload()
|
||||||
>>> post.tags
|
>>> post.tags
|
||||||
['database', 'nosql']
|
['database', 'nosql']
|
||||||
|
|
||||||
|
The positional operator allows you to update list items without knowing the
|
||||||
|
index position, therefore making the update a single atomic operation. As we
|
||||||
|
cannot use the `$` syntax in keyword arguments it has been mapped to `S`::
|
||||||
|
|
||||||
|
>>> post = BlogPost(title='Test', page_views=0, tags=['database', 'mongo'])
|
||||||
|
>>> post.save()
|
||||||
|
>>> BlogPost.objects(id=post.id, tags='mongo').update(set__tags__S='mongodb')
|
||||||
|
>>> post.reload()
|
||||||
|
>>> post.tags
|
||||||
|
['database', 'mongodb']
|
||||||
|
|
||||||
|
.. note ::
|
||||||
|
Currently only top level lists are handled, future versions of mongodb /
|
||||||
|
pymongo plan to support nested positional operators. See `The $ positional
|
||||||
|
operator <http://www.mongodb.org/display/DOCS/Updating#Updating-The%24positionaloperator>`_.
|
@ -1215,6 +1215,9 @@ class QuerySet(object):
|
|||||||
append_field = True
|
append_field = True
|
||||||
for field in fields:
|
for field in fields:
|
||||||
if isinstance(field, str):
|
if isinstance(field, str):
|
||||||
|
# Convert the S operator to $
|
||||||
|
if field == 'S':
|
||||||
|
field = '$'
|
||||||
parts.append(field)
|
parts.append(field)
|
||||||
append_field = False
|
append_field = False
|
||||||
else:
|
else:
|
||||||
@ -1243,7 +1246,7 @@ class QuerySet(object):
|
|||||||
|
|
||||||
return mongo_update
|
return mongo_update
|
||||||
|
|
||||||
def update(self, safe_update=True, upsert=False, write_options=None, **update):
|
def update(self, safe_update=True, upsert=False, multi=True, write_options=None, **update):
|
||||||
"""Perform an atomic update on the fields matched by the query. When
|
"""Perform an atomic update on the fields matched by the query. When
|
||||||
``safe_update`` is used, the number of affected documents is returned.
|
``safe_update`` is used, the number of affected documents is returned.
|
||||||
|
|
||||||
@ -1261,7 +1264,7 @@ class QuerySet(object):
|
|||||||
|
|
||||||
update = QuerySet._transform_update(self._document, **update)
|
update = QuerySet._transform_update(self._document, **update)
|
||||||
try:
|
try:
|
||||||
ret = self._collection.update(self._query, update, multi=True,
|
ret = self._collection.update(self._query, update, multi=multi,
|
||||||
upsert=upsert, safe=safe_update,
|
upsert=upsert, safe=safe_update,
|
||||||
**write_options)
|
**write_options)
|
||||||
if ret is not None and 'n' in ret:
|
if ret is not None and 'n' in ret:
|
||||||
|
@ -260,6 +260,77 @@ class QuerySetTest(unittest.TestCase):
|
|||||||
|
|
||||||
Blog.drop_collection()
|
Blog.drop_collection()
|
||||||
|
|
||||||
|
def test_update_using_positional_operator(self):
|
||||||
|
"""Ensure that the list fields can be updated using the positional
|
||||||
|
operator."""
|
||||||
|
|
||||||
|
class Comment(EmbeddedDocument):
|
||||||
|
by = StringField()
|
||||||
|
votes = IntField()
|
||||||
|
|
||||||
|
class BlogPost(Document):
|
||||||
|
title = StringField()
|
||||||
|
comments = ListField(EmbeddedDocumentField(Comment))
|
||||||
|
|
||||||
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
|
c1 = Comment(by="joe", votes=3)
|
||||||
|
c2 = Comment(by="jane", votes=7)
|
||||||
|
|
||||||
|
BlogPost(title="ABC", comments=[c1, c2]).save()
|
||||||
|
|
||||||
|
BlogPost.objects(comments__by="joe").update(inc__comments__S__votes=1)
|
||||||
|
|
||||||
|
post = BlogPost.objects.first()
|
||||||
|
self.assertEquals(post.comments[0].by, 'joe')
|
||||||
|
self.assertEquals(post.comments[0].votes, 4)
|
||||||
|
|
||||||
|
# Currently the $ operator only applies to the first matched item in
|
||||||
|
# the query
|
||||||
|
|
||||||
|
class Simple(Document):
|
||||||
|
x = ListField()
|
||||||
|
|
||||||
|
Simple.drop_collection()
|
||||||
|
Simple(x=[1, 2, 3, 2]).save()
|
||||||
|
Simple.objects(x=2).update(inc__x__S=1)
|
||||||
|
|
||||||
|
simple = Simple.objects.first()
|
||||||
|
self.assertEquals(simple.x, [1, 3, 3, 2])
|
||||||
|
Simple.drop_collection()
|
||||||
|
|
||||||
|
# You can set multiples
|
||||||
|
Simple.drop_collection()
|
||||||
|
Simple(x=[1, 2, 3, 4]).save()
|
||||||
|
Simple(x=[2, 3, 4, 5]).save()
|
||||||
|
Simple(x=[3, 4, 5, 6]).save()
|
||||||
|
Simple(x=[4, 5, 6, 7]).save()
|
||||||
|
Simple.objects(x=3).update(set__x__S=0)
|
||||||
|
|
||||||
|
s = Simple.objects()
|
||||||
|
self.assertEquals(s[0].x, [1, 2, 0, 4])
|
||||||
|
self.assertEquals(s[1].x, [2, 0, 4, 5])
|
||||||
|
self.assertEquals(s[2].x, [0, 4, 5, 6])
|
||||||
|
self.assertEquals(s[3].x, [4, 5, 6, 7])
|
||||||
|
|
||||||
|
# Using "$unset" with an expression like this "array.$" will result in
|
||||||
|
# the array item becoming None, not being removed.
|
||||||
|
Simple.drop_collection()
|
||||||
|
Simple(x=[1, 2, 3, 4, 3, 2, 3, 4]).save()
|
||||||
|
Simple.objects(x=3).update(unset__x__S=1)
|
||||||
|
simple = Simple.objects.first()
|
||||||
|
self.assertEquals(simple.x, [1, 2, None, 4, 3, 2, 3, 4])
|
||||||
|
|
||||||
|
# Nested updates arent supported yet..
|
||||||
|
def update_nested():
|
||||||
|
Simple.drop_collection()
|
||||||
|
Simple(x=[{'test': [1, 2, 3, 4]}]).save()
|
||||||
|
Simple.objects(x__test=2).update(set__x__S__test__S=3)
|
||||||
|
self.assertEquals(simple.x, [1, 2, 3, 4])
|
||||||
|
|
||||||
|
self.assertRaises(OperationError, update_nested)
|
||||||
|
Simple.drop_collection()
|
||||||
|
|
||||||
def test_mapfield_update(self):
|
def test_mapfield_update(self):
|
||||||
"""Ensure that the MapField can be updated."""
|
"""Ensure that the MapField can be updated."""
|
||||||
class Member(EmbeddedDocument):
|
class Member(EmbeddedDocument):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user