From b9cd7b627bd4840b5b8ff0f18b124008ec7683bf Mon Sep 17 00:00:00 2001 From: Bernie Hackett Date: Wed, 11 Feb 2015 13:39:45 -0800 Subject: [PATCH] PYTHON-821 - Introduce results and options modules. --- doc/api/pymongo/collection.rst | 12 +- doc/api/pymongo/index.rst | 2 + doc/api/pymongo/options.rst | 6 + doc/api/pymongo/results.rst | 6 + pymongo/bulk.py | 237 --------------------------------- pymongo/collection.py | 63 +++------ pymongo/options.py | 188 ++++++++++++++++++++++++++ pymongo/results.py | 117 ++++++++++++++++ test/test_bulk.py | 2 +- test/test_collection.py | 4 +- 10 files changed, 341 insertions(+), 296 deletions(-) create mode 100644 doc/api/pymongo/options.rst create mode 100644 doc/api/pymongo/results.rst create mode 100644 pymongo/options.py create mode 100644 pymongo/results.py diff --git a/doc/api/pymongo/collection.rst b/doc/api/pymongo/collection.rst index 3d0fc4eac..4c58e24ec 100644 --- a/doc/api/pymongo/collection.rst +++ b/doc/api/pymongo/collection.rst @@ -16,17 +16,7 @@ .. autodata:: pymongo.cursor.TAILABLE_AWAIT .. autodata:: pymongo.cursor.EXHAUST - .. autoclass:: pymongo.collection.InsertOneResult - :members: - - .. autoclass:: pymongo.collection.ReturnDocument - - .. autoattribute:: Before - :annotation: - .. autoattribute:: After - :annotation: - - .. autoclass:: pymongo.collection.Collection(database, name[, create=False[, **kwargs]]]) + .. autoclass:: pymongo.collection.Collection(database, name[, create=False[, **kwargs]]) .. describe:: c[name] || c.name diff --git a/doc/api/pymongo/index.rst b/doc/api/pymongo/index.rst index 572f6445f..dde01d5ce 100644 --- a/doc/api/pymongo/index.rst +++ b/doc/api/pymongo/index.rst @@ -40,8 +40,10 @@ Sub-modules: message mongo_client mongo_replica_set_client + options pool read_preferences + results son_manipulator cursor_manager uri_parser diff --git a/doc/api/pymongo/options.rst b/doc/api/pymongo/options.rst new file mode 100644 index 000000000..c757f7635 --- /dev/null +++ b/doc/api/pymongo/options.rst @@ -0,0 +1,6 @@ +:mod:`options` -- Option class definitions +========================================== + +.. automodule:: pymongo.options + :synopsis: Option class definitions + :members: diff --git a/doc/api/pymongo/results.rst b/doc/api/pymongo/results.rst new file mode 100644 index 000000000..70243c285 --- /dev/null +++ b/doc/api/pymongo/results.rst @@ -0,0 +1,6 @@ +:mod:`results` -- Result class definitions +========================================== + +.. automodule:: pymongo.results + :synopsis: Result class definitions + :members: diff --git a/pymongo/bulk.py b/pymongo/bulk.py index 0fa943dbf..e874f674a 100644 --- a/pymongo/bulk.py +++ b/pymongo/bulk.py @@ -42,243 +42,6 @@ _WRITE_CONCERN_ERROR = 64 _COMMANDS = ('insert', 'update', 'delete') -class _WriteOp(object): - """Private base class for all write operations.""" - - __slots__ = ("_filter", "_doc", "_upsert") - - def __init__(self, filter=None, doc=None, upsert=None): - if filter is not None and not isinstance(filter, collections.Mapping): - raise TypeError("filter must be a mapping type.") - if upsert is not None and not isinstance(upsert, bool): - raise TypeError("upsert must be True or False") - self._filter = filter - self._doc = doc - self._upsert = upsert - - -class InsertOne(_WriteOp): - """Represents an insert_one operation.""" - - def __init__(self, document): - """Create an InsertOne instance. - - For use with :meth:`~pymongo.collection.Collection.bulk_write`. - - :Parameters: - - `document`: The document to insert. If the document is missing an - _id field one will be added. - """ - super(InsertOne, self).__init__(doc=document) - - def _add_to_bulk(self, bulkobj): - """Add this operation to the _Bulk instance `bulkobj`.""" - bulkobj.add_insert(self._doc) - - def __repr__(self): - return "InsertOne(%r)" % (self._doc,) - - -class DeleteOne(_WriteOp): - """Represents a delete_one operation.""" - - def __init__(self, filter): - """Create a DeleteOne instance. - - For use with :meth:`~pymongo.collection.Collection.bulk_write`. - - :Parameters: - - `filter`: A query that matches the document to delete. - """ - super(DeleteOne, self).__init__(filter) - - def _add_to_bulk(self, bulkobj): - """Add this operation to the _Bulk instance `bulkobj`.""" - bulkobj.add_delete(self._filter, 1) - - def __repr__(self): - return "DeleteOne(%r)" % (self._filter,) - - -class DeleteMany(_WriteOp): - """Represents a delete_many operation.""" - - def __init__(self, filter): - """Create a DeleteMany instance. - - For use with :meth:`~pymongo.collection.Collection.bulk_write`. - - :Parameters: - - `filter`: A query that matches the documents to delete. - """ - super(DeleteMany, self).__init__(filter) - - def _add_to_bulk(self, bulkobj): - """Add this operation to the _Bulk instance `bulkobj`.""" - bulkobj.add_delete(self._filter, 0) - - def __repr__(self): - return "DeleteMany(%r)" % (self._filter,) - - -class ReplaceOne(_WriteOp): - """Represents a replace_one operation.""" - - def __init__(self, filter, replacement, upsert=False): - """Create a ReplaceOne instance. - - For use with :meth:`~pymongo.collection.Collection.bulk_write`. - - :Parameters: - - `filter`: A query that matches the document to replace. - - `replacement`: The new document. - - `upsert` (optional): If ``True``, perform an insert if no documents - match the filter. - """ - super(ReplaceOne, self).__init__(filter, replacement, upsert) - - def _add_to_bulk(self, bulkobj): - """Add this operation to the _Bulk instance `bulkobj`.""" - bulkobj.add_replace(self._filter, self._doc, self._upsert) - - def __repr__(self): - return "ReplaceOne(%r, %r, %r)" % (self._filter, - self._doc, - self._upsert) - - -class UpdateOne(_WriteOp): - """Represents an update_one operation.""" - - def __init__(self, filter, update, upsert=False): - """Represents an update_one operation. - - For use with :meth:`~pymongo.collection.Collection.bulk_write`. - - :Parameters: - - `filter`: A query that matches the document to update. - - `update`: The modifications to apply. - - `upsert` (optional): If ``True``, perform an insert if no documents - match the filter. - """ - super(UpdateOne, self).__init__(filter, update, upsert) - - def _add_to_bulk(self, bulkobj): - """Add this operation to the _Bulk instance `bulkobj`.""" - bulkobj.add_update(self._filter, self._doc, False, self._upsert) - - def __repr__(self): - return "UpdateOne(%r, %r, %r)" % (self._filter, - self._doc, - self._upsert) - - -class UpdateMany(_WriteOp): - """Represents an update_many operation.""" - - def __init__(self, filter, update, upsert=False): - """Create an UpdateMany instance. - - For use with :meth:`~pymongo.collection.Collection.bulk_write`. - - :Parameters: - - `filter`: A query that matches the documents to update. - - `update`: The modifications to apply. - - `upsert` (optional): If ``True``, perform an insert if no documents - match the filter. - """ - super(UpdateMany, self).__init__(filter, update, upsert) - - def _add_to_bulk(self, bulkobj): - """Add this operation to the _Bulk instance `bulkobj`.""" - bulkobj.add_update(self._filter, self._doc, True, self._upsert) - - def __repr__(self): - return "UpdateMany(%r, %r, %r)" % (self._filter, - self._doc, - self._upsert) - - -class BulkWriteResult(object): - """An object wrapper for bulk API write results.""" - - __slots__ = ("__bulk_api_result", "__acknowledged") - - def __init__(self, bulk_api_result, acknowledged): - """Create a BulkWriteResult instance. - - :Parameters: - - `bulk_api_result`: A result dict from the bulk API - - `acknowledged`: Was this write result acknowledged? If ``False`` - then all properties of this object will raise - :exc:`~pymongo.errors.InvalidOperation`. - """ - self.__bulk_api_result = bulk_api_result - self.__acknowledged = acknowledged - - def __raise_if_unacknowledged(self, property_name): - """Raise an exception on property access if unacknowledged.""" - if not self.__acknowledged: - raise InvalidOperation("A value for %s is not available when " - "the write is unacknowledged. Check the " - "acknowledged attribute to avoid this " - "error." % (property_name,)) - - @property - def bulk_api_result(self): - """The raw bulk API result.""" - return self.__bulk_api_result - - @property - def acknowledged(self): - """Is this the result of an acknowledged bulk write operation?""" - return self.__acknowledged - - @property - def inserted_count(self): - """The number of documents inserted.""" - self.__raise_if_unacknowledged("inserted_count") - return self.__bulk_api_result.get("nInserted") - - @property - def matched_count(self): - """The number of documents matched for an update.""" - self.__raise_if_unacknowledged("matched_count") - return self.__bulk_api_result.get("nMatched") - - @property - def modified_count(self): - """The number of documents modified. - - .. note:: modified_count is only reported by MongoDB 2.6 and later. - When connected to an earlier server version, or in certain mixed - version sharding configurations, this attribute will be set to - ``None``. - """ - self.__raise_if_unacknowledged("modified_count") - return self.__bulk_api_result.get("nModified") - - @property - def deleted_count(self): - """The number of documents deleted.""" - self.__raise_if_unacknowledged("deleted_count") - return self.__bulk_api_result.get("nRemoved") - - @property - def upserted_count(self): - """The number of documents upserted.""" - self.__raise_if_unacknowledged("upserted_count") - return self.__bulk_api_result.get("nUpserted") - - @property - def upserted_ids(self): - """A map of operation index to the _id of the upserted document.""" - self.__raise_if_unacknowledged("upserted_ids") - if self.__bulk_api_result: - return dict((upsert["index"], upsert["_id"]) - for upsert in self.bulk_api_result["upserted"]) - - class _Run(object): """Represents a batch of write operations. """ diff --git a/pymongo/collection.py b/pymongo/collection.py index eb0385272..3b3b17c60 100644 --- a/pymongo/collection.py +++ b/pymongo/collection.py @@ -29,16 +29,15 @@ from bson.son import SON from pymongo import (common, helpers, message) -from pymongo.bulk import (BulkOperationBuilder, - BulkWriteResult, - _Bulk, - _WriteOp) +from pymongo.bulk import (BulkOperationBuilder, _Bulk) from pymongo.command_cursor import CommandCursor from pymongo.cursor import Cursor from pymongo.errors import ConfigurationError, InvalidName, OperationFailure from pymongo.helpers import _check_write_command_response, _command from pymongo.message import _INSERT, _UPDATE, _DELETE +from pymongo.options import ReturnDocument, _WriteOp from pymongo.read_preferences import ReadPreference +from pymongo.results import InsertOneResult, BulkWriteResult from pymongo.write_concern import WriteConcern @@ -51,38 +50,6 @@ except ImportError: _NO_OBJ_ERROR = "No matching object found" -class InsertOneResult(object): - """The return type for :meth:`Collection.insert_one`.""" - - __slots__ = ("__inserted_id", "__acknowledged") - - def __init__(self, inserted_id, acknowledged): - self.__inserted_id = inserted_id - self.__acknowledged = acknowledged - - @property - def inserted_id(self): - """The inserted document's _id.""" - return self.__inserted_id - - @property - def acknowledged(self): - """Is this the result of an acknowledged write operation?""" - return self.__acknowledged - - -class ReturnDocument(object): - """An enum used with :meth:`Collection.find_one_and_replace` and - :meth:`Collection.find_one_and_update`. - """ - Before = False - """Return the original document before it was updated/replaced, or - ``None`` if no document matches the query. - """ - After = True - """Return the updated/replaced or inserted document.""" - - def _gen_index_name(keys): """Generate an index name from the set of fields it is over. """ @@ -321,11 +288,11 @@ class Collection(common.BaseObject): the :meth:`initialize_ordered_bulk_op` and :meth:`initialize_unordered_bulk_op` methods. Write operations are passed as a list using the write operation classes from the - :mod:`~pymongo.bulk` module:: + :mod:`~pymongo.options` module:: >>> # DeleteOne, UpdateOne, and UpdateMany are also available. ... - >>> from pymongo.bulk import InsertOne, DeleteMany, ReplaceOne + >>> from pymongo.options import InsertOne, DeleteMany, ReplaceOne >>> requests = [InsertOne({'foo': 1}), DeleteMany({'bar': 2}), ... ReplaceOne({'bar': 1}, {'bim': 2}, upsert=True)] >>> coll.bulk_write(requests) @@ -339,7 +306,7 @@ class Collection(common.BaseObject): parallel, and all operations will be attempted. :Returns: - An instance of :class:`~pymongo.bulk.BulkWriteResult`. + An instance of :class:`~pymongo.results.BulkWriteResult`. .. versionadded:: 3.0 """ @@ -519,7 +486,7 @@ class Collection(common.BaseObject): added automatically. :Returns: - - An instance of :class:`InsertOneResult`. + - An instance of :class:`~pymongo.results.InsertOneResult`. """ if not isinstance(document, collections.MutableMapping): raise TypeError("document must be a mutable mapping type") @@ -1909,10 +1876,12 @@ class Collection(common.BaseObject): match the query, they are sorted and the first is replaced. - `upsert` (optional): When ``True``, inserts a new document if no document matches the query. Defaults to ``False``. - - `return_document`: If :attr:`ReturnDocument.Before` (the default), + - `return_document`: If + :attr:`~pymongo.options.ReturnDocument.Before` (the default), returns the original document before it was replaced, or ``None`` - if no document matches. If :attr:`ReturnDocument.After`, returns - the replaced or inserted document. + if no document matches. If + :attr:`~pymongo.options.ReturnDocument.After`, returns the replaced + or inserted document. - `**kwargs` (optional): additional command arguments can be passed as keyword arguments (for example maxTimeMS can be used with recent server versions). @@ -1947,10 +1916,12 @@ class Collection(common.BaseObject): match the query, they are sorted and the first is updated. - `upsert` (optional): When ``True``, inserts a new document if no document matches the query. Defaults to ``False``. - - `return_document`: If :attr:`ReturnDocument.Before` (the default), + - `return_document`: If + :attr:`~pymongo.options.ReturnDocument.Before` (the default), returns the original document before it was updated, or ``None`` - if no document matches. If :attr:`ReturnDocument.After`, returns - the updated or inserted document. + if no document matches. If + :attr:`~pymongo.options.ReturnDocument.After`, returns the updated + or inserted document. - `**kwargs` (optional): additional command arguments can be passed as keyword arguments (for example maxTimeMS can be used with recent server versions). diff --git a/pymongo/options.py b/pymongo/options.py new file mode 100644 index 000000000..ddccb917e --- /dev/null +++ b/pymongo/options.py @@ -0,0 +1,188 @@ +# Copyright 2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Option class definitions.""" + +import collections + + +class ReturnDocument(object): + """An enum used with + :meth:`~pymongo.collection.Collection.find_one_and_replace` and + :meth:`~pymongo.collection.Collection.find_one_and_update`. + """ + Before = False + """Return the original document before it was updated/replaced, or + ``None`` if no document matches the query. + """ + After = True + """Return the updated/replaced or inserted document.""" + + +class _WriteOp(object): + """Private base class for all write operations.""" + + __slots__ = ("_filter", "_doc", "_upsert") + + def __init__(self, filter=None, doc=None, upsert=None): + if filter is not None and not isinstance(filter, collections.Mapping): + raise TypeError("filter must be a mapping type.") + if upsert is not None and not isinstance(upsert, bool): + raise TypeError("upsert must be True or False") + self._filter = filter + self._doc = doc + self._upsert = upsert + + +class InsertOne(_WriteOp): + """Represents an insert_one operation.""" + + def __init__(self, document): + """Create an InsertOne instance. + + For use with :meth:`~pymongo.collection.Collection.bulk_write`. + + :Parameters: + - `document`: The document to insert. If the document is missing an + _id field one will be added. + """ + super(InsertOne, self).__init__(doc=document) + + def _add_to_bulk(self, bulkobj): + """Add this operation to the _Bulk instance `bulkobj`.""" + bulkobj.add_insert(self._doc) + + def __repr__(self): + return "InsertOne(%r)" % (self._doc,) + + +class DeleteOne(_WriteOp): + """Represents a delete_one operation.""" + + def __init__(self, filter): + """Create a DeleteOne instance. + + For use with :meth:`~pymongo.collection.Collection.bulk_write`. + + :Parameters: + - `filter`: A query that matches the document to delete. + """ + super(DeleteOne, self).__init__(filter) + + def _add_to_bulk(self, bulkobj): + """Add this operation to the _Bulk instance `bulkobj`.""" + bulkobj.add_delete(self._filter, 1) + + def __repr__(self): + return "DeleteOne(%r)" % (self._filter,) + + +class DeleteMany(_WriteOp): + """Represents a delete_many operation.""" + + def __init__(self, filter): + """Create a DeleteMany instance. + + For use with :meth:`~pymongo.collection.Collection.bulk_write`. + + :Parameters: + - `filter`: A query that matches the documents to delete. + """ + super(DeleteMany, self).__init__(filter) + + def _add_to_bulk(self, bulkobj): + """Add this operation to the _Bulk instance `bulkobj`.""" + bulkobj.add_delete(self._filter, 0) + + def __repr__(self): + return "DeleteMany(%r)" % (self._filter,) + + +class ReplaceOne(_WriteOp): + """Represents a replace_one operation.""" + + def __init__(self, filter, replacement, upsert=False): + """Create a ReplaceOne instance. + + For use with :meth:`~pymongo.collection.Collection.bulk_write`. + + :Parameters: + - `filter`: A query that matches the document to replace. + - `replacement`: The new document. + - `upsert` (optional): If ``True``, perform an insert if no documents + match the filter. + """ + super(ReplaceOne, self).__init__(filter, replacement, upsert) + + def _add_to_bulk(self, bulkobj): + """Add this operation to the _Bulk instance `bulkobj`.""" + bulkobj.add_replace(self._filter, self._doc, self._upsert) + + def __repr__(self): + return "ReplaceOne(%r, %r, %r)" % (self._filter, + self._doc, + self._upsert) + + +class UpdateOne(_WriteOp): + """Represents an update_one operation.""" + + def __init__(self, filter, update, upsert=False): + """Represents an update_one operation. + + For use with :meth:`~pymongo.collection.Collection.bulk_write`. + + :Parameters: + - `filter`: A query that matches the document to update. + - `update`: The modifications to apply. + - `upsert` (optional): If ``True``, perform an insert if no documents + match the filter. + """ + super(UpdateOne, self).__init__(filter, update, upsert) + + def _add_to_bulk(self, bulkobj): + """Add this operation to the _Bulk instance `bulkobj`.""" + bulkobj.add_update(self._filter, self._doc, False, self._upsert) + + def __repr__(self): + return "UpdateOne(%r, %r, %r)" % (self._filter, + self._doc, + self._upsert) + + +class UpdateMany(_WriteOp): + """Represents an update_many operation.""" + + def __init__(self, filter, update, upsert=False): + """Create an UpdateMany instance. + + For use with :meth:`~pymongo.collection.Collection.bulk_write`. + + :Parameters: + - `filter`: A query that matches the documents to update. + - `update`: The modifications to apply. + - `upsert` (optional): If ``True``, perform an insert if no documents + match the filter. + """ + super(UpdateMany, self).__init__(filter, update, upsert) + + def _add_to_bulk(self, bulkobj): + """Add this operation to the _Bulk instance `bulkobj`.""" + bulkobj.add_update(self._filter, self._doc, True, self._upsert) + + def __repr__(self): + return "UpdateMany(%r, %r, %r)" % (self._filter, + self._doc, + self._upsert) + diff --git a/pymongo/results.py b/pymongo/results.py new file mode 100644 index 000000000..35227b105 --- /dev/null +++ b/pymongo/results.py @@ -0,0 +1,117 @@ +# Copyright 2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Result class definitions.""" + +from pymongo.errors import InvalidOperation + + +class InsertOneResult(object): + """The return type for :meth:`Collection.insert_one`.""" + + __slots__ = ("__inserted_id", "__acknowledged") + + def __init__(self, inserted_id, acknowledged): + self.__inserted_id = inserted_id + self.__acknowledged = acknowledged + + @property + def inserted_id(self): + """The inserted document's _id.""" + return self.__inserted_id + + @property + def acknowledged(self): + """Is this the result of an acknowledged write operation?""" + return self.__acknowledged + + +class BulkWriteResult(object): + """An object wrapper for bulk API write results.""" + + __slots__ = ("__bulk_api_result", "__acknowledged") + + def __init__(self, bulk_api_result, acknowledged): + """Create a BulkWriteResult instance. + + :Parameters: + - `bulk_api_result`: A result dict from the bulk API + - `acknowledged`: Was this write result acknowledged? If ``False`` + then all properties of this object will raise + :exc:`~pymongo.errors.InvalidOperation`. + """ + self.__bulk_api_result = bulk_api_result + self.__acknowledged = acknowledged + + def __raise_if_unacknowledged(self, property_name): + """Raise an exception on property access if unacknowledged.""" + if not self.__acknowledged: + raise InvalidOperation("A value for %s is not available when " + "the write is unacknowledged. Check the " + "acknowledged attribute to avoid this " + "error." % (property_name,)) + + @property + def bulk_api_result(self): + """The raw bulk API result.""" + return self.__bulk_api_result + + @property + def acknowledged(self): + """Is this the result of an acknowledged bulk write operation?""" + return self.__acknowledged + + @property + def inserted_count(self): + """The number of documents inserted.""" + self.__raise_if_unacknowledged("inserted_count") + return self.__bulk_api_result.get("nInserted") + + @property + def matched_count(self): + """The number of documents matched for an update.""" + self.__raise_if_unacknowledged("matched_count") + return self.__bulk_api_result.get("nMatched") + + @property + def modified_count(self): + """The number of documents modified. + + .. note:: modified_count is only reported by MongoDB 2.6 and later. + When connected to an earlier server version, or in certain mixed + version sharding configurations, this attribute will be set to + ``None``. + """ + self.__raise_if_unacknowledged("modified_count") + return self.__bulk_api_result.get("nModified") + + @property + def deleted_count(self): + """The number of documents deleted.""" + self.__raise_if_unacknowledged("deleted_count") + return self.__bulk_api_result.get("nRemoved") + + @property + def upserted_count(self): + """The number of documents upserted.""" + self.__raise_if_unacknowledged("upserted_count") + return self.__bulk_api_result.get("nUpserted") + + @property + def upserted_ids(self): + """A map of operation index to the _id of the upserted document.""" + self.__raise_if_unacknowledged("upserted_ids") + if self.__bulk_api_result: + return dict((upsert["index"], upsert["_id"]) + for upsert in self.bulk_api_result["upserted"]) diff --git a/test/test_bulk.py b/test/test_bulk.py index 7f30ad23c..c346ac0a2 100644 --- a/test/test_bulk.py +++ b/test/test_bulk.py @@ -22,7 +22,7 @@ from bson import InvalidDocument, SON from bson.objectid import ObjectId from bson.py3compat import string_type from pymongo import MongoClient -from pymongo.bulk import * +from pymongo.options import * from pymongo.common import partition_node from pymongo.errors import BulkWriteError, InvalidOperation, OperationFailure from pymongo.write_concern import WriteConcern diff --git a/test/test_collection.py b/test/test_collection.py index bfee55b42..1e8aae6cd 100644 --- a/test/test_collection.py +++ b/test/test_collection.py @@ -35,7 +35,7 @@ from bson.son import SON from pymongo import (ASCENDING, DESCENDING, GEO2D, GEOHAYSTACK, GEOSPHERE, HASHED, TEXT) from pymongo import MongoClient -from pymongo.collection import Collection, ReturnDocument, InsertOneResult +from pymongo.collection import Collection from pymongo.command_cursor import CommandCursor from pymongo.cursor import EXHAUST from pymongo.errors import (ConfigurationError, @@ -46,7 +46,9 @@ from pymongo.errors import (ConfigurationError, InvalidOperation, OperationFailure, WTimeoutError) +from pymongo.options import ReturnDocument from pymongo.read_preferences import ReadPreference +from pymongo.results import InsertOneResult from pymongo.son_manipulator import SONManipulator from pymongo.write_concern import WriteConcern from test.test_client import IntegrationTest