Skip to content

Commit 046d789

Browse files
authored
PYTHON-2957 Support 'let' option for multiple CRUD commands (#804)
1 parent e3d1d6f commit 046d789

14 files changed

+1721
-130
lines changed

doc/changelog.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
Changelog
22
=========
33

4+
Changes in Version 4.1
5+
----------------------
6+
7+
- :meth:`pymongo.collection.Collection.update_one`,
8+
:meth:`pymongo.collection.Collection.update_many`,
9+
:meth:`pymongo.collection.Collection.delete_one`,
10+
:meth:`pymongo.collection.Collection.delete_many`,
11+
:meth:`pymongo.collection.Collection.aggregate`,
12+
:meth:`pymongo.collection.Collection.find_one_and_delete`,
13+
:meth:`pymongo.collection.Collection.find_one_and_replace`,
14+
:meth:`pymongo.collection.Collection.find_one_and_update`,
15+
and :meth:`pymongo.collection.Collection.find` all support a new keyword
16+
argument ``let`` which is a map of parameter names and values. Parameters
17+
can then be accessed as variables in an aggregate expression context.
18+
19+
420
Changes in Version 4.0
521
----------------------
622

pymongo/aggregation.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class _AggregationCommand(object):
3030
:meth:`pymongo.database.Database.aggregate` instead.
3131
"""
3232
def __init__(self, target, cursor_class, pipeline, options,
33-
explicit_session, user_fields=None, result_processor=None):
33+
explicit_session, let=None, user_fields=None, result_processor=None):
3434
if "explain" in options:
3535
raise ConfigurationError("The explain option is not supported. "
3636
"Use Database.command instead.")
@@ -44,6 +44,9 @@ def __init__(self, target, cursor_class, pipeline, options,
4444
self._performs_write = True
4545

4646
common.validate_is_mapping('options', options)
47+
if let:
48+
common.validate_is_mapping("let", let)
49+
options["let"] = let
4750
self._options = options
4851

4952
# This is the batchSize that will be used for setting the initial

pymongo/collection.py

Lines changed: 83 additions & 24 deletions
Large diffs are not rendered by default.

pymongo/cursor.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
from bson.code import Code
2525
from bson.son import SON
2626
from pymongo import helpers
27-
from pymongo.common import validate_boolean, validate_is_mapping
27+
from pymongo.common import (validate_boolean, validate_is_mapping,
28+
validate_is_document_type)
2829
from pymongo.collation import validate_collation_or_none
2930
from pymongo.errors import (ConnectionFailure,
3031
InvalidOperation,
@@ -140,7 +141,7 @@ def __init__(self, collection, filter=None, projection=None, skip=0,
140141
collation=None, hint=None, max_scan=None, max_time_ms=None,
141142
max=None, min=None, return_key=None, show_record_id=None,
142143
snapshot=None, comment=None, session=None,
143-
allow_disk_use=None):
144+
allow_disk_use=None, let=None):
144145
"""Create a new cursor.
145146
146147
Should not be called directly by application developers - see
@@ -197,6 +198,10 @@ def __init__(self, collection, filter=None, projection=None, skip=0,
197198
if projection is not None:
198199
projection = helpers._fields_list_to_dict(projection, "projection")
199200

201+
if let:
202+
validate_is_document_type("let", let)
203+
204+
self.__let = let
200205
self.__spec = spec
201206
self.__projection = projection
202207
self.__skip = skip
@@ -370,6 +375,8 @@ def __query_spec(self):
370375
operators["$explain"] = True
371376
if self.__hint:
372377
operators["$hint"] = self.__hint
378+
if self.__let:
379+
operators["let"] = self.__let
373380
if self.__comment:
374381
operators["$comment"] = self.__comment
375382
if self.__max_scan:

test/crud/unified/aggregate-let.json

Lines changed: 0 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -56,109 +56,6 @@
5656
"minServerVersion": "5.0"
5757
}
5858
],
59-
"operations": [
60-
{
61-
"name": "aggregate",
62-
"object": "collection0",
63-
"arguments": {
64-
"pipeline": [
65-
{
66-
"$match": {
67-
"$expr": {
68-
"$eq": [
69-
"$_id",
70-
"$$id"
71-
]
72-
}
73-
}
74-
},
75-
{
76-
"$project": {
77-
"_id": 0,
78-
"x": "$$x",
79-
"y": "$$y",
80-
"rand": "$$rand"
81-
}
82-
}
83-
],
84-
"let": {
85-
"id": 1,
86-
"x": "foo",
87-
"y": {
88-
"$literal": "bar"
89-
},
90-
"rand": {
91-
"$rand": {}
92-
}
93-
}
94-
},
95-
"expectResult": [
96-
{
97-
"x": "foo",
98-
"y": "bar",
99-
"rand": {
100-
"$$type": "double"
101-
}
102-
}
103-
]
104-
}
105-
],
106-
"expectEvents": [
107-
{
108-
"client": "client0",
109-
"events": [
110-
{
111-
"commandStartedEvent": {
112-
"command": {
113-
"aggregate": "coll0",
114-
"pipeline": [
115-
{
116-
"$match": {
117-
"$expr": {
118-
"$eq": [
119-
"$_id",
120-
"$$id"
121-
]
122-
}
123-
}
124-
},
125-
{
126-
"$project": {
127-
"_id": 0,
128-
"x": "$$x",
129-
"y": "$$y",
130-
"rand": "$$rand"
131-
}
132-
}
133-
],
134-
"let": {
135-
"id": 1,
136-
"x": "foo",
137-
"y": {
138-
"$literal": "bar"
139-
},
140-
"rand": {
141-
"$rand": {}
142-
}
143-
}
144-
}
145-
}
146-
}
147-
]
148-
}
149-
]
150-
},
151-
{
152-
"description": "Aggregate with let option and dollar-prefixed $literal value",
153-
"runOnRequirements": [
154-
{
155-
"minServerVersion": "5.0",
156-
"topologies": [
157-
"single",
158-
"replicaset"
159-
]
160-
}
161-
],
16259
"operations": [
16360
{
16461
"name": "aggregate",

test/crud/unified/deleteMany-let.json

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
{
2+
"description": "deleteMany-let",
3+
"schemaVersion": "1.0",
4+
"createEntities": [
5+
{
6+
"client": {
7+
"id": "client0",
8+
"observeEvents": [
9+
"commandStartedEvent"
10+
]
11+
}
12+
},
13+
{
14+
"database": {
15+
"id": "database0",
16+
"client": "client0",
17+
"databaseName": "crud-tests"
18+
}
19+
},
20+
{
21+
"collection": {
22+
"id": "collection0",
23+
"database": "database0",
24+
"collectionName": "coll0"
25+
}
26+
}
27+
],
28+
"initialData": [
29+
{
30+
"collectionName": "coll0",
31+
"databaseName": "crud-tests",
32+
"documents": [
33+
{
34+
"_id": 1
35+
},
36+
{
37+
"_id": 2,
38+
"name": "name"
39+
},
40+
{
41+
"_id": 3,
42+
"name": "name"
43+
}
44+
]
45+
}
46+
],
47+
"tests": [
48+
{
49+
"description": "deleteMany with let option",
50+
"runOnRequirements": [
51+
{
52+
"minServerVersion": "5.0"
53+
}
54+
],
55+
"operations": [
56+
{
57+
"name": "deleteMany",
58+
"object": "collection0",
59+
"arguments": {
60+
"filter": {
61+
"$expr": {
62+
"$eq": [
63+
"$name",
64+
"$$name"
65+
]
66+
}
67+
},
68+
"let": {
69+
"name": "name"
70+
}
71+
},
72+
"expectResult": {
73+
"deletedCount": 2
74+
}
75+
}
76+
],
77+
"expectEvents": [
78+
{
79+
"client": "client0",
80+
"events": [
81+
{
82+
"commandStartedEvent": {
83+
"command": {
84+
"delete": "coll0",
85+
"deletes": [
86+
{
87+
"q": {
88+
"$expr": {
89+
"$eq": [
90+
"$name",
91+
"$$name"
92+
]
93+
}
94+
},
95+
"limit": 0
96+
}
97+
],
98+
"let": {
99+
"name": "name"
100+
}
101+
}
102+
}
103+
}
104+
]
105+
}
106+
],
107+
"outcome": [
108+
{
109+
"collectionName": "coll0",
110+
"databaseName": "crud-tests",
111+
"documents": [
112+
{
113+
"_id": 1
114+
}
115+
]
116+
}
117+
]
118+
},
119+
{
120+
"description": "deleteMany with let option unsupported (server-side error)",
121+
"runOnRequirements": [
122+
{
123+
"minServerVersion": "3.6.0",
124+
"maxServerVersion": "4.4.99"
125+
}
126+
],
127+
"operations": [
128+
{
129+
"name": "deleteMany",
130+
"object": "collection0",
131+
"arguments": {
132+
"filter": {
133+
"$expr": {
134+
"$eq": [
135+
"$name",
136+
"$$name"
137+
]
138+
}
139+
},
140+
"let": {
141+
"name": "name"
142+
}
143+
},
144+
"expectError": {
145+
"errorContains": "'delete.let' is an unknown field",
146+
"isClientError": false
147+
}
148+
}
149+
],
150+
"expectEvents": [
151+
{
152+
"client": "client0",
153+
"events": [
154+
{
155+
"commandStartedEvent": {
156+
"command": {
157+
"delete": "coll0",
158+
"deletes": [
159+
{
160+
"q": {
161+
"$expr": {
162+
"$eq": [
163+
"$name",
164+
"$$name"
165+
]
166+
}
167+
},
168+
"limit": 0
169+
}
170+
],
171+
"let": {
172+
"name": "name"
173+
}
174+
}
175+
}
176+
}
177+
]
178+
}
179+
],
180+
"outcome": [
181+
{
182+
"collectionName": "coll0",
183+
"databaseName": "crud-tests",
184+
"documents": [
185+
{
186+
"_id": 1
187+
},
188+
{
189+
"_id": 2,
190+
"name": "name"
191+
},
192+
{
193+
"_id": 3,
194+
"name": "name"
195+
}
196+
]
197+
}
198+
]
199+
}
200+
]
201+
}

0 commit comments

Comments
 (0)