Skip to content
1 change: 1 addition & 0 deletions snooty.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ sharedinclude_root = "https://raw.githubusercontent.com/10gen/docs-shared/main/"
[constants]
driver-short = "PyMongo"
driver-long = "PyMongo, the MongoDB synchronous Python driver,"
driver-async = "PyMongo Async"
language = "Python"
mdb-server = "MongoDB Server"
mongo-community = "MongoDB Community Edition"
Expand Down
7 changes: 7 additions & 0 deletions source/index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ MongoDB {+driver-short+} Documentation
/troubleshooting
/whats-new
/upgrade
/pymongo-async-migration
/previous-versions
/issues-and-help
/compatibility
Expand Down Expand Up @@ -120,6 +121,12 @@ Upgrade {+driver-short+} Versions
Learn what changes you might need to make to your application to upgrade driver versions
in the :ref:`pymongo-upgrade` section.

Migrate to the {+driver-async+} Driver
------------------------------------

Learn how to migrate from {+driver-short+} or Motor to the {+driver-async+} driver in the
Copy link
Contributor

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.

Copy link
Contributor

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!

Copy link
Collaborator Author

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!

:ref:`pymongo-async-migration` section.

Previous Versions
-----------------

Expand Down
358 changes: 358 additions & 0 deletions source/pymongo-async-migration.txt
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 .
- If an asynchronous method is called within a function, mark the function as ``async``.

The following sections describe how to implement the asynchronous API.

Choose a reason for hiding this comment

The 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.


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(...)
Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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:
...

* - ``server_info()``
- .. code-block:: python

info = await client.server_info(...)

* - ``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:
...

* - ``create_collection()``
- .. code-block:: python

await db.create_collection(...)

* - ``aggregate()``
- .. code-block:: python

async with await client.admin.aggregate(...) as cursor:
...

* - ``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
Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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
Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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.

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.
Loading