Skip to content

Instantly share code, notes, and snippets.

@anand2312
Last active May 24, 2024 09:15
Show Gist options
  • Save anand2312/840aeb3e98c3d7dbb3db8b757c1a7ace to your computer and use it in GitHub Desktop.
Save anand2312/840aeb3e98c3d7dbb3db8b757c1a7ace to your computer and use it in GitHub Desktop.
pymongo vs Motor

pymongo vs Motor

Motor is an async Python driver for MongoDB.

When should I use Motor?

You should use Motor when you're trying to interact with a MongoDB database in an asynchronous context. When you're making something that needs to be asynchronous (like a web server, or most commonly from what I've seen here, Discord bots), you also want all the database calls to be done asynchronously. But pymongo is synchronous, i.e it is blocking, and will block the execution of your asynchronous program for the time that it is talking to the database.

Okay, How do I switch now?!

Thankfully for us, switching from pymongo to Motor isn't too hard, and won't need you to change much code. This process can be roughly summarized as:

Step 1: Install Motor, and import it

Installing can be done with pip - pip install motor After this it can be imported into your file as

import motor.motor_asyncio as motor

(NOTE: The as motor part is just to avoid having to type motor.motor_asyncio everytime)

Step 2: Make a Client and get a database

You must be familiar with pymongo's MongoClient - you will switch that out with motor's equivalent class

client = motor.AsyncIOMotorClient(<connection details>)

Getting a database and a collection is similar to pymongo, (you can do client["database name"] to get the database and you'd do database["collection name"] to get the collection. Or you can also use dot-notation)

Step 3: Make the queries asynchronous

Now add an await before every query you have (except .find(), this will be explained later)

# before
something = collection.find_one({"foo": "bar"})
collection.delete_one({"foobar": 1})
collection.update_many({"foo": "bar"}, {"$set": {"foo": "foobar"}})
# after
something = await collection.find_one({"foo": "bar"})
await collection.delete_one({"foobar": 1})
await collection.update_many({"foo": "bar"}, {"$set": {"foo": "foobar"}})

And you're done! Your queries are no longer blocking the event loop :D

Sidenote; Why aren't .finds awaited?

collection.find only returns a cursor object. No real I/O operation takes place until you actually try accessing the data from the cursor. You can asynchronously loop over the contents of the cursor (with an async for loop), or use the cursor's to_list method to immedietely get all the data (now this has to be awaited!) Meaning,

data = collection.find()    # correct
data = await collection.find()    # wrong!

# retrieving data
# option 1:
async for i in data:
  # do stuff 

#option 2
as_list = await data.to_list(length=None)

to_list takes a length kwarg, which specifies how many documents to retrieve - passing None means you retrieve all documents.

Documentation

@rigens
Copy link

rigens commented May 10, 2024

There is a true asynchronous MongoDB driver for Python and asyncio: Mongojet. According to its developer, it is 4 times faster than Motor in high concurrency scenarios.

@mohit1607
Copy link

I was wondoring for why 2 libraries for mongodb in python, also after studing asynchronous behaviour, I came across this gist, Thanks really helpful

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment