summaryrefslogtreecommitdiff
diff options
authorAnanthu C V <weepingclown@disroot.org>2024-03-12 00:29:40 +0530
committergit-ubuntu importer <ubuntu-devel-discuss@lists.ubuntu.com>2024-03-22 04:35:23 +0000
commit78ab29bd8d914f4c96246eaa0cc836644117d3be (patch)
tree85bdea8757fe2585fba606b6551eeeb453a2215c
parent82cf83cbd298561bde168ef6a521c0e923730359 (diff)
Imported using git-ubuntu import.
Notes
Notes: * Team upload * New upstream version 0.28.2 * Drop upstream merged patch
-rw-r--r--PKG-INFO6
-rw-r--r--README.rst2
-rw-r--r--debian/changelog8
-rw-r--r--debian/patches/0002-add-missing-doc-link.patch14
-rw-r--r--debian/patches/series1
-rw-r--r--docs/changelog.rst25
-rw-r--r--docs/guide/logging-monitoring.rst2
-rw-r--r--docs/guide/querying.rst20
-rw-r--r--mongoengine.egg-info/PKG-INFO6
-rw-r--r--mongoengine/__init__.py2
-rw-r--r--mongoengine/base/document.py2
-rw-r--r--mongoengine/base/fields.py6
-rw-r--r--mongoengine/connection.py35
-rw-r--r--mongoengine/context_managers.py33
-rw-r--r--mongoengine/fields.py16
-rw-r--r--mongoengine/mongodb_support.py3
-rw-r--r--mongoengine/pymongo_support.py20
-rw-r--r--mongoengine/queryset/base.py63
-rw-r--r--mongoengine/queryset/transform.py13
-rw-r--r--setup.py2
20 files changed, 201 insertions, 78 deletions
diff --git a/PKG-INFO b/PKG-INFO
index 483bf98..b83bf81 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: mongoengine
-Version: 0.27.0
+Version: 0.28.2
Summary: MongoEngine is a Python Object-Document Mapper for working with MongoDB.
Home-page: http://mongoengine.org/
Author: Harry Marr
@@ -16,8 +16,6 @@ Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
@@ -62,7 +60,7 @@ an `API reference <https://mongoengine-odm.readthedocs.io/apireference.html>`_.
Supported MongoDB Versions
==========================
-MongoEngine is currently tested against MongoDB v3.6, v4.0, v4.4 and v5.0. Future versions
+MongoEngine is currently tested against MongoDB v3.6, v4.0, v4.4, v5.0, v6.0 and v7.0. Future versions
should be supported as well, but aren't actively tested at the moment. Make
sure to open an issue or submit a pull request if you experience any problems
with a more recent MongoDB versions.
diff --git a/README.rst b/README.rst
index 39497fb..64e43b6 100644
--- a/README.rst
+++ b/README.rst
@@ -31,7 +31,7 @@ an `API reference <https://mongoengine-odm.readthedocs.io/apireference.html>`_.
Supported MongoDB Versions
==========================
-MongoEngine is currently tested against MongoDB v3.6, v4.0, v4.4 and v5.0. Future versions
+MongoEngine is currently tested against MongoDB v3.6, v4.0, v4.4, v5.0, v6.0 and v7.0. Future versions
should be supported as well, but aren't actively tested at the moment. Make
sure to open an issue or submit a pull request if you experience any problems
with a more recent MongoDB versions.
diff --git a/debian/changelog b/debian/changelog
index 8923282..b4536a7 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+python-mongoengine (0.28.2-1) unstable; urgency=medium
+
+ * Team upload
+ * New upstream version 0.28.2
+ * Drop upstream merged patch
+
+ -- Ananthu C V <weepingclown@disroot.org> Tue, 12 Mar 2024 00:29:40 +0530
+
python-mongoengine (0.27.0-1) unstable; urgency=medium
* Team upload
diff --git a/debian/patches/0002-add-missing-doc-link.patch b/debian/patches/0002-add-missing-doc-link.patch
deleted file mode 100644
index 5a926bb..0000000
--- a/debian/patches/0002-add-missing-doc-link.patch
+++ /dev/null
@@ -1,14 +0,0 @@
-Description: add a missing link in the doc
-Author: Ananthu C V <weepingclown@disroot.org>
-Forwarded: https://github.com/MongoEngine/mongoengine/pull/2792
-Last-Update: 2023-12-30
---- a/docs/guide/querying.rst
-+++ b/docs/guide/querying.rst
-@@ -587,6 +587,7 @@
- * ``add_to_set`` -- add value to a list only if its not in the list already
- * ``rename`` -- rename the key name
-
-+.. _need to add upsert=True: http://docs.mongodb.org/manual/reference/operator/update/setOnInsert
- .. _depending on the value: http://docs.mongodb.org/manual/reference/operator/update/pop/
-
- The syntax for atomic updates is similar to the querying syntax, but the
diff --git a/debian/patches/series b/debian/patches/series
index 2519548..7500041 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,2 +1 @@
0001-drop-rtd-extension.patch
-0002-add-missing-doc-link.patch
diff --git a/docs/changelog.rst b/docs/changelog.rst
index b0e54cd..81a0ce6 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -8,6 +8,31 @@ Development
===========
- (Fill this out as you fix issues and develop your features).
+Changes in 0.28.2
+=================
+- Fix typing import incompatible with Pymongo 3.7 #2802
+
+Changes in 0.28.1
+=================
+- Fix bug related with recent updates to no_dereference context manager #2799
+
+Changes in 0.28.0
+=================
+- Fix for uuidRepresentation not read when provided in URI #2741
+- Add option to user array_filters https://www.mongodb.com/docs/manual/reference/operator/update/positional-filtered/ #2769
+- Fix combination of __raw__ and mongoengine syntax #2773
+- Add tests against MongoDB 6.0 and MongoDB 7.0 in the pipeline
+- Fix validate() not being called when inheritance is used in EmbeddedDocument and validate is overriden #2784
+- Add support for readPreferenceTags in connection parameters #2644
+- Use estimated_documents_count OR documents_count when count is called, based on the query #2529
+- Fix no_dereference context manager which wasn't turning off auto-dereferencing correctly in some cases #2788
+- BREAKING CHANGE: no_dereference context manager no longer returns the class in __enter__ #2788
+ as it was useless and making it look like it was returning a different class although it was the same.
+ Thus, it must be called like `with no_dereference(User):` and no longer `with no_dereference(User) as ...:`
+- Added __raw__ to :meth:`~mongoengine.Queryset.order_by()` to allow to provide raw pymongo 'sort' argument and get around some of the limitations #2783
+- Add `text_score` argument on :meth:`~mongoengine.Document.search_text()` to allow text_score computation to be turned off
+ as it interfere with natural returned documents order #2759
+
Changes in 0.27.0
=================
- Update uuidRepresentation warnings with "unspecified" as the future default (instead of 'standard' previously advertised) #2739
diff --git a/docs/guide/logging-monitoring.rst b/docs/guide/logging-monitoring.rst
index 9f523b7..5aedd15 100644
--- a/docs/guide/logging-monitoring.rst
+++ b/docs/guide/logging-monitoring.rst
@@ -2,7 +2,7 @@
Logging/Monitoring
==================
-It is possible to use `pymongo.monitoring <https://api.mongodb.com/python/current/api/pymongo/monitoring.html>`_ to monitor
+It is possible to use `pymongo.monitoring <https://pymongo.readthedocs.io/en/stable/api/pymongo/monitoring.html>`_ to monitor
the driver events (e.g: queries, connections, etc). This can be handy if you want to monitor the queries issued by
MongoEngine to the driver.
diff --git a/docs/guide/querying.rst b/docs/guide/querying.rst
index b9afb60..d5ac70b 100644
--- a/docs/guide/querying.rst
+++ b/docs/guide/querying.rst
@@ -252,6 +252,23 @@ and provide the pipeline as a list
.. versionadded:: 0.23.2
+Update with Array Operator
+--------------------------------
+It is possible to update specific value in array by use array_filters (arrayFilters) operator.
+This is done by using ``__raw__`` keyword argument to the update method and provide the arrayFilters as a list.
+
+`Update with Array Operator <https://www.mongodb.com/docs/manual/reference/operator/update/positional-filtered->`_
+::
+
+ # assuming an initial 'tags' field == ['test1', 'test2', 'test3']
+ Page.objects().update(__raw__={'$set': {"tags.$[element]": 'test11111'}},
+ array_filters=[{"element": {'$eq': 'test2'}}],
+
+ # updated 'tags' field == ['test1', 'test11111', 'test3']
+
+ )
+
+
Sorting/Ordering results
========================
It is possible to order the results by 1 or more keys using :meth:`~mongoengine.queryset.QuerySet.order_by`.
@@ -522,7 +539,7 @@ data. To turn off dereferencing of the results of a query use
You can also turn off all dereferencing for a fixed period by using the
:class:`~mongoengine.context_managers.no_dereference` context manager::
- with no_dereference(Post) as Post:
+ with no_dereference(Post):
post = Post.objects.first()
assert(isinstance(post.author, DBRef))
@@ -587,6 +604,7 @@ There are several different "modifiers" that you may use with these methods:
* ``add_to_set`` -- add value to a list only if its not in the list already
* ``rename`` -- rename the key name
+.. _need to add upsert=True: http://docs.mongodb.org/manual/reference/operator/update/setOnInsert
.. _depending on the value: http://docs.mongodb.org/manual/reference/operator/update/pop/
The syntax for atomic updates is similar to the querying syntax, but the
diff --git a/mongoengine.egg-info/PKG-INFO b/mongoengine.egg-info/PKG-INFO
index 483bf98..b83bf81 100644
--- a/mongoengine.egg-info/PKG-INFO
+++ b/mongoengine.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: mongoengine
-Version: 0.27.0
+Version: 0.28.2
Summary: MongoEngine is a Python Object-Document Mapper for working with MongoDB.
Home-page: http://mongoengine.org/
Author: Harry Marr
@@ -16,8 +16,6 @@ Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
@@ -62,7 +60,7 @@ an `API reference <https://mongoengine-odm.readthedocs.io/apireference.html>`_.
Supported MongoDB Versions
==========================
-MongoEngine is currently tested against MongoDB v3.6, v4.0, v4.4 and v5.0. Future versions
+MongoEngine is currently tested against MongoDB v3.6, v4.0, v4.4, v5.0, v6.0 and v7.0. Future versions
should be supported as well, but aren't actively tested at the moment. Make
sure to open an issue or submit a pull request if you experience any problems
with a more recent MongoDB versions.
diff --git a/mongoengine/__init__.py b/mongoengine/__init__.py
index a42755b..799dad6 100644
--- a/mongoengine/__init__.py
+++ b/mongoengine/__init__.py
@@ -29,7 +29,7 @@ __all__ = (
)
-VERSION = (0, 27, 0)
+VERSION = (0, 28, 2)
def get_version():
diff --git a/mongoengine/base/document.py b/mongoengine/base/document.py
index e5311c5..194b24e 100644
--- a/mongoengine/base/document.py
+++ b/mongoengine/base/document.py
@@ -322,7 +322,7 @@ class BaseDocument:
if "_text_score" not in self._data:
raise InvalidDocumentError(
- "This document is not originally built from a text query"
+ "This document is not originally built from a text query (or text_score was not set on search_text() call)"
)
return self._data["_text_score"]
diff --git a/mongoengine/base/fields.py b/mongoengine/base/fields.py
index 85a10ff..037e916 100644
--- a/mongoengine/base/fields.py
+++ b/mongoengine/base/fields.py
@@ -59,11 +59,11 @@ class BaseField:
unique with (Creates an index).
:param primary_key: Mark this field as the primary key ((Creates an index)). Defaults to False.
:param validation: (optional) A callable to validate the value of the
- field. The callable takes the value as parameter and should raise
+ field. The callable takes the value as parameter and should raise
a ValidationError if validation fails
:param choices: (optional) The valid choices
- :param null: (optional) If the field value can be null when a default exist. If not set, the default value
- will be used in case a field with a default value is set to None. Defaults to False.
+ :param null: (optional) If the field value can be null when a default exists. If not set, the default value
+ will be used in case a field with a default value is set to None. Defaults to False.
:param sparse: (optional) `sparse=True` combined with `unique=True` and `required=False`
means that uniqueness won't be enforced for `None` values (Creates an index). Defaults to False.
:param **kwargs: (optional) Arbitrary indirection-free metadata for
diff --git a/mongoengine/connection.py b/mongoengine/connection.py
index 7852c64..42a7943 100644
--- a/mongoengine/connection.py
+++ b/mongoengine/connection.py
@@ -1,8 +1,16 @@
import warnings
from pymongo import MongoClient, ReadPreference, uri_parser
+from pymongo.common import _UUID_REPRESENTATIONS
from pymongo.database import _check_name
+# DriverInfo was added in PyMongo 3.7.
+try:
+ from pymongo.driver_info import DriverInfo
+except ImportError:
+ DriverInfo = None
+
+import mongoengine
from mongoengine.pymongo_support import PYMONGO_VERSION
__all__ = [
@@ -124,7 +132,9 @@ def _get_connection_settings(
if uri_dict.get(param):
conn_settings[param] = uri_dict[param]
- uri_options = uri_dict["options"]
+ uri_options = uri_dict[
+ "options"
+ ] # uri_options is a _CaseInsensitiveDictionary
if "replicaset" in uri_options:
conn_settings["replicaSet"] = uri_options["replicaset"]
if "authsource" in uri_options:
@@ -153,12 +163,27 @@ def _get_connection_settings(
preference.name.lower() == read_pf_mode
or preference.mode == read_pf_mode
):
- conn_settings["read_preference"] = preference
+ ReadPrefClass = preference.__class__
break
+
+ if "readpreferencetags" in uri_options:
+ conn_settings["read_preference"] = ReadPrefClass(
+ tag_sets=uri_options["readpreferencetags"]
+ )
+ else:
+ conn_settings["read_preference"] = ReadPrefClass()
+
if "authmechanismproperties" in uri_options:
conn_settings["authmechanismproperties"] = uri_options[
"authmechanismproperties"
]
+ if "uuidrepresentation" in uri_options:
+ REV_UUID_REPRESENTATIONS = {
+ v: k for k, v in _UUID_REPRESENTATIONS.items()
+ }
+ conn_settings["uuidrepresentation"] = REV_UUID_REPRESENTATIONS[
+ uri_options["uuidrepresentation"]
+ ]
else:
resolved_hosts.append(entity)
conn_settings["host"] = resolved_hosts
@@ -170,7 +195,7 @@ def _get_connection_settings(
keys = {
key.lower() for key in kwargs.keys()
} # pymongo options are case insensitive
- if "uuidrepresentation" not in keys:
+ if "uuidrepresentation" not in keys and "uuidrepresentation" not in conn_settings:
warnings.warn(
"No uuidRepresentation is specified! Falling back to "
"'pythonLegacy' which is the default for pymongo 3.x. "
@@ -320,6 +345,10 @@ def get_connection(alias=DEFAULT_CONNECTION_NAME, reconnect=False):
# alias and remove the database name and authentication info (we don't
# care about them at this point).
conn_settings = _clean_settings(raw_conn_settings)
+ if DriverInfo is not None:
+ conn_settings.setdefault(
+ "driver", DriverInfo("MongoEngine", mongoengine.__version__)
+ )
# Determine if we should use PyMongo's or mongomock's MongoClient.
if "mongo_client_class" in conn_settings:
diff --git a/mongoengine/context_managers.py b/mongoengine/context_managers.py
index eb9c996..f16753e 100644
--- a/mongoengine/context_managers.py
+++ b/mongoengine/context_managers.py
@@ -1,3 +1,4 @@
+import threading
from contextlib import contextmanager
from pymongo.read_concern import ReadConcern
@@ -15,9 +16,33 @@ __all__ = (
"query_counter",
"set_write_concern",
"set_read_write_concern",
+ "no_dereferencing_active_for_class",
)
+class MyThreadLocals(threading.local):
+ def __init__(self):
+ self.no_dereferencing_class = {}
+
+
+thread_locals = MyThreadLocals()
+
+
+def no_dereferencing_active_for_class(cls):
+ return cls in thread_locals.no_dereferencing_class
+
+
+def _register_no_dereferencing_for_class(cls):
+ thread_locals.no_dereferencing_class.setdefault(cls, 0)
+ thread_locals.no_dereferencing_class[cls] += 1
+
+
+def _unregister_no_dereferencing_for_class(cls):
+ thread_locals.no_dereferencing_class[cls] -= 1
+ if thread_locals.no_dereferencing_class[cls] == 0:
+ thread_locals.no_dereferencing_class.pop(cls)
+
+
class switch_db:
"""switch_db alias context manager.
@@ -107,7 +132,7 @@ class no_dereference:
Turns off all dereferencing in Documents for the duration of the context
manager::
- with no_dereference(Group) as Group:
+ with no_dereference(Group):
Group.objects.find()
"""
@@ -130,15 +155,17 @@ class no_dereference:
def __enter__(self):
"""Change the objects default and _auto_dereference values."""
+ _register_no_dereferencing_for_class(self.cls)
+
for field in self.deref_fields:
self.cls._fields[field]._auto_dereference = False
- return self.cls
def __exit__(self, t, value, traceback):
"""Reset the default and _auto_dereference values."""
+ _unregister_no_dereferencing_for_class(self.cls)
+
for field in self.deref_fields:
self.cls._fields[field]._auto_dereference = True
- return self.cls
class no_sub_classes:
diff --git a/mongoengine/fields.py b/mongoengine/fields.py
index df155e6..40469bf 100644
--- a/mongoengine/fields.py
+++ b/mongoengine/fields.py
@@ -47,6 +47,8 @@ from mongoengine.queryset.transform import STRING_OPERATORS
try:
from PIL import Image, ImageOps
+
+ LANCZOS = Image.LANCZOS if hasattr(Image, "LANCZOS") else Image.ANTIALIAS
except ImportError:
Image = None
ImageOps = None
@@ -759,7 +761,7 @@ class EmbeddedDocumentField(BaseField):
"Invalid embedded document instance provided to an "
"EmbeddedDocumentField"
)
- self.document_type.validate(value, clean)
+ value.validate(clean=clean)
def lookup_member(self, member_name):
doc_and_subclasses = [self.document_type] + self.document_type.__subclasses__()
@@ -1946,23 +1948,19 @@ class ImageGridFsProxy(GridFSProxy):
size = field.size
if size["force"]:
- img = ImageOps.fit(
- img, (size["width"], size["height"]), Image.ANTIALIAS
- )
+ img = ImageOps.fit(img, (size["width"], size["height"]), LANCZOS)
else:
- img.thumbnail((size["width"], size["height"]), Image.ANTIALIAS)
+ img.thumbnail((size["width"], size["height"]), LANCZOS)
thumbnail = None
if field.thumbnail_size:
size = field.thumbnail_size
if size["force"]:
- thumbnail = ImageOps.fit(
- img, (size["width"], size["height"]), Image.ANTIALIAS
- )
+ thumbnail = ImageOps.fit(img, (size["width"], size["height"]), LANCZOS)
else:
thumbnail = img.copy()
- thumbnail.thumbnail((size["width"], size["height"]), Image.ANTIALIAS)
+ thumbnail.thumbnail((size["width"], size["height"]), LANCZOS)
if thumbnail:
thumb_id = self._put_thumbnail(thumbnail, img_format, progressive)
diff --git a/mongoengine/mongodb_support.py b/mongoengine/mongodb_support.py
index 00b87f4..23c538d 100644
--- a/mongoengine/mongodb_support.py
+++ b/mongoengine/mongodb_support.py
@@ -9,6 +9,9 @@ MONGODB_34 = (3, 4)
MONGODB_36 = (3, 6)
MONGODB_42 = (4, 2)
MONGODB_44 = (4, 4)
+MONGODB_50 = (5, 0)
+MONGODB_60 = (6, 0)
+MONGODB_70 = (7, 0)
def get_mongodb_version():
diff --git a/mongoengine/pymongo_support.py b/mongoengine/pymongo_support.py
index 5429fc9..7aa5567 100644
--- a/mongoengine/pymongo_support.py
+++ b/mongoengine/pymongo_support.py
@@ -37,7 +37,12 @@ def count_documents(
# count_documents appeared in pymongo 3.7
if PYMONGO_VERSION >= (3, 7):
try:
- return collection.count_documents(filter=filter, **kwargs)
+ if not filter and set(kwargs) <= {"max_time_ms"}:
+ # when no filter is provided, estimated_document_count
+ # is a lot faster as it uses the collection metadata
+ return collection.estimated_document_count(**kwargs)
+ else:
+ return collection.count_documents(filter=filter, **kwargs)
except OperationFailure as err:
if PYMONGO_VERSION >= (4,):
raise
@@ -46,15 +51,10 @@ def count_documents(
# with .count but are no longer working with count_documents (i.e $geoNear, $near, and $nearSphere)
# fallback to deprecated Cursor.count
# Keeping this should be reevaluated the day pymongo removes .count entirely
- message = str(err)
- if not (
- "not allowed in this context" in message
- and (
- "$where" in message
- or "$geoNear" in message
- or "$near" in message
- or "$nearSphere" in message
- )
+ if (
+ "$geoNear, $near, and $nearSphere are not allowed in this context"
+ not in str(err)
+ and "$where is not allowed in this context" not in str(err)
):
raise
diff --git a/mongoengine/queryset/base.py b/mongoengine/queryset/base.py
index 75b2af6..3ce3cb3 100644
--- a/mongoengine/queryset/base.py
+++ b/mongoengine/queryset/base.py
@@ -17,6 +17,7 @@ from mongoengine.base import get_document
from mongoengine.common import _import_class
from mongoengine.connection import get_db
from mongoengine.context_managers import (
+ no_dereferencing_active_for_class,
set_read_write_concern,
set_write_concern,
switch_db,
@@ -51,9 +52,6 @@ class BaseQuerySet:
providing :class:`~mongoengine.Document` objects as the results.
"""
- __dereference = False
- _auto_dereference = True
-
def __init__(self, document, collection):
self._document = document
self._collection_obj = collection
@@ -73,6 +71,10 @@ class BaseQuerySet:
self._none = False
self._as_pymongo = False
self._search_text = None
+ self._search_text_score = None
+
+ self.__dereference = False
+ self.__auto_dereference = True
# If inheritance is allowed, only return instances and instances of
# subclasses of the class being used
@@ -228,7 +230,7 @@ class BaseQuerySet:
"""An alias of :meth:`~mongoengine.queryset.QuerySet.__call__`"""
return self.__call__(*q_objs, **query)
- def search_text(self, text, language=None):
+ def search_text(self, text, language=None, text_score=True):
"""
Start a text search, using text indexes.
Require: MongoDB server version 2.6+.
@@ -237,7 +239,9 @@ class BaseQuerySet:
for the search and the rules for the stemmer and tokenizer.
If not specified, the search uses the default language of the index.
For supported languages, see
- `Text Search Languages <http://docs.mongodb.org/manual/reference/text-search-languages/#text-search-languages>`.
+ `Text Search Languages <https://docs.mongodb.org/manual/reference/text-search-languages/#text-search-languages>`.
+ :param text_score: True to have it return the text_score (available through get_text_score()), False to disable that
+ Note that unless you order the results, leaving text_score=True may provide randomness in the returned documents
"""
queryset = self.clone()
if queryset._search_text:
@@ -251,11 +255,12 @@ class BaseQuerySet:
queryset._mongo_query = None
queryset._cursor_obj = None
queryset._search_text = text
+ queryset._search_text_score = text_score
return queryset
def get(self, *q_objs, **query):
- """Retrieve the the matching object raising
+ """Retrieve the matching object raising
:class:`~mongoengine.queryset.MultipleObjectsReturned` or
`DocumentName.MultipleObjectsReturned` exception if multiple results
and :class:`~mongoengine.queryset.DoesNotExist` or
@@ -530,6 +535,7 @@ class BaseQuerySet:
write_concern=None,
read_concern=None,
full_result=False,
+ array_filters=None,
**update,
):
"""Perform an atomic update on the fields matched by the query.
@@ -545,6 +551,7 @@ class BaseQuerySet:
:param read_concern: Override the read concern for the operation
:param full_result: Return the associated ``pymongo.UpdateResult`` rather than just the number
updated items
+ :param array_filters: A list of filters specifying which array elements an update should apply.
:param update: Django-style update keyword arguments
:returns the number of updated documents (unless ``full_result`` is True)
@@ -559,7 +566,9 @@ class BaseQuerySet:
queryset = self.clone()
query = queryset._query
- if "__raw__" in update and isinstance(update["__raw__"], list):
+ if "__raw__" in update and isinstance(
+ update["__raw__"], list
+ ): # Case of Update with Aggregation Pipeline
update = [
transform.update(queryset._document, **{"__raw__": u})
for u in update["__raw__"]
@@ -580,7 +589,9 @@ class BaseQuerySet:
update_func = collection.update_one
if multi:
update_func = collection.update_many
- result = update_func(query, update, upsert=upsert)
+ result = update_func(
+ query, update, upsert=upsert, array_filters=array_filters
+ )
if full_result:
return result
elif result.raw_result:
@@ -795,7 +806,7 @@ class BaseQuerySet:
return self._clone_into(self.__class__(self._document, self._collection_obj))
def _clone_into(self, new_qs):
- """Copy all of the relevant properties of this queryset to
+ """Copy all the relevant properties of this queryset to
a new queryset (which has to be an instance of
:class:`~mongoengine.queryset.base.BaseQuerySet`).
"""
@@ -825,8 +836,8 @@ class BaseQuerySet:
"_empty",
"_hint",
"_collation",
- "_auto_dereference",
"_search_text",
+ "_search_text_score",
"_max_time_ms",
"_comment",
"_batch_size",
@@ -836,6 +847,8 @@ class BaseQuerySet:
val = getattr(self, prop)
setattr(new_qs, prop, copy.copy(val))
+ new_qs.__auto_dereference = self._BaseQuerySet__auto_dereference
+
if self._cursor_obj:
new_qs._cursor_obj = self._cursor_obj.clone()
@@ -1111,7 +1124,7 @@ class BaseQuerySet:
)
return queryset
- def order_by(self, *keys):
+ def order_by(self, *keys, __raw__=None):
"""Order the :class:`~mongoengine.queryset.QuerySet` by the given keys.
The order may be specified by prepending each of the keys by a "+" or
@@ -1121,11 +1134,19 @@ class BaseQuerySet:
:param keys: fields to order the query results by; keys may be
prefixed with "+" or a "-" to determine the ordering direction.
+ :param __raw__: a raw pymongo "sort" argument (provided as a list of (key, direction))
+ see 'key_or_list' in `pymongo.cursor.Cursor.sort doc <https://pymongo.readthedocs.io/en/stable/api/pymongo/cursor.html#pymongo.cursor.Cursor.sort>`.
+ If both keys and __raw__ are provided, an exception is raised
"""
- queryset = self.clone()
+ if __raw__ and keys:
+ raise OperationError("Can not use both keys and __raw__ with order_by() ")
+ queryset = self.clone()
old_ordering = queryset._ordering
- new_ordering = queryset._get_order_by(keys)
+ if __raw__:
+ new_ordering = __raw__
+ else:
+ new_ordering = queryset._get_order_by(keys)
if queryset._cursor_obj:
# If a cursor object has already been created, apply the sort to it
@@ -1296,10 +1317,10 @@ class BaseQuerySet:
return [self._document._from_son(data) for data in son_data]
def aggregate(self, pipeline, *suppl_pipeline, **kwargs):
- """Perform a aggregate function based in your queryset params
+ """Perform an aggregate function based on your queryset params
- :param pipeline: list of aggregation commands,\
- see: http://docs.mongodb.org/manual/core/aggregation-pipeline/
+ :param pipeline: list of aggregation commands,
+ see: https://www.mongodb.com/docs/manual/core/aggregation-pipeline/
:param suppl_pipeline: unpacked list of pipeline (added to support deprecation of the old interface)
parameter will be removed shortly
:param kwargs: (optional) kwargs dictionary to be passed to pymongo's aggregate call
@@ -1657,7 +1678,8 @@ class BaseQuerySet:
if fields_name not in cursor_args:
cursor_args[fields_name] = {}
- cursor_args[fields_name]["_text_score"] = {"$meta": "textScore"}
+ if self._search_text_score:
+ cursor_args[fields_name]["_text_score"] = {"$meta": "textScore"}
return cursor_args
@@ -1741,10 +1763,15 @@ class BaseQuerySet:
self.__dereference = _import_class("DeReference")()
return self.__dereference
+ @property
+ def _auto_dereference(self):
+ should_deref = not no_dereferencing_active_for_class(self._document)
+ return should_deref and self.__auto_dereference
+
def no_dereference(self):
"""Turn off any dereferencing for the results of this queryset."""
queryset = self.clone()
- queryset._auto_dereference = False
+ queryset.__auto_dereference = False
return queryset
# Helper Functions
diff --git a/mongoengine/queryset/transform.py b/mongoengine/queryset/transform.py
index a95a846..195bc0b 100644
--- a/mongoengine/queryset/transform.py
+++ b/mongoengine/queryset/transform.py
@@ -62,6 +62,15 @@ MATCH_OPERATORS = (
)
+def handle_raw_query(value, mongo_query):
+ """Combine a raw query with an existing one"""
+ for op, v in value.items():
+ if op not in mongo_query:
+ mongo_query[op] = v
+ elif op in mongo_query and isinstance(mongo_query[op], dict):
+ mongo_query[op].update(v)
+
+
# TODO make this less complex
def query(_doc_cls=None, **kwargs):
"""Transform a query from Django-style format to Mongo format."""
@@ -69,7 +78,7 @@ def query(_doc_cls=None, **kwargs):
merge_query = defaultdict(list)
for key, value in sorted(kwargs.items()):
if key == "__raw__":
- mongo_query.update(value)
+ handle_raw_query(value, mongo_query)
continue
parts = key.rsplit("__")
@@ -234,7 +243,7 @@ def update(_doc_cls=None, **update):
for key, value in update.items():
if key == "__raw__":
- mongo_update.update(value)
+ handle_raw_query(value, mongo_update)
continue
parts = key.split("__")
diff --git a/setup.py b/setup.py
index b1f1752..aebabc4 100644
--- a/setup.py
+++ b/setup.py
@@ -98,8 +98,6 @@ CLASSIFIERS = [
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.6",
- "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",