Skip to content

Commit 94b5b69

Browse files
authored
Merge pull request #2854 from bagerard/Winedays-feature/query_set_more_support_for_collation_hint_comment
set more support for collation hint comment (Winedays feature/query )
2 parents c334c77 + 1e06d52 commit 94b5b69

File tree

5 files changed

+164
-12
lines changed

5 files changed

+164
-12
lines changed

docs/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Development
1515
- Further to the deprecation warning, remove ability to use an unpacked list to `Queryset.aggregate(*pipeline)`, a plain list must be provided instead `Queryset.aggregate(pipeline)`, as it's closer to pymongo interface
1616
- Further to the deprecation warning, remove `full_response` from `QuerySet.modify` as it wasn't supported with Pymongo 3+
1717
- Fixed stacklevel of many warnings (to point places emitting the warning more accurately)
18+
- Add support for collation/hint/comment to delete/update and aggregate #2842
1819

1920
Changes in 0.29.0
2021
=================

tests/document/test_instance.py

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@
4242
PickleSignalsTest,
4343
PickleTest,
4444
)
45-
from tests.utils import MongoDBTestCase, get_as_pymongo
45+
from tests.utils import (
46+
MongoDBTestCase,
47+
db_ops_tracker,
48+
get_as_pymongo,
49+
requires_mongodb_gte_44,
50+
)
4651

4752
TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), "../fields/mongoengine.png")
4853

@@ -1871,6 +1876,53 @@ class User(self.Person):
18711876
person = self.Person.objects.get()
18721877
assert not person.comments_dict["first_post"].published
18731878

1879+
@requires_mongodb_gte_44
1880+
def test_update_propagates_hint_collation_and_comment(self):
1881+
"""Make sure adding a hint/comment/collation to the query gets added to the query"""
1882+
mongo_ver = get_mongodb_version()
1883+
1884+
base = {"locale": "en", "strength": 2}
1885+
index_name = "name_1"
1886+
1887+
class AggPerson(Document):
1888+
name = StringField()
1889+
meta = {
1890+
"indexes": [{"fields": ["name"], "name": index_name, "collation": base}]
1891+
}
1892+
1893+
AggPerson.drop_collection()
1894+
_ = AggPerson.objects.first()
1895+
1896+
comment = "test_comment"
1897+
1898+
if PYMONGO_VERSION >= (4, 1):
1899+
with db_ops_tracker() as q:
1900+
_ = AggPerson.objects.comment(comment).update_one(name="something")
1901+
query_op = q.db.system.profile.find(
1902+
{"ns": "mongoenginetest.agg_person"}
1903+
)[0]
1904+
CMD_QUERY_KEY = "command" if mongo_ver >= MONGODB_36 else "query"
1905+
assert "hint" not in query_op[CMD_QUERY_KEY]
1906+
assert query_op[CMD_QUERY_KEY]["comment"] == comment
1907+
assert "collation" not in query_op[CMD_QUERY_KEY]
1908+
1909+
with db_ops_tracker() as q:
1910+
_ = AggPerson.objects.hint(index_name).update_one(name="something")
1911+
query_op = q.db.system.profile.find({"ns": "mongoenginetest.agg_person"})[0]
1912+
CMD_QUERY_KEY = "command" if mongo_ver >= MONGODB_36 else "query"
1913+
1914+
assert query_op[CMD_QUERY_KEY]["hint"] == {"$hint": index_name}
1915+
assert "comment" not in query_op[CMD_QUERY_KEY]
1916+
assert "collation" not in query_op[CMD_QUERY_KEY]
1917+
1918+
with db_ops_tracker() as q:
1919+
_ = AggPerson.objects.collation(base).update_one(name="something")
1920+
query_op = q.db.system.profile.find({"ns": "mongoenginetest.agg_person"})[0]
1921+
CMD_QUERY_KEY = "command" if mongo_ver >= MONGODB_36 else "query"
1922+
assert "hint" not in query_op[CMD_QUERY_KEY]
1923+
assert "comment" not in query_op[CMD_QUERY_KEY]
1924+
assert query_op[CMD_QUERY_KEY]["collation"] == base
1925+
18741926
def test_delete(self):
18751927
"""Ensure that document may be deleted using the delete method."""
18761928
person = self.Person(name="Test User", age=30)
@@ -1879,6 +1931,53 @@ def test_delete(self):
18791931
person.delete()
18801932
assert self.Person.objects.count() == 0
18811933

