PYTHON-982 - Support bypassDocumentValidation
This commit is contained in:
parent
bcf0d57df3
commit
f5b44ea35f
@ -199,7 +199,7 @@ def _merge_command(run, full_result, results):
|
||||
class _Bulk(object):
|
||||
"""The private guts of the bulk write API.
|
||||
"""
|
||||
def __init__(self, collection, ordered):
|
||||
def __init__(self, collection, ordered, bypass_document_validation):
|
||||
"""Initialize a _Bulk instance.
|
||||
"""
|
||||
self.collection = collection
|
||||
@ -208,6 +208,7 @@ class _Bulk(object):
|
||||
self.name = "%s.%s" % (collection.database.name, collection.name)
|
||||
self.namespace = collection.database.name + '.$cmd'
|
||||
self.executed = False
|
||||
self.bypass_doc_val = bypass_document_validation
|
||||
|
||||
def add_insert(self, document):
|
||||
"""Add an insert document to the list of ops.
|
||||
@ -289,6 +290,8 @@ class _Bulk(object):
|
||||
('ordered', self.ordered)])
|
||||
if write_concern.document:
|
||||
cmd['writeConcern'] = write_concern.document
|
||||
if self.bypass_doc_val and sock_info.max_wire_version >= 4:
|
||||
cmd['bypassDocumentValidation'] = True
|
||||
|
||||
bwc = _BulkWriteContext(db_name, cmd, sock_info, op_id, listeners)
|
||||
results = _do_batched_write_command(
|
||||
@ -320,26 +323,30 @@ class _Bulk(object):
|
||||
for run in generator:
|
||||
try:
|
||||
if run.op_type == _INSERT:
|
||||
coll._insert(sock_info,
|
||||
run.ops,
|
||||
self.ordered,
|
||||
write_concern=write_concern,
|
||||
op_id=op_id)
|
||||
coll._insert(
|
||||
sock_info,
|
||||
run.ops,
|
||||
self.ordered,
|
||||
write_concern=write_concern,
|
||||
op_id=op_id,
|
||||
bypass_doc_val=self.bypass_doc_val)
|
||||
elif run.op_type == _UPDATE:
|
||||
for operation in run.ops:
|
||||
doc = operation['u']
|
||||
check_keys = True
|
||||
if doc and next(iter(doc)).startswith('$'):
|
||||
check_keys = False
|
||||
coll._update(sock_info,
|
||||
operation['q'],
|
||||
doc,
|
||||
operation['upsert'],
|
||||
check_keys,
|
||||
operation['multi'],
|
||||
write_concern=write_concern,
|
||||
op_id=op_id,
|
||||
ordered=self.ordered)
|
||||
coll._update(
|
||||
sock_info,
|
||||
operation['q'],
|
||||
doc,
|
||||
operation['upsert'],
|
||||
check_keys,
|
||||
operation['multi'],
|
||||
write_concern=write_concern,
|
||||
op_id=op_id,
|
||||
ordered=self.ordered,
|
||||
bypass_doc_val=self.bypass_doc_val)
|
||||
else:
|
||||
for operation in run.ops:
|
||||
coll._delete(sock_info,
|
||||
@ -556,7 +563,8 @@ class BulkOperationBuilder(object):
|
||||
|
||||
__slots__ = '__bulk'
|
||||
|
||||
def __init__(self, collection, ordered=True):
|
||||
def __init__(self, collection, ordered=True,
|
||||
bypass_document_validation=False):
|
||||
"""Initialize a new BulkOperationBuilder instance.
|
||||
|
||||
:Parameters:
|
||||
@ -567,8 +575,17 @@ class BulkOperationBuilder(object):
|
||||
in arbitrary order (possibly in parallel on the server), reporting
|
||||
any errors that occurred after attempting all operations. Defaults
|
||||
to ``True``.
|
||||
- `bypass_document_validation`: (optional) If ``True``, allows the
|
||||
write to opt-out of document level validation. Default is
|
||||
``False``.
|
||||
|
||||
.. note:: `bypass_document_validation` requires server version
|
||||
**>= 3.2**
|
||||
|
||||
.. versionchanged:: 3.2
|
||||
Added bypass_document_validation support
|
||||
"""
|
||||
self.__bulk = _Bulk(collection, ordered)
|
||||
self.__bulk = _Bulk(collection, ordered, bypass_document_validation)
|
||||
|
||||
def find(self, selector):
|
||||
"""Specify selection criteria for bulk operations.
|
||||
|
||||
@ -306,36 +306,59 @@ class Collection(common.BaseObject):
|
||||
write_concern or self.write_concern,
|
||||
read_concern or self.read_concern)
|
||||
|
||||
def initialize_unordered_bulk_op(self):
|
||||
def initialize_unordered_bulk_op(self, bypass_document_validation=False):
|
||||
"""Initialize an unordered batch of write operations.
|
||||
|
||||
Operations will be performed on the server in arbitrary order,
|
||||
possibly in parallel. All operations will be attempted.
|
||||
|
||||
:Parameters:
|
||||
- `bypass_document_validation`: (optional) If ``True``, allows the
|
||||
write to opt-out of document level validation. Default is
|
||||
``False``.
|
||||
|
||||
Returns a :class:`~pymongo.bulk.BulkOperationBuilder` instance.
|
||||
|
||||
See :ref:`unordered_bulk` for examples.
|
||||
|
||||
.. note:: `bypass_document_validation` requires server version
|
||||
**>= 3.2**
|
||||
|
||||
.. versionchanged:: 3.2
|
||||
Added bypass_document_validation support
|
||||
|
||||
.. versionadded:: 2.7
|
||||
"""
|
||||
return BulkOperationBuilder(self, ordered=False)
|
||||
return BulkOperationBuilder(self, False, bypass_document_validation)
|
||||
|
||||
def initialize_ordered_bulk_op(self):
|
||||
def initialize_ordered_bulk_op(self, bypass_document_validation=False):
|
||||
"""Initialize an ordered batch of write operations.
|
||||
|
||||
Operations will be performed on the server serially, in the
|
||||
order provided. If an error occurs all remaining operations
|
||||
are aborted.
|
||||
|
||||
:Parameters:
|
||||
- `bypass_document_validation`: (optional) If ``True``, allows the
|
||||
write to opt-out of document level validation. Default is
|
||||
``False``.
|
||||
|
||||
Returns a :class:`~pymongo.bulk.BulkOperationBuilder` instance.
|
||||
|
||||
See :ref:`ordered_bulk` for examples.
|
||||
|
||||
.. note:: `bypass_document_validation` requires server version
|
||||
**>= 3.2**
|
||||
|
||||
.. versionchanged:: 3.2
|
||||
Added bypass_document_validation support
|
||||
|
||||
.. versionadded:: 2.7
|
||||
"""
|
||||
return BulkOperationBuilder(self, ordered=True)
|
||||
return BulkOperationBuilder(self, True, bypass_document_validation)
|
||||
|
||||
def bulk_write(self, requests, ordered=True):
|
||||
def bulk_write(self, requests, ordered=True,
|
||||
bypass_document_validation=False):
|
||||
"""Send a batch of write operations to the server.
|
||||
|
||||
Requests are passed as a list of write operation instances (
|
||||
@ -379,18 +402,27 @@ class Collection(common.BaseObject):
|
||||
occurs all remaining operations are aborted. If ``False`` requests
|
||||
will be performed on the server in arbitrary order, possibly in
|
||||
parallel, and all operations will be attempted.
|
||||
- `bypass_document_validation`: (optional) If ``True``, allows the
|
||||
write to opt-out of document level validation. Default is
|
||||
``False``.
|
||||
|
||||
:Returns:
|
||||
An instance of :class:`~pymongo.results.BulkWriteResult`.
|
||||
|
||||
.. seealso:: :ref:`writes-and-ids`
|
||||
|
||||
.. note:: `bypass_document_validation` requires server version
|
||||
**>= 3.2**
|
||||
|
||||
.. versionchanged:: 3.2
|
||||
Added bypass_document_validation support
|
||||
|
||||
.. versionadded:: 3.0
|
||||
"""
|
||||
if not isinstance(requests, list):
|
||||
raise TypeError("requests must be a list")
|
||||
|
||||
blk = _Bulk(self, ordered)
|
||||
blk = _Bulk(self, ordered, bypass_document_validation)
|
||||
for request in requests:
|
||||
if not isinstance(request, _WriteOp):
|
||||
raise TypeError("%r is not a valid request" % (request,))
|
||||
@ -448,7 +480,7 @@ class Collection(common.BaseObject):
|
||||
|
||||
def _insert_one(
|
||||
self, sock_info, doc, ordered,
|
||||
check_keys, manipulate, write_concern, op_id):
|
||||
check_keys, manipulate, write_concern, op_id, bypass_doc_val):
|
||||
"""Internal helper for inserting a single document."""
|
||||
if manipulate:
|
||||
doc = self.__database._apply_incoming_manipulators(doc, self)
|
||||
@ -465,6 +497,8 @@ class Collection(common.BaseObject):
|
||||
command['writeConcern'] = concern
|
||||
|
||||
if sock_info.max_wire_version > 1 and acknowledged:
|
||||
if bypass_doc_val and sock_info.max_wire_version >= 4:
|
||||
command['bypassDocumentValidation'] = True
|
||||
# Insert command.
|
||||
result = sock_info.command(self.__database.name,
|
||||
command,
|
||||
@ -480,12 +514,13 @@ class Collection(common.BaseObject):
|
||||
return doc.get('_id')
|
||||
|
||||
def _insert(self, sock_info, docs, ordered=True, check_keys=True,
|
||||
manipulate=False, write_concern=None, op_id=None):
|
||||
manipulate=False, write_concern=None, op_id=None,
|
||||
bypass_doc_val=False):
|
||||
"""Internal insert helper."""
|
||||
if isinstance(docs, collections.MutableMapping):
|
||||
return self._insert_one(
|
||||
sock_info, docs, ordered,
|
||||
check_keys, manipulate, write_concern, op_id)
|
||||
check_keys, manipulate, write_concern, op_id, bypass_doc_val)
|
||||
|
||||
ids = []
|
||||
|
||||
@ -522,6 +557,8 @@ class Collection(common.BaseObject):
|
||||
command['writeConcern'] = concern
|
||||
if op_id is None:
|
||||
op_id = message._randint()
|
||||
if bypass_doc_val and sock_info.max_wire_version >= 4:
|
||||
command['bypassDocumentValidation'] = True
|
||||
bwc = message._BulkWriteContext(
|
||||
self.database.name, command, sock_info, op_id,
|
||||
self.database.client._event_listeners)
|
||||
@ -538,7 +575,7 @@ class Collection(common.BaseObject):
|
||||
self.codec_options, bwc)
|
||||
return ids
|
||||
|
||||
def insert_one(self, document):
|
||||
def insert_one(self, document, bypass_document_validation=False):
|
||||
"""Insert a single document.
|
||||
|
||||
>>> db.test.count({'x': 1})
|
||||
@ -553,22 +590,34 @@ class Collection(common.BaseObject):
|
||||
- `document`: The document to insert. Must be a mutable mapping
|
||||
type. If the document does not have an _id field one will be
|
||||
added automatically.
|
||||
- `bypass_document_validation`: (optional) If ``True``, allows the
|
||||
write to opt-out of document level validation. Default is
|
||||
``False``.
|
||||
|
||||
:Returns:
|
||||
- An instance of :class:`~pymongo.results.InsertOneResult`.
|
||||
|
||||
.. seealso:: :ref:`writes-and-ids`
|
||||
|
||||
.. note:: `bypass_document_validation` requires server version
|
||||
**>= 3.2**
|
||||
|
||||
.. versionchanged:: 3.2
|
||||
Added bypass_document_validation support
|
||||
|
||||
.. versionadded:: 3.0
|
||||
"""
|
||||
common.validate_is_mutable_mapping("document", document)
|
||||
if "_id" not in document:
|
||||
document["_id"] = ObjectId()
|
||||
with self._socket_for_writes() as sock_info:
|
||||
return InsertOneResult(self._insert(sock_info, document),
|
||||
self.write_concern.acknowledged)
|
||||
return InsertOneResult(
|
||||
self._insert(sock_info, document,
|
||||
bypass_doc_val=bypass_document_validation),
|
||||
self.write_concern.acknowledged)
|
||||
|
||||
def insert_many(self, documents, ordered=True):
|
||||
def insert_many(self, documents, ordered=True,
|
||||
bypass_document_validation=False):
|
||||
"""Insert an iterable of documents.
|
||||
|
||||
>>> db.test.count()
|
||||
@ -586,12 +635,21 @@ class Collection(common.BaseObject):
|
||||
occurs all remaining inserts are aborted. If ``False``, documents
|
||||
will be inserted on the server in arbitrary order, possibly in
|
||||
parallel, and all document inserts will be attempted.
|
||||
- `bypass_document_validation`: (optional) If ``True``, allows the
|
||||
write to opt-out of document level validation. Default is
|
||||
``False``.
|
||||
|
||||
:Returns:
|
||||
An instance of :class:`~pymongo.results.InsertManyResult`.
|
||||
|
||||
.. seealso:: :ref:`writes-and-ids`
|
||||
|
||||
.. note:: `bypass_document_validation` requires server version
|
||||
**>= 3.2**
|
||||
|
||||
.. versionchanged:: 3.2
|
||||
Added bypass_document_validation support
|
||||
|
||||
.. versionadded:: 3.0
|
||||
"""
|
||||
if not isinstance(documents, collections.Iterable) or not documents:
|
||||
@ -606,14 +664,15 @@ class Collection(common.BaseObject):
|
||||
inserted_ids.append(document["_id"])
|
||||
yield (message._INSERT, document)
|
||||
|
||||
blk = _Bulk(self, ordered)
|
||||
blk = _Bulk(self, ordered, bypass_document_validation)
|
||||
blk.ops = [doc for doc in gen()]
|
||||
blk.execute(self.write_concern.document)
|
||||
return InsertManyResult(inserted_ids, self.write_concern.acknowledged)
|
||||
|
||||
def _update(self, sock_info, criteria, document, upsert=False,
|
||||
check_keys=True, multi=False, manipulate=False,
|
||||
write_concern=None, op_id=None, ordered=True):
|
||||
write_concern=None, op_id=None, ordered=True,
|
||||
bypass_doc_val=False):
|
||||
"""Internal update / replace helper."""
|
||||
common.validate_boolean("upsert", upsert)
|
||||
if manipulate:
|
||||
@ -630,6 +689,8 @@ class Collection(common.BaseObject):
|
||||
command['writeConcern'] = concern
|
||||
if sock_info.max_wire_version > 1 and acknowledged:
|
||||
# Update command.
|
||||
if bypass_doc_val and sock_info.max_wire_version >= 4:
|
||||
command['bypassDocumentValidation'] = True
|
||||
|
||||
# The command result has to be published for APM unmodified
|
||||
# so we make a shallow copy here before adding updatedExisting.
|
||||
@ -655,7 +716,8 @@ class Collection(common.BaseObject):
|
||||
message.update, self.__full_name, upsert, multi, criteria,
|
||||
document, acknowledged, concern, check_keys, self.codec_options)
|
||||
|
||||
def replace_one(self, filter, replacement, upsert=False):
|
||||
def replace_one(self, filter, replacement, upsert=False,
|
||||
bypass_document_validation=False):
|
||||
"""Replace a single document matching the filter.
|
||||
|
||||
>>> for doc in db.test.find({}):
|
||||
@ -690,19 +752,30 @@ class Collection(common.BaseObject):
|
||||
- `replacement`: The new document.
|
||||
- `upsert` (optional): If ``True``, perform an insert if no documents
|
||||
match the filter.
|
||||
- `bypass_document_validation`: (optional) If ``True``, allows the
|
||||
write to opt-out of document level validation. Default is
|
||||
``False``.
|
||||
|
||||
:Returns:
|
||||
- An instance of :class:`~pymongo.results.UpdateResult`.
|
||||
|
||||
.. note:: `bypass_document_validation` requires server version
|
||||
**>= 3.2**
|
||||
|
||||
.. versionchanged:: 3.2
|
||||
Added bypass_document_validation support
|
||||
|
||||
.. versionadded:: 3.0
|
||||
"""
|
||||
common.validate_is_mapping("filter", filter)
|
||||
common.validate_ok_for_replace(replacement)
|
||||
with self._socket_for_writes() as sock_info:
|
||||
result = self._update(sock_info, filter, replacement, upsert)
|
||||
result = self._update(sock_info, filter, replacement, upsert,
|
||||
bypass_doc_val=bypass_document_validation)
|
||||
return UpdateResult(result, self.write_concern.acknowledged)
|
||||
|
||||
def update_one(self, filter, update, upsert=False):
|
||||
def update_one(self, filter, update, upsert=False,
|
||||
bypass_document_validation=False):
|
||||
"""Update a single document matching the filter.
|
||||
|
||||
>>> for doc in db.test.find():
|
||||
@ -728,20 +801,31 @@ class Collection(common.BaseObject):
|
||||
- `update`: The modifications to apply.
|
||||
- `upsert` (optional): If ``True``, perform an insert if no documents
|
||||
match the filter.
|
||||
- `bypass_document_validation`: (optional) If ``True``, allows the
|
||||
write to opt-out of document level validation. Default is
|
||||
``False``.
|
||||
|
||||
:Returns:
|
||||
- An instance of :class:`~pymongo.results.UpdateResult`.
|
||||
|
||||
.. note:: `bypass_document_validation` requires server version
|
||||
**>= 3.2**
|
||||
|
||||
.. versionchanged:: 3.2
|
||||
Added bypass_document_validation support
|
||||
|
||||
.. versionadded:: 3.0
|
||||
"""
|
||||
common.validate_is_mapping("filter", filter)
|
||||
common.validate_ok_for_update(update)
|
||||
with self._socket_for_writes() as sock_info:
|
||||
result = self._update(sock_info, filter, update,
|
||||
upsert, check_keys=False)
|
||||
result = self._update(sock_info, filter, update, upsert,
|
||||
check_keys=False,
|
||||
bypass_doc_val=bypass_document_validation)
|
||||
return UpdateResult(result, self.write_concern.acknowledged)
|
||||
|
||||
def update_many(self, filter, update, upsert=False):
|
||||
def update_many(self, filter, update, upsert=False,
|
||||
bypass_document_validation=False):
|
||||
"""Update one or more documents that match the filter.
|
||||
|
||||
>>> for doc in db.test.find():
|
||||
@ -767,17 +851,27 @@ class Collection(common.BaseObject):
|
||||
- `update`: The modifications to apply.
|
||||
- `upsert` (optional): If ``True``, perform an insert if no documents
|
||||
match the filter.
|
||||
- `bypass_document_validation`: (optional) If ``True``, allows the
|
||||
write to opt-out of document level validation. Default is
|
||||
``False``.
|
||||
|
||||
:Returns:
|
||||
- An instance of :class:`~pymongo.results.UpdateResult`.
|
||||
|
||||
.. note:: `bypass_document_validation` requires server version
|
||||
**>= 3.2**
|
||||
|
||||
.. versionchanged:: 3.2
|
||||
Added bypass_document_validation support
|
||||
|
||||
.. versionadded:: 3.0
|
||||
"""
|
||||
common.validate_is_mapping("filter", filter)
|
||||
common.validate_ok_for_update(update)
|
||||
with self._socket_for_writes() as sock_info:
|
||||
result = self._update(sock_info, filter, update, upsert,
|
||||
check_keys=False, multi=True)
|
||||
check_keys=False, multi=True,
|
||||
bypass_doc_val=bypass_document_validation)
|
||||
return UpdateResult(result, self.write_concern.acknowledged)
|
||||
|
||||
def drop(self):
|
||||
|
||||
@ -138,6 +138,42 @@ class TestBulk(BulkTestBase):
|
||||
# No error.
|
||||
bulk.find({})
|
||||
|
||||
@client_context.require_version_min(3, 1, 9, -1)
|
||||
@client_context.require_no_auth
|
||||
def test_bypass_document_validation_bulk_op(self):
|
||||
|
||||
# Test insert
|
||||
self.coll.insert_one({"z": 0})
|
||||
self.db.command(SON([("collMod", "test"),
|
||||
("validator", {"z": {"$gte": 0}})]))
|
||||
bulk = self.coll.initialize_ordered_bulk_op(
|
||||
bypass_document_validation=False)
|
||||
bulk.insert({"z": -1}) # error
|
||||
self.assertRaises(BulkWriteError, bulk.execute)
|
||||
self.assertEqual(0, self.coll.count({"z": -1}))
|
||||
|
||||
bulk = self.coll.initialize_ordered_bulk_op(
|
||||
bypass_document_validation=True)
|
||||
bulk.insert({"z": -1})
|
||||
bulk.execute()
|
||||
self.assertEqual(1, self.coll.count({"z": -1}))
|
||||
|
||||
self.coll.insert_one({"z": 0})
|
||||
self.db.command(SON([("collMod", "test"),
|
||||
("validator", {"z": {"$gte": 0}})]))
|
||||
bulk = self.coll.initialize_unordered_bulk_op(
|
||||
bypass_document_validation=False)
|
||||
bulk.insert({"z": -1}) # error
|
||||
self.assertRaises(BulkWriteError, bulk.execute)
|
||||
self.assertEqual(1, self.coll.count({"z": -1}))
|
||||
|
||||
bulk = self.coll.initialize_unordered_bulk_op(
|
||||
bypass_document_validation=True)
|
||||
bulk.insert({"z": -1})
|
||||
bulk.execute()
|
||||
self.assertEqual(2, self.coll.count({"z": -1}))
|
||||
self.coll.drop()
|
||||
|
||||
def test_insert(self):
|
||||
expected = {
|
||||
'nMatched': 0,
|
||||
|
||||
@ -32,6 +32,7 @@ from bson.son import SON
|
||||
from pymongo import (ASCENDING, DESCENDING, GEO2D,
|
||||
GEOHAYSTACK, GEOSPHERE, HASHED, TEXT)
|
||||
from pymongo import MongoClient, monitoring
|
||||
from pymongo.bulk import BulkWriteError
|
||||
from pymongo.collection import Collection, ReturnDocument
|
||||
from pymongo.command_cursor import CommandCursor
|
||||
from pymongo.cursor import CursorType
|
||||
@ -42,7 +43,7 @@ from pymongo.errors import (DocumentTooLarge,
|
||||
InvalidOperation,
|
||||
OperationFailure)
|
||||
from pymongo.message import _COMMAND_OVERHEAD
|
||||
from pymongo.operations import IndexModel
|
||||
from pymongo.operations import *
|
||||
from pymongo.read_preferences import ReadPreference
|
||||
from pymongo.results import (InsertOneResult,
|
||||
InsertManyResult,
|
||||
@ -726,6 +727,177 @@ class TestCollection(IntegrationTest):
|
||||
self.assertRaises(
|
||||
DocumentTooLarge, coll.delete_one, {'data': large})
|
||||
|
||||
@client_context.require_version_min(3, 1, 9, -1)
|
||||
@client_context.require_no_auth
|
||||
def test_insert_bypass_document_validation(self):
|
||||
db = self.db
|
||||
db.test.drop()
|
||||
db.create_collection("test", validator={"a": {"$exists": True}})
|
||||
|
||||
# Test insert_one
|
||||
self.assertRaises(OperationFailure, db.test.insert_one,
|
||||
{"_id": 1, "x": 100})
|
||||
result = db.test.insert_one({"_id": 1, "x": 100},
|
||||
bypass_document_validation=True)
|
||||
self.assertTrue(isinstance(result, InsertOneResult))
|
||||
self.assertEqual(1, result.inserted_id)
|
||||
result = db.test.insert_one({"_id":2, "a":0})
|
||||
self.assertTrue(isinstance(result, InsertOneResult))
|
||||
self.assertEqual(2, result.inserted_id)
|
||||
|
||||
# Test insert_many
|
||||
docs = [{"_id": i, "x": 100 - i} for i in range(3, 100)]
|
||||
self.assertRaises(OperationFailure, db.test.insert_many, docs)
|
||||
result = db.test.insert_many(docs, bypass_document_validation=True)
|
||||
self.assertTrue(isinstance(result, InsertManyResult))
|
||||
self.assertTrue(97, len(result.inserted_ids))
|
||||
for doc in docs:
|
||||
_id = doc["_id"]
|
||||
self.assertTrue(isinstance(_id, int))
|
||||
self.assertTrue(_id in result.inserted_ids)
|
||||
self.assertEqual(1, db.test.count({"x": doc["x"]}))
|
||||
self.assertTrue(result.acknowledged)
|
||||
docs = [{"_id": i, "a": 200 - i} for i in range(100, 200)]
|
||||
result = db.test.insert_many(docs)
|
||||
self.assertTrue(isinstance(result, InsertManyResult))
|
||||
self.assertTrue(97, len(result.inserted_ids))
|
||||
for doc in docs:
|
||||
_id = doc["_id"]
|
||||
self.assertTrue(isinstance(_id, int))
|
||||
self.assertTrue(_id in result.inserted_ids)
|
||||
self.assertEqual(1, db.test.count({"a": doc["a"]}))
|
||||
self.assertTrue(result.acknowledged)
|
||||
|
||||
@client_context.require_version_min(3, 1, 9, -1)
|
||||
@client_context.require_no_auth
|
||||
def test_replace_bypass_document_validation(self):
|
||||
db = self.db
|
||||
db.test.drop()
|
||||
db.create_collection("test", validator={"a": {"$exists": True}})
|
||||
|
||||
# Test replace_one
|
||||
db.test.insert_one({"a": 101})
|
||||
self.assertRaises(OperationFailure, db.test.replace_one,
|
||||
{"a": 101}, {"y": 1})
|
||||
self.assertEqual(0, db.test.count({"y": 1}))
|
||||
self.assertEqual(1, db.test.count({"a": 101}))
|
||||
db.test.replace_one({"a": 101}, {"y": 1},
|
||||
bypass_document_validation=True)
|
||||
self.assertEqual(0, db.test.count({"a": 101}))
|
||||
self.assertEqual(1, db.test.count({"y": 1}))
|
||||
db.test.replace_one({"y": 1}, {"a": 102})
|
||||
self.assertEqual(0, db.test.count({"y": 1}))
|
||||
self.assertEqual(0, db.test.count({"a": 101}))
|
||||
self.assertEqual(1, db.test.count({"a": 102}))
|
||||
|
||||
db.test.insert_one({"y": 1}, bypass_document_validation=True)
|
||||
self.assertRaises(OperationFailure, db.test.replace_one,
|
||||
{"y": 1}, {"x": 101})
|
||||
self.assertEqual(0, db.test.count({"x": 101}))
|
||||
self.assertEqual(1, db.test.count({"y": 1}))
|
||||
db.test.replace_one({"y": 1}, {"x": 101},
|
||||
bypass_document_validation=True)
|
||||
self.assertEqual(0, db.test.count({"y": 1}))
|
||||
self.assertEqual(1, db.test.count({"x": 101}))
|
||||
db.test.replace_one({"x": 101}, {"a": 103},
|
||||
bypass_document_validation=False)
|
||||
self.assertEqual(0, db.test.count({"x": 101}))
|
||||
self.assertEqual(1, db.test.count({"a": 103}))
|
||||
|
||||
@client_context.require_version_min(3, 1, 9, -1)
|
||||
@client_context.require_no_auth
|
||||
def test_update_bypass_document_validation(self):
|
||||
db = self.db
|
||||
db.test.drop()
|
||||
db.test.insert_one({"z": 5})
|
||||
db.command(SON([("collMod", "test"),
|
||||
("validator", {"z": {"$gte": 0}})]))
|
||||
|
||||
# Test update_one
|
||||
self.assertRaises(OperationFailure, db.test.update_one,
|
||||
{"z": 5}, {"$inc": {"z": -10}})
|
||||
self.assertEqual(0, db.test.count({"z": -5}))
|
||||
self.assertEqual(1, db.test.count({"z": 5}))
|
||||
db.test.update_one({"z": 5}, {"$inc": {"z": -10}},
|
||||
bypass_document_validation=True)
|
||||
self.assertEqual(0, db.test.count({"z": 5}))
|
||||
self.assertEqual(1, db.test.count({"z": -5}))
|
||||
db.test.update_one({"z": -5}, {"$inc": {"z": 6}},
|
||||
bypass_document_validation=False)
|
||||
self.assertEqual(1, db.test.count({"z": 1}))
|
||||
self.assertEqual(0, db.test.count({"z": -5}))
|
||||
|
||||
db.test.insert_one({"z": -10},
|
||||
bypass_document_validation=True)
|
||||
self.assertRaises(OperationFailure, db.test.update_one,
|
||||
{"z": -10}, {"$inc": {"z": 1}})
|
||||
self.assertEqual(0, db.test.count({"z": -9}))
|
||||
self.assertEqual(1, db.test.count({"z": -10}))
|
||||
db.test.update_one({"z": -10}, {"$inc": {"z": 1}},
|
||||
bypass_document_validation=True)
|
||||
self.assertEqual(1, db.test.count({"z": -9}))
|
||||
self.assertEqual(0, db.test.count({"z": -10}))
|
||||
db.test.update_one({"z": -9}, {"$inc": {"z": 9}},
|
||||
bypass_document_validation=False)
|
||||
self.assertEqual(0, db.test.count({"z": -9}))
|
||||
self.assertEqual(1, db.test.count({"z": 0}))
|
||||
|
||||
# Test update_many
|
||||
db.test.insert_many([{"z": i} for i in range(3, 101)])
|
||||
db.test.insert_one({"y": 0},
|
||||
bypass_document_validation=True)
|
||||
self.assertRaises(OperationFailure, db.test.update_many, {},
|
||||
{"$inc": {"z": -100}})
|
||||
self.assertEqual(100, db.test.count({"z": {"$gte": 0}}))
|
||||
self.assertEqual(0, db.test.count({"z": {"$lt": 0}}))
|
||||
self.assertEqual(0, db.test.count({"y": 0, "z": -100}))
|
||||
db.test.update_many({"z": {"$gte": 0}}, {"$inc": {"z": -100}},
|
||||
bypass_document_validation=True)
|
||||
self.assertEqual(0, db.test.count({"z": {"$gt": 0}}))
|
||||
self.assertEqual(100, db.test.count({"z": {"$lte": 0}}))
|
||||
db.test.update_many({"z": {"$gt": -50}}, {"$inc": {"z": 100}},
|
||||
bypass_document_validation=False)
|
||||
self.assertEqual(50, db.test.count({"z": {"$gt": 0}}))
|
||||
self.assertEqual(50, db.test.count({"z": {"$lt": 0}}))
|
||||
|
||||
db.test.insert_many([{"z": -i} for i in range(50)],
|
||||
bypass_document_validation=True)
|
||||
self.assertRaises(OperationFailure, db.test.update_many,
|
||||
{}, {"$inc": {"z": 1}})
|
||||
self.assertEqual(100, db.test.count({"z": {"$lte": 0}}))
|
||||
self.assertEqual(50, db.test.count({"z": {"$gt": 1}}))
|
||||
db.test.update_many({"z": {"$gte": 0}}, {"$inc": {"z": -100}},
|
||||
bypass_document_validation=True)
|
||||
self.assertEqual(0, db.test.count({"z": {"$gt": 0}}))
|
||||
self.assertEqual(150, db.test.count({"z": {"$lte": 0}}))
|
||||
db.test.update_many({"z": {"$lte": 0}}, {"$inc": {"z": 100}},
|
||||
bypass_document_validation=False)
|
||||
self.assertEqual(150, db.test.count({"z": {"$gte": 0}}))
|
||||
self.assertEqual(0, db.test.count({"z": {"$lt": 0}}))
|
||||
|
||||
@client_context.require_version_min(3, 1, 9, -1)
|
||||
@client_context.require_no_auth
|
||||
def test_bypass_document_validation_bulk_write(self):
|
||||
db = self.db
|
||||
db.test.drop()
|
||||
db.create_collection("test", validator={"a": {"$gte": 0}})
|
||||
ops = [InsertOne({"a": -10}),
|
||||
InsertOne({"a": -11}),
|
||||
InsertOne({"a": -12}),
|
||||
UpdateOne({"a": {"$lte": -10}}, {"$inc": {"a": 1}}),
|
||||
UpdateMany({"a": {"$lte": -10}}, {"$inc": {"a": 1}}),
|
||||
ReplaceOne({"a": {"$lte": -10}}, {"a": -1})]
|
||||
db.test.bulk_write(ops, bypass_document_validation=True)
|
||||
|
||||
self.assertEqual(3, db.test.count())
|
||||
self.assertEqual(1, db.test.count({"a": -11}))
|
||||
self.assertEqual(1, db.test.count({"a": -1}))
|
||||
self.assertEqual(1, db.test.count({"a": -9}))
|
||||
|
||||
# Assert that the operations would fail without bypass_doc_val
|
||||
for op in ops:
|
||||
self.assertRaises(BulkWriteError, db.test.bulk_write, [op])
|
||||
|
||||
def test_find_by_default_dct(self):
|
||||
db = self.db
|
||||
db.test.insert_one({'foo': 'bar'})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user