Merge branch 'master' of github.com:MongoEngine/mongoengine into feature/allow-setting-read-concern-queryset
This commit is contained in:
commit
4e58e9f8d1
@ -8,6 +8,7 @@ Development
|
|||||||
- (Fill this out as you fix issues and develop your features).
|
- (Fill this out as you fix issues and develop your features).
|
||||||
- Add Mongo 4.0 to Travis
|
- Add Mongo 4.0 to Travis
|
||||||
- Fixed a bug causing inaccurate query results, while combining ``__raw__`` and regular filters for the same field #2264
|
- Fixed a bug causing inaccurate query results, while combining ``__raw__`` and regular filters for the same field #2264
|
||||||
|
- Add support for the `elemMatch` projection operator in .fields (e.g BlogPost.objects.fields(elemMatch__comments="test")) #2267
|
||||||
|
|
||||||
Changes in 0.19.1
|
Changes in 0.19.1
|
||||||
=================
|
=================
|
||||||
|
@ -56,7 +56,7 @@ class InvalidCollectionError(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
|
class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
|
||||||
"""A :class:`~mongoengine.Document` that isn't stored in its own
|
r"""A :class:`~mongoengine.Document` that isn't stored in its own
|
||||||
collection. :class:`~mongoengine.EmbeddedDocument`\ s should be used as
|
collection. :class:`~mongoengine.EmbeddedDocument`\ s should be used as
|
||||||
fields on :class:`~mongoengine.Document`\ s through the
|
fields on :class:`~mongoengine.Document`\ s through the
|
||||||
:class:`~mongoengine.EmbeddedDocumentField` field type.
|
:class:`~mongoengine.EmbeddedDocumentField` field type.
|
||||||
|
@ -711,8 +711,8 @@ class BaseQuerySet(object):
|
|||||||
def in_bulk(self, object_ids):
|
def in_bulk(self, object_ids):
|
||||||
"""Retrieve a set of documents by their ids.
|
"""Retrieve a set of documents by their ids.
|
||||||
|
|
||||||
:param object_ids: a list or tuple of ``ObjectId``\ s
|
:param object_ids: a list or tuple of ObjectId's
|
||||||
:rtype: dict of ObjectIds as keys and collection-specific
|
:rtype: dict of ObjectId's as keys and collection-specific
|
||||||
Document subclasses as values.
|
Document subclasses as values.
|
||||||
|
|
||||||
.. versionadded:: 0.3
|
.. versionadded:: 0.3
|
||||||
@ -1043,9 +1043,11 @@ class BaseQuerySet(object):
|
|||||||
|
|
||||||
posts = BlogPost.objects(...).fields(comments=0)
|
posts = BlogPost.objects(...).fields(comments=0)
|
||||||
|
|
||||||
To retrieve a subrange of array elements:
|
To retrieve a subrange or sublist of array elements,
|
||||||
|
support exist for both the `slice` and `elemMatch` projection operator:
|
||||||
|
|
||||||
posts = BlogPost.objects(...).fields(slice__comments=5)
|
posts = BlogPost.objects(...).fields(slice__comments=5)
|
||||||
|
posts = BlogPost.objects(...).fields(elemMatch__comments="test")
|
||||||
|
|
||||||
:param kwargs: A set of keyword arguments identifying what to
|
:param kwargs: A set of keyword arguments identifying what to
|
||||||
include, exclude, or slice.
|
include, exclude, or slice.
|
||||||
@ -1054,7 +1056,7 @@ class BaseQuerySet(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Check for an operator and transform to mongo-style if there is
|
# Check for an operator and transform to mongo-style if there is
|
||||||
operators = ["slice"]
|
operators = ["slice", "elemMatch"]
|
||||||
cleaned_fields = []
|
cleaned_fields = []
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
parts = key.split("__")
|
parts = key.split("__")
|
||||||
@ -1157,7 +1159,7 @@ class BaseQuerySet(object):
|
|||||||
|
|
||||||
def explain(self):
|
def explain(self):
|
||||||
"""Return an explain plan record for the
|
"""Return an explain plan record for the
|
||||||
:class:`~mongoengine.queryset.QuerySet`\ 's cursor.
|
:class:`~mongoengine.queryset.QuerySet` cursor.
|
||||||
"""
|
"""
|
||||||
return self._cursor.explain()
|
return self._cursor.explain()
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
|
|||||||
for values in itertools.product([2014], mm, dd, hh, ii, ss, microsecond):
|
for values in itertools.product([2014], mm, dd, hh, ii, ss, microsecond):
|
||||||
stored = LogEntry(date=datetime.datetime(*values)).to_mongo()["date"]
|
stored = LogEntry(date=datetime.datetime(*values)).to_mongo()["date"]
|
||||||
assert (
|
assert (
|
||||||
re.match("^\d{4},\d{2},\d{2},\d{2},\d{2},\d{2},\d{6}$", stored)
|
re.match(r"^\d{4},\d{2},\d{2},\d{2},\d{2},\d{2},\d{6}$", stored)
|
||||||
is not None
|
is not None
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
|
|||||||
"date_with_dots"
|
"date_with_dots"
|
||||||
]
|
]
|
||||||
assert (
|
assert (
|
||||||
re.match("^\d{4}.\d{2}.\d{2}.\d{2}.\d{2}.\d{2}.\d{6}$", stored) is not None
|
re.match(r"^\d{4}.\d{2}.\d{2}.\d{2}.\d{2}.\d{2}.\d{6}$", stored) is not None
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_complexdatetime_usage(self):
|
def test_complexdatetime_usage(self):
|
||||||
|
@ -4477,6 +4477,74 @@ class TestQueryset(unittest.TestCase):
|
|||||||
expected = "[u'A1', u'A2']"
|
expected = "[u'A1', u'A2']"
|
||||||
assert expected == "%s" % sorted(names)
|
assert expected == "%s" % sorted(names)
|
||||||
|
|
||||||
|
def test_fields(self):
|
||||||
|
class Bar(EmbeddedDocument):
|
||||||
|
v = StringField()
|
||||||
|
z = StringField()
|
||||||
|
|
||||||
|
class Foo(Document):
|
||||||
|
x = StringField()
|
||||||
|
y = IntField()
|
||||||
|
items = EmbeddedDocumentListField(Bar)
|
||||||
|
|
||||||
|
Foo.drop_collection()
|
||||||
|
|
||||||
|
Foo(x="foo1", y=1).save()
|
||||||
|
Foo(x="foo2", y=2, items=[]).save()
|
||||||
|
Foo(x="foo3", y=3, items=[Bar(z="a", v="V")]).save()
|
||||||
|
Foo(
|
||||||
|
x="foo4",
|
||||||
|
y=4,
|
||||||
|
items=[
|
||||||
|
Bar(z="a", v="V"),
|
||||||
|
Bar(z="b", v="W"),
|
||||||
|
Bar(z="b", v="X"),
|
||||||
|
Bar(z="c", v="V"),
|
||||||
|
],
|
||||||
|
).save()
|
||||||
|
Foo(
|
||||||
|
x="foo5",
|
||||||
|
y=5,
|
||||||
|
items=[
|
||||||
|
Bar(z="b", v="X"),
|
||||||
|
Bar(z="c", v="V"),
|
||||||
|
Bar(z="d", v="V"),
|
||||||
|
Bar(z="e", v="V"),
|
||||||
|
],
|
||||||
|
).save()
|
||||||
|
|
||||||
|
foos_with_x = list(Foo.objects.order_by("y").fields(x=1))
|
||||||
|
|
||||||
|
assert all(o.x is not None for o in foos_with_x)
|
||||||
|
|
||||||
|
foos_without_y = list(Foo.objects.order_by("y").fields(y=0))
|
||||||
|
|
||||||
|
assert all(o.y is None for o in foos_with_x)
|
||||||
|
|
||||||
|
foos_with_sliced_items = list(Foo.objects.order_by("y").fields(slice__items=1))
|
||||||
|
|
||||||
|
assert foos_with_sliced_items[0].items == []
|
||||||
|
assert foos_with_sliced_items[1].items == []
|
||||||
|
assert len(foos_with_sliced_items[2].items) == 1
|
||||||
|
assert foos_with_sliced_items[2].items[0].z == "a"
|
||||||
|
assert len(foos_with_sliced_items[3].items) == 1
|
||||||
|
assert foos_with_sliced_items[3].items[0].z == "a"
|
||||||
|
assert len(foos_with_sliced_items[4].items) == 1
|
||||||
|
assert foos_with_sliced_items[4].items[0].z == "b"
|
||||||
|
|
||||||
|
foos_with_elem_match_items = list(
|
||||||
|
Foo.objects.order_by("y").fields(elemMatch__items={"z": "b"})
|
||||||
|
)
|
||||||
|
|
||||||
|
assert foos_with_elem_match_items[0].items == []
|
||||||
|
assert foos_with_elem_match_items[1].items == []
|
||||||
|
assert foos_with_elem_match_items[2].items == []
|
||||||
|
assert len(foos_with_elem_match_items[3].items) == 1
|
||||||
|
assert foos_with_elem_match_items[3].items[0].z == "b"
|
||||||
|
assert foos_with_elem_match_items[3].items[0].v == "W"
|
||||||
|
assert len(foos_with_elem_match_items[4].items) == 1
|
||||||
|
assert foos_with_elem_match_items[4].items[0].z == "b"
|
||||||
|
|
||||||
def test_elem_match(self):
|
def test_elem_match(self):
|
||||||
class Foo(EmbeddedDocument):
|
class Foo(EmbeddedDocument):
|
||||||
shape = StringField()
|
shape = StringField()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user