1934+
@requires_mongodb_gte_44
1935+
def test_delete_propagates_hint_collation_and_comment(self):
1936+
"""Make sure adding a hint/comment/collation to the query gets added to the query"""
1937+
mongo_ver = get_mongodb_version()
1938+
1939+
base = {"locale": "en", "strength": 2}
1940+
index_name = "name_1"
1941+
1942+
class AggPerson(Document):
1943+
name = StringField()
1944+
meta = {
1945+
"indexes": [{"fields": ["name"], "name": index_name, "collation": base}]
1946+
}
1947+
1948+
AggPerson.drop_collection()
1949+
_ = AggPerson.objects.first()
1950+
1951+
comment = "test_comment"
1952+
1953+
if PYMONGO_VERSION >= (4, 1):
1954+
with db_ops_tracker() as q:
1955+
_ = AggPerson.objects().comment(comment).delete()
1956+
query_op = q.db.system.profile.find(
1957+
{"ns": "mongoenginetest.agg_person"}
1958+
)[0]
1959+
CMD_QUERY_KEY = "command" if mongo_ver >= MONGODB_36 else "query"
1960+
assert "hint" not in query_op[CMD_QUERY_KEY]
1961+
assert query_op[CMD_QUERY_KEY]["comment"] == comment
1962+
assert "collation" not in query_op[CMD_QUERY_KEY]
1963+
1964+
with db_ops_tracker() as q:
1965+
_ = AggPerson.objects.hint(index_name).delete()
1966+
query_op = q.db.system.profile.find({"ns": "mongoenginetest.agg_person"})[0]
1967+
CMD_QUERY_KEY = "command" if mongo_ver >= MONGODB_36 else "query"
1968+
1969+
assert query_op[CMD_QUERY_KEY]["hint"] == {"$hint": index_name}
1970+
assert "comment" not in query_op[CMD_QUERY_KEY]
1971+
assert "collation" not in query_op[CMD_QUERY_KEY]
1972+
1973+
with db_ops_tracker() as q:
1974+
_ = AggPerson.objects.collation(base).delete()
1975+
query_op = q.db.system.profile.find({"ns": "mongoenginetest.agg_person"})[0]
1976+
CMD_QUERY_KEY = "command" if mongo_ver >= MONGODB_36 else "query"
1977+
assert "hint" not in query_op[CMD_QUERY_KEY]
1978+
assert "comment" not in query_op[CMD_QUERY_KEY]
1979+
assert query_op[CMD_QUERY_KEY]["collation"] == base
1980+
18821981
def test_save_custom_id(self):
18831982
"""Ensure that a document may be saved with a custom _id."""
18841983

tests/queryset/test_queryset.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,13 @@
2727
)
2828
from mongoengine.queryset.base import BaseQuerySet
2929
from tests.utils import (
30+
db_ops_tracker,
3031
requires_mongodb_gte_42,
3132
requires_mongodb_gte_44,
3233
requires_mongodb_lt_42,
3334
)
3435

3536

36-
class db_ops_tracker(query_counter):
37-
def get_ops(self):
38-
ignore_query = dict(self._ignored_query)
39-
ignore_query["command.count"] = {
40-
"$ne": "system.profile"
41-
} # Ignore the query issued by query_counter
42-
return list(self.db.system.profile.find(ignore_query))
43-
44-
4537
def get_key_compat(mongo_ver):
4638
ORDER_BY_KEY = "sort"
4739
CMD_QUERY_KEY = "command" if mongo_ver >= MONGODB_36 else "query"

tests/queryset/test_queryset_aggregation.py

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
import pytest
44
from pymongo.read_preferences import ReadPreference
55

