Skip to content

Commit 2942d50

Browse files
Luke Lovettbehackett
authored andcommitted
PYTHON-989 - Support write concern in find_and_modify, find_one_and_XXX.
1 parent dde4a65 commit 2942d50

File tree

2 files changed

+110
-3
lines changed

2 files changed

+110
-3
lines changed

pymongo/collection.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1797,6 +1797,10 @@ def __find_and_modify(self, filter, projection, sort, upsert=None,
17971797
common.validate_boolean("upsert", upsert)
17981798
cmd["upsert"] = upsert
17991799
with self._socket_for_writes() as sock_info:
1800+
if sock_info.max_wire_version >= 4 and 'writeConcern' not in cmd:
1801+
wc_doc = self.write_concern.document
1802+
if wc_doc:
1803+
cmd['writeConcern'] = wc_doc
18001804
out = self._command(sock_info, cmd,
18011805
read_preference=ReadPreference.PRIMARY,
18021806
allowable_errors=[_NO_OBJ_ERROR])
@@ -1844,7 +1848,17 @@ def find_one_and_delete(self, filter,
18441848
as keyword arguments (for example maxTimeMS can be used with
18451849
recent server versions).
18461850
1851+
.. versionchanged:: 3.2
1852+
Respects write concern.
1853+
1854+
.. warning:: Starting in PyMongo 3.2, this command uses the
1855+
:class:`~pymongo.write_concern.WriteConcern` of this
1856+
:class:`~pymongo.collection.Collection` when connected to MongoDB >=
1857+
3.2. Note that using an elevated write concern with this command may
1858+
be slower compared to using the default write concern.
1859+
18471860
.. versionadded:: 3.0
1861+
18481862
"""
18491863
kwargs['remove'] = True
18501864
return self.__find_and_modify(filter, projection, sort, **kwargs)
@@ -1897,6 +1911,15 @@ def find_one_and_replace(self, filter, replacement,
18971911
as keyword arguments (for example maxTimeMS can be used with
18981912
recent server versions).
18991913
1914+
.. versionchanged:: 3.2
1915+
Respects write concern.
1916+
1917+
.. warning:: Starting in PyMongo 3.2, this command uses the
1918+
:class:`~pymongo.write_concern.WriteConcern` of this
1919+
:class:`~pymongo.collection.Collection` when connected to MongoDB >=
1920+
3.2. Note that using an elevated write concern with this command may
1921+
be slower compared to using the default write concern.
1922+
19001923
.. versionadded:: 3.0
19011924
"""
19021925
common.validate_ok_for_replace(replacement)
@@ -1983,6 +2006,15 @@ def find_one_and_update(self, filter, update,
19832006
as keyword arguments (for example maxTimeMS can be used with
19842007
recent server versions).
19852008
2009+
.. versionchanged:: 3.2
2010+
Respects write concern.
2011+
2012+
.. warning:: Starting in PyMongo 3.2, this command uses the
2013+
:class:`~pymongo.write_concern.WriteConcern` of this
2014+
:class:`~pymongo.collection.Collection` when connected to MongoDB >=
2015+
3.2. Note that using an elevated write concern with this command may
2016+
be slower compared to using the default write concern.
2017+
19862018
.. versionadded:: 3.0
19872019
"""
19882020
common.validate_ok_for_update(update)
@@ -2130,14 +2162,17 @@ def find_and_modify(self, query={}, update=None,
21302162
"pairs, a dict of len 1, or an instance of "
21312163
"SON or OrderedDict")
21322164

2133-
21342165
fields = kwargs.pop("fields", None)
21352166
if fields is not None:
21362167
kwargs["fields"] = helpers._fields_list_to_dict(fields, "fields")
21372168

21382169
cmd = SON([("findAndModify", self.__name)])
21392170
cmd.update(kwargs)
21402171
with self._socket_for_writes() as sock_info:
2172+
if sock_info.max_wire_version >= 4 and 'writeConcern' not in cmd:
2173+
wc_doc = self.write_concern.document
2174+
if wc_doc:
2175+
cmd['writeConcern'] = wc_doc
21412176
out = self._command(sock_info, cmd,
21422177
read_preference=ReadPreference.PRIMARY,
21432178
allowable_errors=[_NO_OBJ_ERROR])

test/test_collection.py

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from bson.son import SON
3232
from pymongo import (ASCENDING, DESCENDING, GEO2D,
3333
GEOHAYSTACK, GEOSPHERE, HASHED, TEXT)
34-
from pymongo import MongoClient
34+
from pymongo import MongoClient, monitoring
3535
from pymongo.collection import Collection, ReturnDocument
3636
from pymongo.command_cursor import CommandCursor
3737
from pymongo.cursor import CursorType
@@ -51,7 +51,8 @@
5151
from pymongo.write_concern import WriteConcern
5252
from test.test_client import IntegrationTest
5353
from test.utils import (is_mongos, enable_text_search, get_pool,
54-
rs_or_single_client, wait_until)
54+
rs_or_single_client, single_client,
55+
wait_until, EventListener)
5556
from test import client_context, host, port, unittest
5657

5758

@@ -1668,6 +1669,77 @@ def test_find_one_and(self):
16681669
{'$inc': {'i': 1}},
16691670
sort=sort)['j'])
16701671

1672+
def test_find_one_and_write_concern(self):
1673+
listener = EventListener()
1674+
saved_listeners = monitoring._LISTENERS
1675+
monitoring._LISTENERS = monitoring._Listeners([])
1676+
db = single_client(event_listeners=[listener])[self.db.name]
1677+
# non-default WriteConcern.
1678+
c_w0 = db.get_collection(
1679+
'test', write_concern=WriteConcern(w=0))
1680+
# default WriteConcern.
1681+
c_default = db.get_collection('test', write_concern=WriteConcern())
1682+
results = listener.results
1683+
try:
1684+
if client_context.version.at_least(3, 1, 9, -1):
1685+
c_w0.find_and_modify(
1686+
{'_id': 1}, {'$set': {'foo': 'bar'}})
1687+
self.assertEqual(
1688+
{'w': 0}, results['started'][0].command['writeConcern'])
1689+
results.clear()
1690+
1691+
c_w0.find_one_and_update(
1692+
{'_id': 1}, {'$set': {'foo': 'bar'}})
1693+
self.assertEqual(
1694+
{'w': 0}, results['started'][0].command['writeConcern'])
1695+
results.clear()
1696+
1697+
c_w0.find_one_and_replace({'_id': 1}, {'foo': 'bar'})
1698+
self.assertEqual(
1699+
{'w': 0}, results['started'][0].command['writeConcern'])
1700+
results.clear()
1701+
1702+
c_w0.find_one_and_delete({'_id': 1})
1703+
self.assertEqual(
1704+
{'w': 0}, results['started'][0].command['writeConcern'])
1705+
results.clear()
1706+
else:
1707+
c_w0.find_and_modify(
1708+
{'_id': 1}, {'$set': {'foo': 'bar'}})
1709+
self.assertNotIn('writeConcern', results['started'][0].command)
1710+
results.clear()
1711+
1712+
c_w0.find_one_and_update(
1713+
{'_id': 1}, {'$set': {'foo': 'bar'}})
1714+
self.assertNotIn('writeConcern', results['started'][0].command)
1715+
results.clear()
1716+
1717+
c_w0.find_one_and_replace({'_id': 1}, {'foo': 'bar'})
1718+
self.assertNotIn('writeConcern', results['started'][0].command)
1719+
results.clear()
1720+
1721+
c_w0.find_one_and_delete({'_id': 1})
1722+
self.assertNotIn('writeConcern', results['started'][0].command)
1723+
results.clear()
1724+
1725+
c_default.find_and_modify({'_id': 1}, {'$set': {'foo': 'bar'}})
1726+
self.assertNotIn('writeConcern', results['started'][0].command)
1727+
results.clear()
1728+
1729+
c_default.find_one_and_update({'_id': 1}, {'$set': {'foo': 'bar'}})
1730+
self.assertNotIn('writeConcern', results['started'][0].command)
1731+
results.clear()
1732+
1733+
c_default.find_one_and_replace({'_id': 1}, {'foo': 'bar'})
1734+
self.assertNotIn('writeConcern', results['started'][0].command)
1735+
results.clear()
1736+
1737+
c_default.find_one_and_delete({'_id': 1})
1738+
self.assertNotIn('writeConcern', results['started'][0].command)
1739+
results.clear()
1740+
finally:
1741+
monitoring._LISTENERS = saved_listeners
1742+
16711743
def test_find_with_nested(self):
16721744
c = self.db.test
16731745
c.drop()

0 commit comments

Comments
 (0)