Skip to content

PYTHON-2957 Support 'let' option for multiple CRUD commands #804

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Nov 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
Changelog
=========

Changes in Version 4.1
----------------------

- :meth:`pymongo.collection.Collection.update_one`,
:meth:`pymongo.collection.Collection.update_many`,
:meth:`pymongo.collection.Collection.delete_one`,
:meth:`pymongo.collection.Collection.delete_many`,
:meth:`pymongo.collection.Collection.aggregate`,
:meth:`pymongo.collection.Collection.find_one_and_delete`,
:meth:`pymongo.collection.Collection.find_one_and_replace`,
:meth:`pymongo.collection.Collection.find_one_and_update`,
and :meth:`pymongo.collection.Collection.find` all support a new keyword
argument ``let`` which is a map of parameter names and values. Parameters
can then be accessed as variables in an aggregate expression context.


Changes in Version 4.0
----------------------

Expand Down
5 changes: 4 additions & 1 deletion pymongo/aggregation.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class _AggregationCommand(object):
:meth:`pymongo.database.Database.aggregate` instead.
"""
def __init__(self, target, cursor_class, pipeline, options,
explicit_session, user_fields=None, result_processor=None):
explicit_session, let=None, user_fields=None, result_processor=None):
if "explain" in options:
raise ConfigurationError("The explain option is not supported. "
"Use Database.command instead.")
Expand All @@ -44,6 +44,9 @@ def __init__(self, target, cursor_class, pipeline, options,
self._performs_write = True

common.validate_is_mapping('options', options)
if let:
common.validate_is_mapping("let", let)
options["let"] = let
self._options = options

# This is the batchSize that will be used for setting the initial
Expand Down
107 changes: 83 additions & 24 deletions pymongo/collection.py

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions pymongo/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
from bson.code import Code
from bson.son import SON
from pymongo import helpers
from pymongo.common import validate_boolean, validate_is_mapping
from pymongo.common import (validate_boolean, validate_is_mapping,
validate_is_document_type)
from pymongo.collation import validate_collation_or_none
from pymongo.errors import (ConnectionFailure,
InvalidOperation,
Expand Down Expand Up @@ -140,7 +141,7 @@ def __init__(self, collection, filter=None, projection=None, skip=0,
collation=None, hint=None, max_scan=None, max_time_ms=None,
max=None, min=None, return_key=None, show_record_id=None,
snapshot=None, comment=None, session=None,
allow_disk_use=None):
allow_disk_use=None, let=None):
"""Create a new cursor.

Should not be called directly by application developers - see
Expand Down Expand Up @@ -197,6 +198,10 @@ def __init__(self, collection, filter=None, projection=None, skip=0,
if projection is not None:
projection = helpers._fields_list_to_dict(projection, "projection")

if let:
validate_is_document_type("let", let)

self.__let = let
self.__spec = spec
self.__projection = projection
self.__skip = skip
Expand Down Expand Up @@ -370,6 +375,8 @@ def __query_spec(self):
operators["$explain"] = True
if self.__hint:
operators["$hint"] = self.__hint
if self.__let:
operators["let"] = self.__let
if self.__comment:
operators["$comment"] = self.__comment
if self.__max_scan:
Expand Down
103 changes: 0 additions & 103 deletions test/crud/unified/aggregate-let.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,109 +56,6 @@
"minServerVersion": "5.0"
}
],
"operations": [
{
"name": "aggregate",
"object": "collection0",
"arguments": {
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$_id",
"$$id"
]
}
}
},
{
"$project": {
"_id": 0,
"x": "$$x",
"y": "$$y",
"rand": "$$rand"
}
}
],
"let": {
"id": 1,
"x": "foo",
"y": {
"$literal": "bar"
},
"rand": {
"$rand": {}
}
}
},
"expectResult": [
{
"x": "foo",
"y": "bar",
"rand": {
"$$type": "double"
}
}
]
}
],
"expectEvents": [
{
"client": "client0",
"events": [
{
"commandStartedEvent": {
"command": {
"aggregate": "coll0",
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$_id",
"$$id"
]
}
}
},
{
"$project": {
"_id": 0,
"x": "$$x",
"y": "$$y",
"rand": "$$rand"
}
}
],
"let": {
"id": 1,
"x": "foo",
"y": {
"$literal": "bar"
},
"rand": {
"$rand": {}
}
}
}
}
}
]
}
]
},
{
"description": "Aggregate with let option and dollar-prefixed $literal value",
"runOnRequirements": [
{
"minServerVersion": "5.0",
"topologies": [
"single",
"replicaset"
]
}
],
"operations": [
{
"name": "aggregate",
Expand Down
201 changes: 201 additions & 0 deletions test/crud/unified/deleteMany-let.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
{
"description": "deleteMany-let",
"schemaVersion": "1.0",
"createEntities": [
{
"client": {
"id": "client0",
"observeEvents": [
"commandStartedEvent"
]
}
},
{
"database": {
"id": "database0",
"client": "client0",
"databaseName": "crud-tests"
}
},
{
"collection": {
"id": "collection0",
"database": "database0",
"collectionName": "coll0"
}
}
],
"initialData": [
{
"collectionName": "coll0",
"databaseName": "crud-tests",
"documents": [
{
"_id": 1
},
{
"_id": 2,
"name": "name"
},
{
"_id": 3,
"name": "name"
}
]
}
],
"tests": [
{
"description": "deleteMany with let option",
"runOnRequirements": [
{
"minServerVersion": "5.0"
}
],
"operations": [
{
"name": "deleteMany",
"object": "collection0",
"arguments": {
"filter": {
"$expr": {
"$eq": [
"$name",
"$$name"
]
}
},
"let": {
"name": "name"
}
},
"expectResult": {
"deletedCount": 2
}
}
],
"expectEvents": [
{
"client": "client0",
"events": [
{
"commandStartedEvent": {
"command": {
"delete": "coll0",
"deletes": [
{
"q": {
"$expr": {
"$eq": [
"$name",
"$$name"
]
}
},
"limit": 0
}
],
"let": {
"name": "name"
}
}
}
}
]
}
],
"outcome": [
{
"collectionName": "coll0",
"databaseName": "crud-tests",
"documents": [
{
"_id": 1
}
]
}
]
},
{
"description": "deleteMany with let option unsupported (server-side error)",
"runOnRequirements": [
{
"minServerVersion": "3.6.0",
"maxServerVersion": "4.4.99"
}
],
"operations": [
{
"name": "deleteMany",
"object": "collection0",
"arguments": {
"filter": {
"$expr": {
"$eq": [
"$name",
"$$name"
]
}
},
"let": {
"name": "name"
}
},
"expectError": {
"errorContains": "'delete.let' is an unknown field",
"isClientError": false
}
}
],
"expectEvents": [
{
"client": "client0",
"events": [
{
"commandStartedEvent": {
"command": {
"delete": "coll0",
"deletes": [
{
"q": {
"$expr": {
"$eq": [
"$name",
"$$name"
]
}
},
"limit": 0
}
],
"let": {
"name": "name"
}
}
}
}
]
}
],
"outcome": [
{
"collectionName": "coll0",
"databaseName": "crud-tests",
"documents": [
{
"_id": 1
},
{
"_id": 2,
"name": "name"
},
{
"_id": 3,
"name": "name"
}
]
}
]
}
]
}
Loading