6-
from mongoengine import *
7-
from tests.utils import MongoDBTestCase
6+
from mongoengine import Document, IntField, PointField, StringField
7+
from mongoengine.mongodb_support import (
8+
MONGODB_36,
9+
get_mongodb_version,
10+
)
11+
from tests.utils import MongoDBTestCase, db_ops_tracker
812

913

1014
class TestQuerysetAggregate(MongoDBTestCase):
@@ -87,6 +91,49 @@ class Person(Document):
8791
{"_id": p3.pk, "name": "SANDRA MARA"},
8892
]
8993

94+
def test_aggregation_propagates_hint_collation_and_comment(self):
95+
"""Make sure adding a hint/comment/collation to the query gets added to the query"""
96+
mongo_ver = get_mongodb_version()
97+
98+
base = {"locale": "en", "strength": 2}
99+
index_name = "name_1"
100+
101+
class AggPerson(Document):
102+
name = StringField()
103+
meta = {
104+
"indexes": [{"fields": ["name"], "name": index_name, "collation": base}]
105+
}
106+
107+
AggPerson.drop_collection()
108+
_ = AggPerson.objects.first()
109+
110+
pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}]
111+
comment = "test_comment"
112+
113+
with db_ops_tracker() as q:
114+
_ = list(AggPerson.objects.comment(comment).aggregate(pipeline))
115+
query_op = q.db.system.profile.find({"ns": "mongoenginetest.agg_person"})[0]
116+
CMD_QUERY_KEY = "command" if mongo_ver >= MONGODB_36 else "query"
117+
assert "hint" not in query_op[CMD_QUERY_KEY]
118+
assert query_op[CMD_QUERY_KEY]["comment"] == comment
119+
assert "collation" not in query_op[CMD_QUERY_KEY]
120+
121+
with db_ops_tracker() as q:
122+
_ = list(AggPerson.objects.hint(index_name).aggregate(pipeline))
123+
query_op = q.db.system.profile.find({"ns": "mongoenginetest.agg_person"})[0]
124+
CMD_QUERY_KEY = "command" if mongo_ver >= MONGODB_36 else "query"
125+
assert query_op[CMD_QUERY_KEY]["hint"] == "name_1"
126+
assert "comment" not in query_op[CMD_QUERY_KEY]
127+
assert "collation" not in query_op[CMD_QUERY_KEY]
128+
129+
with db_ops_tracker() as q:
130+
_ = list(AggPerson.objects.collation(base).aggregate(pipeline))
131+
query_op = q.db.system.profile.find({"ns": "mongoenginetest.agg_person"})[0]
132+
CMD_QUERY_KEY = "command" if mongo_ver >= MONGODB_36 else "query"
133+
assert "hint" not in query_op[CMD_QUERY_KEY]
134+
assert "comment" not in query_op[CMD_QUERY_KEY]
135+
assert query_op[CMD_QUERY_KEY]["collation"] == base
136+
90137
def test_queryset_aggregation_with_limit(self):
91138
class Person(Document):
92139
name = StringField()

tests/utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22
import operator
33
import unittest
44

5+
import pymongo
56
import pytest
67

78
from mongoengine import connect
89
from mongoengine.connection import disconnect_all, get_db
10+
from mongoengine.context_managers import query_counter
911
from mongoengine.mongodb_support import get_mongodb_version
1012

13+
PYMONGO_VERSION = tuple(pymongo.version_tuple[:2])
14+
1115
MONGO_TEST_DB = "mongoenginetest" # standard name for the test database
1216

1317

@@ -92,3 +96,12 @@ def _inner(*args, **kwargs):
9296
pytest.skip(f"Needs MongoDB {oper.__name__} v{pretty_version}")
9397

9498
return _inner
99+
100+
101+
class db_ops_tracker(query_counter):
102+
def get_ops(self):
103+
ignore_query = dict(self._ignored_query)
104+
ignore_query["command.count"] = {
105+
"$ne": "system.profile"
106+
} # Ignore the query issued by query_counter
107+
return list(self.db.system.profile.find(ignore_query))

0 commit comments

Comments
 (0)