996 lines
38 KiB
ReStructuredText
996 lines
38 KiB
ReStructuredText
:orphan:
|
|
|
|
.. _pymongo4-migration-guide:
|
|
|
|
PyMongo 4 Migration Guide
|
|
=========================
|
|
|
|
.. testsetup::
|
|
|
|
from pymongo import MongoClient, ReadPreference
|
|
|
|
client = MongoClient()
|
|
database = client.my_database
|
|
collection = database.my_collection
|
|
|
|
PyMongo 4.0 brings a number of improvements as well as some backward breaking
|
|
changes. This guide provides a roadmap for migrating an existing application
|
|
from PyMongo 3.x to 4.x or writing libraries that will work with both
|
|
PyMongo 3.x and 4.x.
|
|
|
|
PyMongo 3
|
|
---------
|
|
|
|
The first step in any successful migration involves upgrading to, or
|
|
requiring, at least that latest version of PyMongo 3.x. If your project has a
|
|
requirements.txt file, add the line "pymongo >= 3.12, < 4.0" until you have
|
|
completely migrated to PyMongo 4. Most of the key new methods and options from
|
|
PyMongo 4.0 are backported in PyMongo 3.12 making migration much easier.
|
|
|
|
.. note:: Users of PyMongo 2.X who wish to upgrade to 4.x must first upgrade
|
|
to PyMongo 3.x by following the `PyMongo 3 Migration Guide
|
|
<https://pymongo.readthedocs.io/en/3.12.1/migrate-to-pymongo3.html>`_.
|
|
|
|
Python 3.6+
|
|
-----------
|
|
|
|
PyMongo 4.0 drops support for Python 2.7, 3.4, and 3.5. Users who wish to
|
|
upgrade to 4.x must first upgrade to Python 3.6.2+. Users upgrading from
|
|
Python 2 should consult `Python 3 <https://www.mongodb.com/docs/languages/python/pymongo-driver/current/reference/upgrade/#upgrade-pymongo-versions>`_.
|
|
|
|
Enable Deprecation Warnings
|
|
---------------------------
|
|
|
|
:exc:`DeprecationWarning` is raised by most methods removed in PyMongo 4.0.
|
|
Make sure you enable runtime warnings to see where deprecated functions and
|
|
methods are being used in your application::
|
|
|
|
python -Wd <your application>
|
|
|
|
Warnings can also be changed to errors::
|
|
|
|
python -Wd -Werror <your application>
|
|
|
|
.. note:: Not all deprecated features raise :exc:`DeprecationWarning` when
|
|
used. See `Removed features with no migration path`_.
|
|
|
|
MongoReplicaSetClient
|
|
---------------------
|
|
|
|
Removed :class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`.
|
|
Since PyMongo 3.0, ``MongoReplicaSetClient`` has been identical to
|
|
:class:`pymongo.mongo_client.MongoClient`. Applications can simply replace
|
|
``MongoReplicaSetClient`` with :class:`pymongo.mongo_client.MongoClient` and
|
|
get the same behavior.
|
|
|
|
MongoClient
|
|
-----------
|
|
|
|
.. _pymongo4-migration-direct-connection:
|
|
|
|
``directConnection`` defaults to False
|
|
......................................
|
|
|
|
``directConnection`` URI option and keyword argument to :class:`~pymongo
|
|
.mongo_client.MongoClient` defaults to ``False`` instead of ``None``,
|
|
allowing for the automatic discovery of replica sets. This means that if you
|
|
want a direct connection to a single server you must pass
|
|
``directConnection=True`` as a URI option or keyword argument.
|
|
|
|
If you see any :exc:`~pymongo.errors.ServerSelectionTimeoutError`'s after upgrading from PyMongo 3 to 4.x, you likely
|
|
need to add ``directConnection=True`` when creating the client.
|
|
Here are some example errors:
|
|
|
|
.. code-block::
|
|
|
|
pymongo.errors.ServerSelectionTimeoutError: mongo_node2: [Errno 8] nodename nor servname
|
|
provided, or not known,mongo_node1:27017
|
|
|
|
.. code-block::
|
|
|
|
ServerSelectionTimeoutError: No servers match selector "Primary()", Timeout: 30s,
|
|
Topology Description: ...
|
|
|
|
|
|
Additionally, the "isWritablePrimary" attribute of a hello command sent back by the server will
|
|
always be True if ``directConnection=False``::
|
|
|
|
>>> client.admin.command('hello')['isWritablePrimary']
|
|
True
|
|
|
|
|
|
The waitQueueMultiple parameter is removed
|
|
..........................................
|
|
|
|
Removed the ``waitQueueMultiple`` keyword argument to
|
|
:class:`~pymongo.mongo_client.MongoClient` and removed
|
|
:exc:`pymongo.errors.ExceededMaxWaiters`. Instead of using
|
|
``waitQueueMultiple`` to bound queuing, limit the size of the thread
|
|
pool in your application.
|
|
|
|
The socketKeepAlive parameter is removed
|
|
..........................................
|
|
|
|
Removed the ``socketKeepAlive`` keyword argument to
|
|
:class:`~pymongo.mongo_client.MongoClient`. PyMongo now always enables TCP
|
|
keepalive. For more information see the `documentation <https://mongodb.com/docs/manual/faq/diagnostics/#does-tcp-keepalive-time-affect-mongodb-deployments->`_.
|
|
|
|
Renamed URI options
|
|
...................
|
|
|
|
Several deprecated URI options have been renamed to the standardized
|
|
option names defined in the
|
|
`URI options specification <https://github.com/mongodb/specifications/blob/master/source/uri-options/uri-options.md>`_.
|
|
The old option names and their renamed equivalents are summarized in the table
|
|
below. Some renamed options have different semantics from the option being
|
|
replaced as noted in the 'Migration Notes' column.
|
|
|
|
+--------------------+-------------------------------+--------------------------------------------------------+
|
|
| Old URI Option | Renamed URI Option | Migration Notes |
|
|
+====================+===============================+========================================================+
|
|
| ssl_pem_passphrase | tlsCertificateKeyFilePassword | - |
|
|
+--------------------+-------------------------------+--------------------------------------------------------+
|
|
| ssl_ca_certs | tlsCAFile | - |
|
|
+--------------------+-------------------------------+--------------------------------------------------------+
|
|
| ssl_crlfile | tlsCRLFile | - |
|
|
+--------------------+-------------------------------+--------------------------------------------------------+
|
|
| ssl_match_hostname | tlsAllowInvalidHostnames | ``ssl_match_hostname=True`` is equivalent to |
|
|
| | | ``tlsAllowInvalidHostnames=False`` and vice-versa. |
|
|
+--------------------+-------------------------------+--------------------------------------------------------+
|
|
| ssl_cert_reqs | tlsAllowInvalidCertificates | Instead of ``ssl.CERT_NONE``, ``ssl.CERT_OPTIONAL`` |
|
|
| | | and ``ssl.CERT_REQUIRED``, the new option expects |
|
|
| | | a boolean value - ``True`` is equivalent to |
|
|
| | | ``ssl.CERT_NONE``, while ``False`` is equivalent to |
|
|
| | | ``ssl.CERT_REQUIRED``. |
|
|
+--------------------+-------------------------------+--------------------------------------------------------+
|
|
| ssl_certfile | tlsCertificateKeyFile | Instead of using ``ssl_certfile`` and ``ssl_keyfile`` |
|
|
| | | to specify the certificate and private key files |
|
|
+--------------------+ | respectively, use ``tlsCertificateKeyFile`` to pass |
|
|
| ssl_keyfile | | a single file containing both the client certificate |
|
|
| | | and the private key. |
|
|
+--------------------+-------------------------------+--------------------------------------------------------+
|
|
| j | journal | - |
|
|
+--------------------+-------------------------------+--------------------------------------------------------+
|
|
| wtimeout | wTimeoutMS | - |
|
|
+--------------------+-------------------------------+--------------------------------------------------------+
|
|
|
|
MongoClient.fsync is removed
|
|
............................
|
|
|
|
Removed :meth:`pymongo.mongo_client.MongoClient.fsync`. Run the
|
|
`fsync command`_ directly with :meth:`~pymongo.database.Database.command`
|
|
instead. For example::
|
|
|
|
client.admin.command('fsync', lock=True)
|
|
|
|
.. _fsync command: https://mongodb.com/docs/manual/reference/command/fsync/
|
|
|
|
MongoClient.unlock is removed
|
|
.............................
|
|
|
|
Removed :meth:`pymongo.mongo_client.MongoClient.unlock`. Run the
|
|
`fsyncUnlock command`_ directly with
|
|
:meth:`~pymongo.database.Database.command` instead. For example::
|
|
|
|
client.admin.command('fsyncUnlock')
|
|
|
|
.. _fsyncUnlock command: https://mongodb.com/docs/manual/reference/command/fsyncUnlock/
|
|
|
|
MongoClient.is_locked is removed
|
|
................................
|
|
|
|
Removed :attr:`pymongo.mongo_client.MongoClient.is_locked`. Run the
|
|
`currentOp command`_ directly with
|
|
:meth:`~pymongo.database.Database.command` instead. For example::
|
|
|
|
is_locked = client.admin.command('currentOp').get('fsyncLock')
|
|
|
|
.. _currentOp command: https://mongodb.com/docs/manual/reference/command/currentOp/
|
|
|
|
MongoClient.database_names is removed
|
|
.....................................
|
|
|
|
Removed :meth:`pymongo.mongo_client.MongoClient.database_names`. Use
|
|
:meth:`~pymongo.mongo_client.MongoClient.list_database_names` instead. Code like
|
|
this::
|
|
|
|
names = client.database_names()
|
|
|
|
can be changed to this::
|
|
|
|
names = client.list_database_names()
|
|
|
|
MongoClient.max_bson_size/max_message_size/max_write_batch_size are removed
|
|
...........................................................................
|
|
|
|
Removed :attr:`pymongo.mongo_client.MongoClient.max_bson_size`,
|
|
:attr:`pymongo.mongo_client.MongoClient.max_message_size`, and
|
|
:attr:`pymongo.mongo_client.MongoClient.max_write_batch_size`. These helpers
|
|
were incorrect when in ``loadBalanced=true mode`` and ambiguous in clusters
|
|
with mixed versions. Use the `hello command`_ to get the authoritative
|
|
value from the remote server instead. Code like this::
|
|
|
|
max_bson_size = client.max_bson_size
|
|
max_message_size = client.max_message_size
|
|
max_write_batch_size = client.max_write_batch_size
|
|
|
|
can be changed to this::
|
|
|
|
doc = client.admin.command('hello')
|
|
max_bson_size = doc['maxBsonObjectSize']
|
|
max_message_size = doc['maxMessageSizeBytes']
|
|
max_write_batch_size = doc['maxWriteBatchSize']
|
|
|
|
.. _hello command: https://mongodb.com/docs/manual/reference/command/hello/
|
|
|
|
MongoClient.event_listeners and other configuration option helpers are removed
|
|
..............................................................................
|
|
|
|
The following client configuration option helpers are removed:
|
|
- :attr:`pymongo.mongo_client.MongoClient.event_listeners`.
|
|
- :attr:`pymongo.mongo_client.MongoClient.max_pool_size`.
|
|
- :attr:`pymongo.mongo_client.MongoClient.max_idle_time_ms`.
|
|
- :attr:`pymongo.mongo_client.MongoClient.local_threshold_ms`.
|
|
- :attr:`pymongo.mongo_client.MongoClient.server_selection_timeout`.
|
|
- :attr:`pymongo.mongo_client.MongoClient.retry_writes`.
|
|
- :attr:`pymongo.mongo_client.MongoClient.retry_reads`.
|
|
|
|
These helpers have been replaced by
|
|
:attr:`pymongo.mongo_client.MongoClient.options`. Code like this::
|
|
|
|
client.event_listeners
|
|
client.local_threshold_ms
|
|
client.server_selection_timeout
|
|
client.max_pool_size
|
|
client.min_pool_size
|
|
client.max_idle_time_ms
|
|
|
|
can be changed to this::
|
|
|
|
client.options.event_listeners
|
|
client.options.local_threshold_ms
|
|
client.options.server_selection_timeout
|
|
client.options.pool_options.max_pool_size
|
|
client.options.pool_options.min_pool_size
|
|
client.options.pool_options.max_idle_time_seconds
|
|
|
|
.. _tz_aware_default_change:
|
|
|
|
``tz_aware`` defaults to ``False``
|
|
..................................
|
|
|
|
The ``tz_aware`` argument to :class:`~bson.json_util.JSONOptions`
|
|
now defaults to ``False`` instead of ``True``. :meth:`bson.json_util.loads`
|
|
now decodes datetime as naive by default::
|
|
|
|
>>> from bson import json_util
|
|
>>> s = '{"dt": {"$date": "2022-05-09T17:54:00Z"}}'
|
|
>>> json_util.loads(s)
|
|
{'dt': datetime.datetime(2022, 5, 9, 17, 54)}
|
|
|
|
To retain the PyMongo 3 behavior set ``tz_aware=True``, for example::
|
|
|
|
>>> from bson import json_util
|
|
>>> opts = json_util.JSONOptions(tz_aware=True)
|
|
>>> s = '{"dt": {"$date": "2022-05-09T17:54:00Z"}}'
|
|
>>> json_util.loads(s, json_options=opts)
|
|
{'dt': datetime.datetime(2022, 5, 9, 17, 54, tzinfo=<bson.tz_util.FixedOffset object at 0x7fd1ebc1add0>)}
|
|
|
|
This change was made to match the default behavior of
|
|
:class:`~bson.codec_options.CodecOptions` and :class:`bson.decode`.
|
|
|
|
MongoClient cannot execute operations after ``close()``
|
|
.......................................................
|
|
|
|
:class:`~pymongo.mongo_client.MongoClient` cannot execute any operations
|
|
after being closed. The previous behavior would simply reconnect. However,
|
|
now you must create a new instance.
|
|
|
|
MongoClient raises exception when given more than one URI
|
|
.........................................................
|
|
|
|
:class:`~pymongo.mongo_client.MongoClient` now raises a :exc:`~pymongo.errors.ConfigurationError`
|
|
when more than one URI is passed into the ``hosts`` argument.
|
|
|
|
MongoClient raises exception when given unescaped percent sign in login info
|
|
............................................................................
|
|
|
|
:class:`~pymongo.mongo_client.MongoClient` now raises an
|
|
:exc:`~pymongo.errors.InvalidURI` exception
|
|
when it encounters unescaped percent signs in username and password.
|
|
|
|
Database
|
|
--------
|
|
|
|
Database.authenticate and Database.logout are removed
|
|
.....................................................
|
|
|
|
Removed :meth:`pymongo.database.Database.authenticate` and
|
|
:meth:`pymongo.database.Database.logout`. Authenticating multiple users
|
|
on the same client conflicts with support for logical sessions in MongoDB 3.6+.
|
|
To authenticate as multiple users, create multiple instances of
|
|
:class:`~pymongo.mongo_client.MongoClient`. Code like this::
|
|
|
|
client = MongoClient()
|
|
client.admin.authenticate('user1', 'pass1')
|
|
client.admin.authenticate('user2', 'pass2')
|
|
|
|
can be changed to this::
|
|
|
|
client1 = MongoClient(username='user1', password='pass1')
|
|
client2 = MongoClient(username='user2', password='pass2')
|
|
|
|
Alternatively, create a single user that contains all the authentication privileges
|
|
required by your application.
|
|
|
|
Database.collection_names is removed
|
|
....................................
|
|
|
|
Removed :meth:`pymongo.database.Database.collection_names`. Use
|
|
:meth:`~pymongo.database.Database.list_collection_names` instead. Code like
|
|
this::
|
|
|
|
names = client.db.collection_names()
|
|
non_system_names = client.db.collection_names(include_system_collections=False)
|
|
|
|
can be changed to this::
|
|
|
|
names = client.db.list_collection_names()
|
|
non_system_names = client.db.list_collection_names(filter={"name": {"$regex": "^(?!system\\.)"}})
|
|
|
|
Database.current_op is removed
|
|
..............................
|
|
|
|
Removed :meth:`pymongo.database.Database.current_op`. Use
|
|
:meth:`~pymongo.database.Database.aggregate` instead with the
|
|
`$currentOp aggregation pipeline stage`_. Code like
|
|
this::
|
|
|
|
ops = client.admin.current_op()['inprog']
|
|
|
|
can be changed to this::
|
|
|
|
ops = list(client.admin.aggregate([{'$currentOp': {}}]))
|
|
|
|
.. _$currentOp aggregation pipeline stage: https://mongodb.com/docs/manual/reference/operator/aggregation/currentOp/
|
|
|
|
Database.add_user is removed
|
|
............................
|
|
|
|
Removed :meth:`pymongo.database.Database.add_user` which was deprecated in
|
|
PyMongo 3.6. Use the `createUser command`_ or `updateUser command`_ instead.
|
|
To create a user::
|
|
|
|
db.command("createUser", "admin", pwd="password", roles=["dbAdmin"])
|
|
|
|
To create a read-only user::
|
|
|
|
db.command("createUser", "user", pwd="password", roles=["read"])
|
|
|
|
To change a password::
|
|
|
|
db.command("updateUser", "user", pwd="newpassword")
|
|
|
|
Or change roles::
|
|
|
|
db.command("updateUser", "user", roles=["readWrite"])
|
|
|
|
.. _createUser command: https://mongodb.com/docs/manual/reference/command/createUser/
|
|
.. _updateUser command: https://mongodb.com/docs/manual/reference/command/updateUser/
|
|
|
|
Database.remove_user is removed
|
|
...............................
|
|
|
|
Removed :meth:`pymongo.database.Database.remove_user` which was deprecated in
|
|
PyMongo 3.6. Use the `dropUser command`_ instead::
|
|
|
|
db.command("dropUser", "user")
|
|
|
|
.. _dropUser command: https://mongodb.com/docs/manual/reference/command/createUser/
|
|
|
|
Database.profiling_level is removed
|
|
...................................
|
|
|
|
Removed :meth:`pymongo.database.Database.profiling_level` which was deprecated in
|
|
PyMongo 3.12. Use the `profile command`_ instead. Code like this::
|
|
|
|
level = db.profiling_level()
|
|
|
|
Can be changed to this::
|
|
|
|
profile = db.command('profile', -1)
|
|
level = profile['was']
|
|
|
|
.. _profile command: https://mongodb.com/docs/manual/reference/command/profile/
|
|
|
|
Database.set_profiling_level is removed
|
|
.......................................
|
|
|
|
Removed :meth:`pymongo.database.Database.set_profiling_level` which was deprecated in
|
|
PyMongo 3.12. Use the `profile command`_ instead. Code like this::
|
|
|
|
db.set_profiling_level(pymongo.ALL, filter={'op': 'query'})
|
|
|
|
Can be changed to this::
|
|
|
|
res = db.command('profile', 2, filter={'op': 'query'})
|
|
|
|
Database.profiling_info is removed
|
|
..................................
|
|
|
|
Removed :meth:`pymongo.database.Database.profiling_info` which was deprecated in
|
|
PyMongo 3.12. Query the `'system.profile' collection`_ instead. Code like this::
|
|
|
|
profiling_info = db.profiling_info()
|
|
|
|
Can be changed to this::
|
|
|
|
profiling_info = list(db['system.profile'].find())
|
|
|
|
.. _'system.profile' collection: https://mongodb.com/docs/manual/reference/database-profiler/
|
|
|
|
Database.__bool__ raises NotImplementedError
|
|
............................................
|
|
:class:`~pymongo.database.Database` now raises an error upon evaluating as a
|
|
Boolean. Code like this::
|
|
|
|
if database:
|
|
|
|
Can be changed to this::
|
|
|
|
if database is not None:
|
|
|
|
You must now explicitly compare with None.
|
|
|
|
Collection
|
|
----------
|
|
|
|
The useCursor option for Collection.aggregate is removed
|
|
........................................................
|
|
|
|
Removed the ``useCursor`` option for
|
|
:meth:`~pymongo.collection.Collection.aggregate` which was deprecated in
|
|
PyMongo 3.6. The option was only necessary when upgrading from MongoDB 2.4
|
|
to MongoDB 2.6.
|
|
|
|
Collection.insert is removed
|
|
............................
|
|
|
|
Removed :meth:`pymongo.collection.Collection.insert`. Use
|
|
:meth:`~pymongo.collection.Collection.insert_one` or
|
|
:meth:`~pymongo.collection.Collection.insert_many` instead.
|
|
|
|
Code like this::
|
|
|
|
collection.insert({'doc': 1})
|
|
collection.insert([{'doc': 2}, {'doc': 3}])
|
|
|
|
Can be changed to this::
|
|
|
|
collection.insert_one({'my': 'document'})
|
|
collection.insert_many([{'doc': 2}, {'doc': 3}])
|
|
|
|
Collection.save is removed
|
|
..........................
|
|
|
|
Removed :meth:`pymongo.collection.Collection.save`. Applications will
|
|
get better performance using :meth:`~pymongo.collection.Collection.insert_one`
|
|
to insert a new document and :meth:`~pymongo.collection.Collection.update_one`
|
|
to update an existing document. Code like this::
|
|
|
|
doc = collection.find_one({"_id": "some id"})
|
|
doc["some field"] = <some value>
|
|
db.collection.save(doc)
|
|
|
|
Can be changed to this::
|
|
|
|
result = collection.update_one({"_id": "some id"}, {"$set": {"some field": <some value>}})
|
|
|
|
If performance is not a concern and refactoring is untenable, ``save`` can be
|
|
implemented like so::
|
|
|
|
def save(doc):
|
|
if '_id' in doc:
|
|
collection.replace_one({'_id': doc['_id']}, doc, upsert=True)
|
|
return doc['_id']
|
|
else:
|
|
res = collection.insert_one(doc)
|
|
return res.inserted_id
|
|
|
|
Collection.update is removed
|
|
............................
|
|
|
|
Removed :meth:`pymongo.collection.Collection.update`. Use
|
|
:meth:`~pymongo.collection.Collection.update_one`
|
|
to update a single document or
|
|
:meth:`~pymongo.collection.Collection.update_many` to update multiple
|
|
documents. Code like this::
|
|
|
|
collection.update({}, {'$set': {'a': 1}})
|
|
collection.update({}, {'$set': {'b': 1}}, multi=True)
|
|
|
|
Can be changed to this::
|
|
|
|
collection.update_one({}, {'$set': {'a': 1}})
|
|
collection.update_many({}, {'$set': {'b': 1}})
|
|
|
|
Collection.remove is removed
|
|
............................
|
|
|
|
Removed :meth:`pymongo.collection.Collection.remove`. Use
|
|
:meth:`~pymongo.collection.Collection.delete_one`
|
|
to delete a single document or
|
|
:meth:`~pymongo.collection.Collection.delete_many` to delete multiple
|
|
documents. Code like this::
|
|
|
|
collection.remove({'a': 1}, multi=False)
|
|
collection.remove({'b': 1})
|
|
|
|
Can be changed to this::
|
|
|
|
collection.delete_one({'a': 1})
|
|
collection.delete_many({'b': 1})
|
|
|
|
Collection.find_and_modify is removed
|
|
.....................................
|
|
|
|
Removed :meth:`pymongo.collection.Collection.find_and_modify`. Use
|
|
:meth:`~pymongo.collection.Collection.find_one_and_update`,
|
|
:meth:`~pymongo.collection.Collection.find_one_and_replace`, or
|
|
:meth:`~pymongo.collection.Collection.find_one_and_delete` instead.
|
|
Code like this::
|
|
|
|
updated_doc = collection.find_and_modify({'a': 1}, {'$set': {'b': 1}})
|
|
replaced_doc = collection.find_and_modify({'b': 1}, {'c': 1})
|
|
deleted_doc = collection.find_and_modify({'c': 1}, remove=True)
|
|
|
|
Can be changed to this::
|
|
|
|
updated_doc = collection.find_one_and_update({'a': 1}, {'$set': {'b': 1}})
|
|
replaced_doc = collection.find_one_and_replace({'b': 1}, {'c': 1})
|
|
deleted_doc = collection.find_one_and_delete({'c': 1})
|
|
|
|
Collection.count and Cursor.count is removed
|
|
............................................
|
|
|
|
Removed :meth:`pymongo.collection.Collection.count` and
|
|
:meth:`pymongo.cursor.Cursor.count`. Use
|
|
:meth:`~pymongo.collection.Collection.count_documents` or
|
|
:meth:`~pymongo.collection.Collection.estimated_document_count` instead.
|
|
Code like this::
|
|
|
|
ntotal = collection.count({})
|
|
nmatched = collection.count({'price': {'$gte': 10}})
|
|
# Or via the Cursor.count api:
|
|
ntotal = collection.find({}).count()
|
|
nmatched = collection.find({'price': {'$gte': 10}}).count()
|
|
|
|
Can be changed to this::
|
|
|
|
ntotal = collection.estimated_document_count()
|
|
nmatched = collection.count_documents({'price': {'$gte': 10}})
|
|
|
|
.. note:: When migrating from :meth:`count` to :meth:`count_documents`
|
|
the following query operators must be replaced:
|
|
|
|
+-------------+--------------------------------------------------------------+
|
|
| Operator | Replacement |
|
|
+=============+==============================================================+
|
|
| $where | `$expr`_ |
|
|
+-------------+--------------------------------------------------------------+
|
|
| $near | `$geoWithin`_ with `$center`_; i.e. |
|
|
| | ``{'$geoWithin': {'$center': [[<x>,<y>], <radius>]}}`` |
|
|
+-------------+--------------------------------------------------------------+
|
|
| $nearSphere | `$geoWithin`_ with `$centerSphere`_; i.e. |
|
|
| | ``{'$geoWithin': {'$centerSphere': [[<x>,<y>], <radius>]}}`` |
|
|
+-------------+--------------------------------------------------------------+
|
|
|
|
.. _$expr: https://mongodb.com/docs/manual/reference/operator/query/expr/
|
|
.. _$geoWithin: https://mongodb.com/docs/manual/reference/operator/query/geoWithin/
|
|
.. _$center: https://mongodb.com/docs/manual/reference/operator/query/center/
|
|
.. _$centerSphere: https://mongodb.com/docs/manual/reference/operator/query/centerSphere/
|
|
|
|
Collection.initialize_ordered_bulk_op and initialize_unordered_bulk_op is removed
|
|
.................................................................................
|
|
|
|
Removed :meth:`pymongo.collection.Collection.initialize_ordered_bulk_op`
|
|
and :class:`pymongo.bulk.BulkOperationBuilder`. Use
|
|
:meth:`pymongo.collection.Collection.bulk_write` instead. Code like this::
|
|
|
|
batch = coll.initialize_ordered_bulk_op()
|
|
batch.insert({'a': 1})
|
|
batch.find({'a': 1}).update_one({'$set': {'b': 1}})
|
|
batch.find({'a': 2}).upsert().replace_one({'b': 2})
|
|
batch.find({'a': 3}).remove()
|
|
result = batch.execute()
|
|
|
|
Can be changed to this::
|
|
|
|
coll.bulk_write([
|
|
InsertOne({'a': 1}),
|
|
UpdateOne({'a': 1}, {'$set': {'b': 1}}),
|
|
ReplaceOne({'a': 2}, {'b': 2}, upsert=True),
|
|
DeleteOne({'a': 3}),
|
|
])
|
|
|
|
Collection.initialize_unordered_bulk_op is removed
|
|
..................................................
|
|
|
|
Removed :meth:`pymongo.collection.Collection.initialize_unordered_bulk_op`.
|
|
Use :meth:`pymongo.collection.Collection.bulk_write` instead. Code like this::
|
|
|
|
batch = coll.initialize_unordered_bulk_op()
|
|
batch.insert({'a': 1})
|
|
batch.find({'a': 1}).update_one({'$set': {'b': 1}})
|
|
batch.find({'a': 2}).upsert().replace_one({'b': 2})
|
|
batch.find({'a': 3}).remove()
|
|
result = batch.execute()
|
|
|
|
Can be changed to this::
|
|
|
|
coll.bulk_write([
|
|
InsertOne({'a': 1}),
|
|
UpdateOne({'a': 1}, {'$set': {'b': 1}}),
|
|
ReplaceOne({'a': 2}, {'b': 2}, upsert=True),
|
|
DeleteOne({'a': 3}),
|
|
], ordered=False)
|
|
|
|
Collection.group is removed
|
|
...........................
|
|
|
|
Removed :meth:`pymongo.collection.Collection.group`. This method was
|
|
deprecated in PyMongo 3.5. MongoDB 4.2 removed the group command.
|
|
Use :meth:`~pymongo.collection.Collection.aggregate` with the ``$group`` stage
|
|
instead.
|
|
|
|
Collection.map_reduce and Collection.inline_map_reduce are removed
|
|
..................................................................
|
|
|
|
Removed :meth:`pymongo.collection.Collection.map_reduce` and
|
|
:meth:`pymongo.collection.Collection.inline_map_reduce`.
|
|
Migrate to :meth:`~pymongo.collection.Collection.aggregate` or run the
|
|
`mapReduce command`_ directly with :meth:`~pymongo.database.Database.command`
|
|
instead. For more guidance on this migration see:
|
|
|
|
- https://mongodb.com/docs/manual/reference/map-reduce-to-aggregation-pipeline/
|
|
- https://mongodb.com/docs/manual/reference/aggregation-commands-comparison/
|
|
|
|
.. _mapReduce command: https://mongodb.com/docs/manual/reference/command/mapReduce/
|
|
|
|
Collection.ensure_index is removed
|
|
..................................
|
|
|
|
Removed :meth:`pymongo.collection.Collection.ensure_index`. Use
|
|
:meth:`~pymongo.collection.Collection.create_index` or
|
|
:meth:`~pymongo.collection.Collection.create_indexes` instead. Note that
|
|
``ensure_index`` maintained an in memory cache of recently created indexes
|
|
whereas the newer methods do not. Applications should avoid frequent calls
|
|
to :meth:`~pymongo.collection.Collection.create_index` or
|
|
:meth:`~pymongo.collection.Collection.create_indexes`. Code like this::
|
|
|
|
def persist(self, document):
|
|
collection.ensure_index('a', unique=True)
|
|
collection.insert_one(document)
|
|
|
|
Can be changed to this::
|
|
|
|
def persist(self, document):
|
|
if not self.created_index:
|
|
collection.create_index('a', unique=True)
|
|
self.created_index = True
|
|
collection.insert_one(document)
|
|
|
|
Collection.reindex is removed
|
|
.............................
|
|
|
|
Removed :meth:`pymongo.collection.Collection.reindex`. Run the
|
|
`reIndex command`_ directly instead. Code like this::
|
|
|
|
>>> result = database.my_collection.reindex()
|
|
|
|
can be changed to this::
|
|
|
|
>>> result = database.command('reIndex', 'my_collection')
|
|
|
|
.. _reIndex command: https://mongodb.com/docs/manual/reference/command/reIndex/
|
|
|
|
The modifiers parameter is removed
|
|
..................................
|
|
|
|
Removed the ``modifiers`` parameter from
|
|
:meth:`~pymongo.collection.Collection.find`,
|
|
:meth:`~pymongo.collection.Collection.find_one`,
|
|
:meth:`~pymongo.collection.Collection.find_raw_batches`, and
|
|
:meth:`~pymongo.cursor.Cursor`. Pass the options directly to the method
|
|
instead. Code like this::
|
|
|
|
cursor = coll.find({}, modifiers={
|
|
"$comment": "comment",
|
|
"$hint": {"_id": 1},
|
|
"$min": {"_id": 0},
|
|
"$max": {"_id": 6},
|
|
"$maxTimeMS": 6000,
|
|
"$returnKey": False,
|
|
"$showDiskLoc": False,
|
|
})
|
|
|
|
can be changed to this::
|
|
|
|
cursor = coll.find(
|
|
{},
|
|
comment="comment",
|
|
hint={"_id": 1},
|
|
min={"_id": 0},
|
|
max={"_id": 6},
|
|
max_time_ms=6000,
|
|
return_key=False,
|
|
show_record_id=False,
|
|
)
|
|
|
|
The hint parameter is required with min/max
|
|
...........................................
|
|
|
|
The ``hint`` option is now required when using ``min`` or ``max`` queries
|
|
with :meth:`~pymongo.collection.Collection.find` to ensure the query utilizes
|
|
the correct index. For example, code like this::
|
|
|
|
cursor = coll.find({}, min={'x', min_value})
|
|
|
|
can be changed to this::
|
|
|
|
cursor = coll.find({}, min={'x', min_value}, hint=[('x', ASCENDING)])
|
|
|
|
Collection.__bool__ raises NotImplementedError
|
|
..............................................
|
|
:class:`~pymongo.collection.Collection` now raises an error upon evaluating
|
|
as a Boolean. Code like this::
|
|
|
|
if collection:
|
|
|
|
Can be changed to this::
|
|
|
|
if collection is not None:
|
|
|
|
You must now explicitly compare with None.
|
|
|
|
Collection.find returns entire document with empty projection
|
|
.............................................................
|
|
Empty projections (eg {} or []) for
|
|
:meth:`~pymongo.collection.Collection.find`, and
|
|
:meth:`~pymongo.collection.Collection.find_one`
|
|
are passed to the server as-is rather than the previous behavior which
|
|
substituted in a projection of ``{"_id": 1}``. This means that an empty
|
|
projection will now return the entire document, not just the ``"_id"`` field.
|
|
To ensure that behavior remains consistent, code like this::
|
|
|
|
coll.find({}, projection={})
|
|
|
|
Can be changed to this::
|
|
|
|
coll.find({}, projection={"_id":1})
|
|
|
|
SONManipulator is removed
|
|
-------------------------
|
|
|
|
Removed :mod:`pymongo.son_manipulator`,
|
|
:class:`pymongo.son_manipulator.SONManipulator`,
|
|
:class:`pymongo.son_manipulator.ObjectIdInjector`,
|
|
:class:`pymongo.son_manipulator.ObjectIdShuffler`,
|
|
:class:`pymongo.son_manipulator.AutoReference`,
|
|
:class:`pymongo.son_manipulator.NamespaceInjector`,
|
|
:meth:`pymongo.database.Database.add_son_manipulator`,
|
|
:attr:`pymongo.database.Database.outgoing_copying_manipulators`,
|
|
:attr:`pymongo.database.Database.outgoing_manipulators`,
|
|
:attr:`pymongo.database.Database.incoming_copying_manipulators`, and
|
|
:attr:`pymongo.database.Database.incoming_manipulators`.
|
|
|
|
Removed the ``manipulate`` parameter from
|
|
:meth:`~pymongo.collection.Collection.find`,
|
|
:meth:`~pymongo.collection.Collection.find_one`, and
|
|
:meth:`~pymongo.cursor.Cursor`.
|
|
|
|
The :class:`pymongo.son_manipulator.SONManipulator` API has limitations as a
|
|
technique for transforming your data and was deprecated in PyMongo 3.0.
|
|
Instead, it is more flexible and straightforward to transform outgoing
|
|
documents in your own code before passing them to PyMongo, and transform
|
|
incoming documents after receiving them from PyMongo.
|
|
|
|
Alternatively, if your application uses the ``SONManipulator`` API to convert
|
|
custom types to BSON, the :class:`~bson.codec_options.TypeCodec` and
|
|
:class:`~bson.codec_options.TypeRegistry` APIs may be a suitable alternative.
|
|
For more information, see `Custom Types <https://www.mongodb.com/docs/languages/python/pymongo-driver/current/data-formats/custom-types/type-codecs/#encode-data-with-type-codecs>`_.
|
|
|
|
``SON().items()`` now returns ``dict_items`` object.
|
|
----------------------------------------------------
|
|
:meth:`~bson.son.SON.items` now returns a ``dict_items`` object rather than
|
|
a list.
|
|
|
|
``SON().iteritems()`` removed.
|
|
------------------------------
|
|
``SON.iteritems()`` now removed. Code that looks like this::
|
|
|
|
for k, v in son.iteritems():
|
|
|
|
Can now be replaced by code that looks like::
|
|
|
|
for k, v in son.items():
|
|
|
|
IsMaster is removed
|
|
-------------------
|
|
|
|
Removed :class:`pymongo.ismaster.IsMaster`.
|
|
Use :class:`pymongo.hello.Hello` instead.
|
|
|
|
NotMasterError is removed
|
|
-------------------------
|
|
|
|
Removed :exc:`~pymongo.errors.NotMasterError`.
|
|
Use :exc:`~pymongo.errors.NotPrimaryError` instead.
|
|
|
|
CertificateError is removed
|
|
---------------------------
|
|
|
|
Removed :exc:`~pymongo.errors.CertificateError`. Since PyMongo 3.0 this error
|
|
is handled internally and is never raised to the application.
|
|
|
|
pymongo.GEOHAYSTACK is removed
|
|
------------------------------
|
|
|
|
Removed :attr:`pymongo.GEOHAYSTACK`. Replace with "geoHaystack" or create a
|
|
2d index and use $geoNear or $geoWithin instead.
|
|
|
|
UUIDLegacy is removed
|
|
---------------------
|
|
|
|
Removed :class:`bson.binary.UUIDLegacy`. Use
|
|
:meth:`bson.binary.Binary.from_uuid` instead. Code like this::
|
|
|
|
uu = uuid.uuid4()
|
|
uuid_legacy = UUIDLegacy(uu)
|
|
|
|
can be changed to this::
|
|
|
|
uu = uuid.uuid4()
|
|
uuid_legacy = Binary.from_uuid(uu, PYTHON_LEGACY)
|
|
|
|
Default JSONMode changed from LEGACY to RELAXED
|
|
-----------------------------------------------
|
|
|
|
Changed the default JSON encoding representation from legacy to relaxed.
|
|
The json_mode parameter for :const:`bson.json_util.dumps` now defaults to
|
|
:const:`~bson.json_util.RELAXED_JSON_OPTIONS`.
|
|
|
|
GridFS changes
|
|
--------------
|
|
|
|
.. _removed-gridfs-checksum:
|
|
|
|
disable_md5 parameter is removed
|
|
................................
|
|
|
|
Removed the ``disable_md5`` option for :class:`~gridfs.GridFSBucket` and
|
|
:class:`~gridfs.GridFS`. GridFS no longer generates checksums.
|
|
Applications that desire a file digest should implement it outside GridFS
|
|
and store it with other file metadata. For example::
|
|
|
|
import hashlib
|
|
my_db = MongoClient().test
|
|
fs = GridFSBucket(my_db)
|
|
with fs.open_upload_stream("test_file") as grid_in:
|
|
file_data = b'...'
|
|
sha356 = hashlib.sha256(file_data).hexdigest()
|
|
grid_in.write(file_data)
|
|
grid_in.sha356 = sha356 # Set the custom 'sha356' field
|
|
|
|
Note that for large files, the checksum may need to be computed in chunks
|
|
to avoid the excessive memory needed to load the entire file at once.
|
|
|
|
Removed features with no migration path
|
|
---------------------------------------
|
|
|
|
cursor_manager support is removed
|
|
.................................
|
|
|
|
Removed :class:`pymongo.cursor_manager.CursorManager`,
|
|
:mod:`pymongo.cursor_manager`, and
|
|
:meth:`pymongo.mongo_client.MongoClient.set_cursor_manager`.
|
|
|
|
MongoClient.close_cursor is removed
|
|
...................................
|
|
|
|
Removed :meth:`pymongo.mongo_client.MongoClient.close_cursor` and
|
|
:meth:`pymongo.mongo_client.MongoClient.kill_cursors`. Instead, close cursors
|
|
with :meth:`pymongo.cursor.Cursor.close` or
|
|
:meth:`pymongo.command_cursor.CommandCursor.close`.
|
|
|
|
.. _killCursors command: https://mongodb.com/docs/manual/reference/command/killCursors/
|
|
|
|
Database.eval, Database.system_js, and SystemJS are removed
|
|
...........................................................
|
|
|
|
Removed :meth:`~pymongo.database.Database.eval`,
|
|
:data:`~pymongo.database.Database.system_js` and
|
|
:class:`~pymongo.database.SystemJS`. The eval command was deprecated in
|
|
MongoDB 3.0 and removed in MongoDB 4.2. There is no replacement for eval with
|
|
MongoDB 4.2+.
|
|
|
|
However, on MongoDB <= 4.0, code like this::
|
|
|
|
>>> result = database.eval('function (x) {return x;}', 3)
|
|
|
|
can be changed to this::
|
|
|
|
>>> from bson.code import Code
|
|
>>> result = database.command('eval', Code('function (x) {return x;}'), args=[3]).get('retval')
|
|
|
|
Database.error, Database.last_status, Database.previous_error, and Database.reset_error_history are removed
|
|
...........................................................................................................
|
|
|
|
Removed :meth:`pymongo.database.Database.error`,
|
|
:meth:`pymongo.database.Database.last_status`,
|
|
:meth:`pymongo.database.Database.previous_error`, and
|
|
:meth:`pymongo.database.Database.reset_error_history`.
|
|
These methods are obsolete: all MongoDB write operations use an acknowledged
|
|
write concern and report their errors by default. These methods were
|
|
deprecated in PyMongo 2.8.
|
|
|
|
Collection.parallel_scan is removed
|
|
...................................
|
|
|
|
Removed :meth:`~pymongo.collection.Collection.parallel_scan`. MongoDB 4.2
|
|
removed the parallelCollectionScan command. There is no replacement.
|
|
|
|
pymongo.message helpers are removed
|
|
...................................
|
|
|
|
Removed :meth:`pymongo.message.delete`, :meth:`pymongo.message.get_more`,
|
|
:meth:`pymongo.message.insert`, :meth:`pymongo.message.kill_cursors`,
|
|
:meth:`pymongo.message.query`, and :meth:`pymongo.message.update`.
|
|
|
|
|
|
Name is a required argument for pymongo.driver_info.DriverInfo
|
|
..............................................................
|
|
|
|
``name`` is now a required argument for the :class:`pymongo.driver_info.DriverInfo` class.
|
|
|
|
DBRef BSON/JSON decoding behavior
|
|
.................................
|
|
|
|
Changed the BSON and JSON decoding behavior of :class:`~bson.dbref.DBRef`
|
|
to match the behavior outlined in the `DBRef specification`_ version 1.0.
|
|
Specifically, PyMongo now only decodes a subdocument into a
|
|
:class:`~bson.dbref.DBRef` if and only if, it contains both ``$ref`` and
|
|
``$id`` fields and the ``$ref``, ``$id``, and ``$db`` fields are of the
|
|
correct type. Otherwise the document is returned as normal. Previously, any
|
|
subdocument containing a ``$ref`` field would be decoded as a
|
|
:class:`~bson.dbref.DBRef`.
|
|
|
|
.. _DBRef specification: https://github.com/mongodb/specifications/blob/master/source/dbref/dbref.md
|
|
|
|
Encoding a UUID raises an error by default
|
|
..........................................
|
|
|
|
The default ``uuid_representation`` for :class:`~bson.codec_options.CodecOptions`,
|
|
:class:`~bson.json_util.JSONOptions`, and
|
|
:class:`~pymongo.mongo_client.MongoClient` has been changed from
|
|
:data:`bson.binary.UuidRepresentation.PYTHON_LEGACY` to
|
|
:data:`bson.binary.UuidRepresentation.UNSPECIFIED`. Attempting to encode a
|
|
:class:`uuid.UUID` instance to BSON or JSON now produces an error by default.
|
|
If you were using UUIDs previously, you will need to set your ``uuid_representation`` to
|
|
:data:`bson.binary.UuidRepresentation.PYTHON_LEGACY` to avoid data corruption. If you do not have UUIDs,
|
|
then you should set :data:`bson.binary.UuidRepresentation.STANDARD`. If you do not explicitly set a value,
|
|
you will receive an error like this when attempting to encode a :class:`uuid.UUID`::
|
|
|
|
ValueError: cannot encode native uuid.UUID with UuidRepresentation.UNSPECIFIED. UUIDs can be manually converted...
|
|
|
|
See `Handling UUIDs <https://www.mongodb.com/docs/languages/python/pymongo-driver/current/data-formats/uuid/#universally-unique-ids--uuids->`_ for details.
|
|
|
|
Additional BSON classes implement ``__slots__``
|
|
...............................................
|
|
|
|
:class:`~bson.int64.Int64`, :class:`~bson.min_key.MinKey`,
|
|
:class:`~bson.max_key.MaxKey`, :class:`~bson.timestamp.Timestamp`,
|
|
:class:`~bson.regex.Regex`, and :class:`~bson.dbref.DBRef` now implement
|
|
``__slots__`` to reduce memory usage. This means that their attributes are fixed, and new
|
|
attributes cannot be added to the object at runtime.
|