-
Notifications
You must be signed in to change notification settings - Fork 20
DOCSP-41887 Async Migration Guide #103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
c29250e
7a49198
5e2d5a5
ca264a9
e36034b
bc1a8a3
3e9acc3
edfff73
3045f0d
6f728ce
301ba14
6637b87
102c829
afc9ddf
bf38a06
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,358 @@ | ||
.. _pymongo-async-migration: | ||
|
||
=================================== | ||
Migrate to the {+driver-async+} Driver | ||
=================================== | ||
|
||
.. contents:: On this page | ||
:local: | ||
:backlinks: none | ||
:depth: 2 | ||
:class: singlecol | ||
|
||
.. facet:: | ||
:name: genre | ||
:values: reference | ||
|
||
.. meta:: | ||
:keywords: motor, async, refactor, migration | ||
|
||
Overview | ||
-------- | ||
|
||
In September 2024, MongoDB released the {+driver-async+} driver to unify {+driver-short+} | ||
and `Motor <https://www.mongodb.com/docs/drivers/motor/>`__, the asynchronous | ||
MongoDB driver for Python. In this guide, you can identify the changes you must | ||
make to migrate an application from {+driver-short+} or Motor to the | ||
{+driver-async+} driver. | ||
|
||
Migrate From {+driver-short+} | ||
-------------------- | ||
|
||
The {+driver-async+} driver behaves similarly to {+driver-short+}, but | ||
all methods that perform network operations are coroutines and must be awaited. | ||
To migrate from {+driver-short+} to {+driver-async+}, you must update your code | ||
in the following ways: | ||
|
||
- Replace all uses of ``MongoClient`` with ``AsyncMongoClient``. | ||
- Add the ``await`` keyword to all asynchronous method calls . | ||
jordan-smith721 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- If an asynchronous method is called within a function, mark the function as ``async``. | ||
|
||
The following sections describe how to implement the asynchronous API. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd also link to the official Python async docs here for some additional context. |
||
|
||
rustagir marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Asynchronous Methods | ||
~~~~~~~~~~~~~~~~~~~~ | ||
|
||
The following tables list the asynchronous methods that are available in the | ||
{+driver-async+} driver. To call these methods, you must ``await`` them and call them | ||
inside an ``async`` function. | ||
|
||
Client Methods | ||
`````````````` | ||
|
||
.. list-table:: | ||
:header-rows: 1 | ||
:widths: 20 80 | ||
|
||
* - Method | ||
- Example | ||
|
||
* - ``AsyncMongoClient()`` | ||
- .. code-block:: python | ||
|
||
from pymongo import AsyncMongoClient | ||
|
||
client = AsyncMongoClient(...) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. S: perhaps this should have its own subsection as its a constructor rather than a method? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about that at first, but it seemed weird to be in its own table and it's related to the client so I figured it made sense here. Can change if anyone has strong feelings the other way though |
||
|
||
* - ``watch()`` | ||
- .. code-block:: python | ||
|
||
async with await client.watch(...) as stream: | ||
... | ||
jordan-smith721 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
* - ``server_info()`` | ||
- .. code-block:: python | ||
|
||
info = await client.server_info(...) | ||
jordan-smith721 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
* - ``list_databases()`` | ||
- .. code-block:: python | ||
|
||
databases = await client.list_databases() | ||
|
||
* - ``list_database_names()`` | ||
- .. code-block:: python | ||
|
||
database_names = await client.list_database_names() | ||
|
||
* - ``drop_database()`` | ||
- .. code-block:: python | ||
|
||
await client.drop_database(...) | ||
|
||
Database Methods | ||
```````````````` | ||
|
||
.. list-table:: | ||
:header-rows: 1 | ||
:widths: 20 80 | ||
|
||
* - Method | ||
- Example | ||
|
||
* - ``watch()`` | ||
- .. code-block:: python | ||
|
||
async with await db.watch(...) as stream: | ||
... | ||
jordan-smith721 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
* - ``create_collection()`` | ||
- .. code-block:: python | ||
|
||
await db.create_collection(...) | ||
|
||
* - ``aggregate()`` | ||
- .. code-block:: python | ||
|
||
async with await client.admin.aggregate(...) as cursor: | ||
... | ||
jordan-smith721 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
* - ``command()`` | ||
- .. code-block:: python | ||
|
||
result = await db.command(...) | ||
|
||
* - ``cursor_command()`` | ||
- .. code-block:: python | ||
|
||
curr = await db.cursor_command(...) | ||
|
||
* - ``list_collections()`` | ||
- .. code-block:: python | ||
|
||
collections = await db.list_collections() | ||
|
||
* - ``list_collection_names()`` | ||
- .. code-block:: python | ||
|
||
collection_names = await db.list_collection_names() | ||
|
||
* - ``drop_collection()`` | ||
- .. code-block:: python | ||
|
||
await db.drop_collection(...) | ||
|
||
* - ``validate_collection()`` | ||
- .. code-block:: python | ||
|
||
result = await db.validate_collection(...) | ||
|
||
* - ``dereference()`` | ||
- .. code-block:: python | ||
|
||
result = await db.dereference(...) | ||
|
||
Collection Methods | ||
`````````````````` | ||
|
||
.. list-table:: | ||
:header-rows: 1 | ||
:widths: 20 80 | ||
|
||
* - Method | ||
- Example | ||
|
||
* - ``watch()`` | ||
- .. code-block:: python | ||
|
||
async with await collection.watch(...) as stream: | ||
... | ||
|
||
* - ``insert_one()`` | ||
- .. code-block:: python | ||
|
||
await collection.insert_one(...) | ||
|
||
* - ``insert_many()`` | ||
- .. code-block:: python | ||
|
||
await collection.insert_many(...) | ||
|
||
* - ``replace_one()`` | ||
- .. code-block:: python | ||
|
||
await collection.replace_one(...) | ||
|
||
* - ``update_one()`` | ||
- .. code-block:: python | ||
|
||
await collection.update_one(...) | ||
|
||
* - ``update_many()`` | ||
- .. code-block:: python | ||
|
||
await collection.update_many(...) | ||
|
||
* - ``drop()`` | ||
- .. code-block:: python | ||
|
||
await collection.drop() | ||
|
||
* - ``delete_one()`` | ||
- .. code-block:: python | ||
|
||
await collection.delete_one(...) | ||
|
||
* - ``delete_many()`` | ||
- .. code-block:: python | ||
|
||
await collection.delete_many(...) | ||
|
||
* - ``find_one()`` | ||
- .. code-block:: python | ||
|
||
result = await collection.find_one(...) | ||
|
||
* - ``estimated_document_count()`` | ||
- .. code-block:: python | ||
|
||
count = await collection.estimated_document_count() | ||
|
||
* - ``count_documents()`` | ||
- .. code-block:: python | ||
|
||
count = await collection.count_documents(...) | ||
|
||
* - ``create_index()`` | ||
- .. code-block:: python | ||
|
||
await collection.create_index(...) | ||
|
||
* - ``create_indexes()`` | ||
- .. code-block:: python | ||
|
||
await collection.create_indexes(...) | ||
|
||
* - ``drop_index()`` | ||
- .. code-block:: python | ||
|
||
await collection.drop_index(...) | ||
|
||
* - ``drop_indexes()`` | ||
- .. code-block:: python | ||
|
||
await collection.drop_indexes() | ||
|
||
* - ``list_indexes()`` | ||
- .. code-block:: python | ||
|
||
indexes = await collection.list_indexes() | ||
|
||
* - ``index_information()`` | ||
- .. code-block:: python | ||
|
||
info = await collection.index_information() | ||
|
||
* - ``list_search_indexes()`` | ||
- .. code-block:: python | ||
|
||
indexes = await collection.list_search_indexes() | ||
|
||
* - ``create_search_index()`` | ||
- .. code-block:: python | ||
|
||
await collection.create_search_index(...) | ||
|
||
* - ``create_search_indexes()`` | ||
- .. code-block:: python | ||
|
||
await collection.create_search_indexes(...) | ||
|
||
* - ``drop_search_index()`` | ||
- .. code-block:: python | ||
|
||
await collection.drop_search_index(...) | ||
|
||
* - ``update_search_index()`` | ||
- .. code-block:: python | ||
|
||
await collection.update_search_index(...) | ||
|
||
* - ``options()`` | ||
- .. code-block:: python | ||
|
||
options = await collection.options() | ||
|
||
* - ``aggregate()`` | ||
- .. code-block:: python | ||
|
||
async for doc in await collection.aggregate(...): | ||
... | ||
|
||
* - ``aggregate_raw_batches()`` | ||
- .. code-block:: python | ||
|
||
async for batch in await collection.aggregate_raw_batches(...): | ||
... | ||
|
||
* - ``rename()`` | ||
- .. code-block:: python | ||
|
||
await collection.rename(...) | ||
|
||
* - ``distinct()`` | ||
- .. code-block:: python | ||
|
||
distinct_values = await collection.distinct(...) | ||
|
||
* - ``find_one_and_delete()`` | ||
- .. code-block:: python | ||
|
||
result = await collection.find_one_and_delete(...) | ||
|
||
* - ``find_one_and_replace()`` | ||
- .. code-block:: python | ||
|
||
result = await collection.find_one_and_replace(...) | ||
|
||
* - ``find_one_and_update()`` | ||
- .. code-block:: python | ||
|
||
result = await collection.find_one_and_update(...) | ||
|
||
Migrate From Motor | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to see the motor -> async pymongo migration as its own dedicated page. The pymongo sync -> async differences clutters the page for motor users. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Split the pages into two |
||
------------------ | ||
|
||
The {+driver-async+} driver behaves nearly identically to the Motor library. In | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a little misleading. Async PyMongo is (or should be) vastly superior to Motor in behavior both in terms of latency and throughput because Async Pymongo directly uses asyncio whereas Motor delegates all work to a thread pool. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated the wording here, let me know if that works better! |
||
most cases, you can directly migrate existing Motor applications to | ||
{+driver-async+} by using ``AsyncMongoClient`` in place of ``MotorClient``, and | ||
changing the application's import statements to import from ``pymongo``. | ||
|
||
The following example shows the difference in imports to use a client for | ||
read and write operations in Motor compared to {+driver-async+}: | ||
|
||
.. code-block:: python | ||
|
||
# Motor client import | ||
from motor.motor_asyncio import AsyncIOMotorClient | ||
|
||
# {+driver-async+} client import | ||
from pymongo import AsyncMongoClient | ||
|
||
|
||
The following section shows the method signature changes that you must make when | ||
migrating from Motor to the {+driver-async+} driver. | ||
jordan-smith721 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Method Signature Changes | ||
~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
The following Motor method signatures behave differently in the {+driver-async+} driver: | ||
|
||
- ``GridOut.open()`` returns ``None``. | ||
- ``AsyncMongoClient.__init__()`` does not accept an ``io_loop`` parameter. | ||
- ``GridIn.set()`` does not accept a filename. Instead pass a file name by using the | ||
``GridIn.filename`` attribute. | ||
- ``Cursor.each()`` does not exist in the {+driver-async+} driver. | ||
- ``stream_to_handler()`` does not exist in the {+driver-async+} driver. | ||
- ``to_list(0)`` is not valid in the {+driver-async+} driver. Use | ||
``to_list(None)`` instead. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
S: I think another sentence here could be useful before linking to the guide. Maybe you can mention the time period that this applies to?
Eg.
In Aug 2024, MongoDB released the PyMongo Async driver to unify PyMongo and Motor, the asynchronous MongoDB driver for Python.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Flagging this again, but also ok if you wanted to leave as is!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, misread this the first time and applied it to the wrong area. Fixed!