How to Update a MongoDB Document in Python

Introduction

If you’re storing data in MongoDB, you’ll probably need to update documents from time to time. Fortunately, the PyMongo library makes it easy to get the job done with a simple Python script. Using PyMongo, your script can update a MongoDB document in a collection and have it return a results object containing the updated document in the form of a Python dictionary. In this article, we’ll show you how to update documents in MongoDB using the Update API.

Prerequisites

Before we look at some code examples, let’s take a moment to review the system requirements for this task. There are a few important prerequisites to keep in mind:

  • You must install the MongoDB server on the machine or server where your Python scripts will be running. The default port for MongoDB is 27017, but you can choose a custom port using the --port option:
mongo --port 28015
  • The API calls shown in this tutorial depend on the PyMongo Python driver module for MongoDB. You’ll need to install the library for Python 3 with the pip3 command:
pip3 install pymongo
  • You must also install the BSON library for the MongoDB ObjectIds if you plan to query and update a document using its "_id" field. You can use PIP to install the BSON library:
pip3 install bson
  • You’ll need to have a MongoDB collection on the server that contains some documents for you to update. Remember that these API calls cannot be undone, so it’s best to try out these code examples on a test collection where you’re not concerned about data loss.

Access a MongoDB collection with documents to update

At this point, let’s turn our attention to the Python code. We’ll start by importing the PyMongo library into the Python script, and then we’ll create a new client instance of the driver:

# import the MongoClient class of the PyMongo library
from pymongo import MongoClient

# create a client instance of the MongoClient class
mongo_client = MongoClient('mongodb://localhost:27017')

Now we can use the MongoDB client instance to access a database and one of its collections:

db = mongo_client.some_database
col = db["some_collection"]

Look for some MongoDB documents to update with PyMongo

Before we proceed, let’s make sure that our collection actually has some documents in it. We can use the count_documents() method to accomplish this. Be sure to pass a query in Python dictionary format to this method (or just pass {} to find all documents in the collection) or you’ll get a TypeError exception:

total_docs = col.count_documents({})
print (col.name, "has", total_docs, "total documents.")

Now that we know our collection has documents in it, we can find a document by its ObjectId using the Collection class’s find_one() method. This example uses a try-except block that can handle situations where Python doesn’t recognize the ObjectId name:

# call the Collection class's methods using the db object
try:
# pass a doc's ObjectId to find exact doc match
find_result = db["Some Collection"].find_one(
ObjectId("5cfa4c0ad9edff487939dda5")
)
except NameError as err:
find_result = None
print (err, "-- Use pip3 to install bson")
print ("Import the 'ObjectId' class from the 'bson' library")

# print the document's contents if found
if find_result != None and type(find_result) == dict:
print ("found doc:", find_result)

If the find_one() query found a document, it will print out the document’s contents as a Python dictionary.

Using MongoDB’s Update API methods in PyMongo

The Update API methods are quite similar to the Mongo Shell methods updateOne() and updateMany().

The update() method is deprecated in favor of update_one() and update_many()

If you’re familiar with the update() method used in older versions of MongoDB, please be aware that this method has been deprecated as of MongoDB version 3.x and above. Because of this, you’ll need plan ahead before writing your code to determine if the API call will be updating just one document or many.

>NOTE: The save() method is also deprecated in newer versions of MongoDB. You can use the update_one() or update_many() methods instead of save().

The MongoDB Update Operators for document fields

There are numerous MongoDB update operators that can be used to modify a document’s fields. Let’s take a closer look at these operators:

Update Operators for Setting Values:

$set — As the name implies, this operator sets the value of a field. $setOnInsert — This only updates a field’s value if the it’s a new document being inserted. $unset — This removes both the field and its value from the document.

Numerical-based Operators:

$inc — This will change the numerical value of a field by an incremental amount specified in the parameter. $min and $max — These update a document’s field only if the numerical value of that field falls within the specified min or max range. $mul — This multiplies a document’s numerical value by the specified amount.

Other Update Operators:

$currentDate — This changes the value of a field to match the current date. $rename — This simply changes a field’s name.

Find a MongoDB document using a filter query, and then update it using PyMongo

The two methods we’ll be looking at in this article require a tuple containing dictionary objects as its elements to be passed to its method call.

The first dictionary is a query, or filter, to find the document. The second dictionary’s key is the “Update Operator” that specifies how the document gets updated.

Updating a MongoDB document using find_one_and_update()

You can use the find_one_and_update() method to query a MongoDB document and update a document’s contents at the same time, and then have it return the document as a result.

Use the find_one_and_update() method to find a MongoDB document by its BSON ObjectId, and then update its contents

Although you can query a document by its BSON ObjectId, as we saw in our earlier example with the find_one() method call, the find_one_and_update() method requires that a complete dictionary object, with a key-value pair, be passed when querying a document. Therefore, you’ll need to set the key to "_id" and pass a BSON object as the query’s value. Don’t worry if that sounds a bit confusing, the example below shows that the syntax is actually quite simple:

# BSON dict for the tuple object's first element
{"_id" : ObjectId("5cfbb46d6fb0f3245fd8fd34")}

As we mentioned above, the second element in the tuple requires that you pass one of the aforementioned “Update Operators” as its dictionary key. Let’s look at an example of a find_one_and_update() method call using the "$set" operator:

doc = col.find_one_and_update(
{"_id" : ObjectId("5cfbb46d6fb0f3245fd8fd34")},
{"$set":
{"some field": "OBJECTROCKET ROCKS!!"}
},upsert=True
)

The find_one_and_update() method returns a dictionary object containing all of the MongoDB document’s data. The dictionary will contain the document’s "_id", and all of the document’s other fields, as its keys:

{'_id': ObjectId('5cfbb46d6fb0f3245fd8fd34'), 'some field': 'OBJECTROCKET ROCKS!!'}

Using the update_one() method to modify MongoDB documents

The update_one() API method works in much the same way as find_one_and_update(), except that it doesn’t return a document, but rather a results object instead.

Attributes of the PyMongo Results object returned by the update_one() method

If the API call is successful, it should return a results.UpdateResult object when updating MongoDB documents using either the update_one() or update_many() methods.

Let’s look at a few of the results object’s attributes:

# boolean confirmation that the API call went through
print ("acknowledged:", result.acknowledged)

# integer of the number of docs modified
print ("number of docs updated:", result.modified_count)

# dict object with more info on API call
print ("raw_result:", result.raw_result)

The results object provides a simple way to get specific feedback on exactly what changed in a document, as well as providing the IDs and other data for the documents that got updated or modified.

Use the update_one() method to incrementally increase a MongoDB document’s integer

In our next example, we’ll use the "$inc" key when passing parameters to the update_one() method. This will increment a specific field’s value by a number amount specified in the "$inc" key’s value:

result = db["Some Collection"].update_one(
{"num": 41}, {"$inc":
{'num': 1} # new value will be 42
}
)

This call will find a single document where a field named "num" has a value of 41. It will increment its value by 1 so that it has a new value of 42.

If no documents match the query for {"num" : 41}, ...., then the result.raw_result["nModified"] integer value returned will be 0, letting us know that no documents have been modified.

Check the result.raw_result["nModified"] value returned by a PyMongo update_one() call to update a MongoDB document

Screenshot of IDLE using PyMongo to update a MongoDB document and printing results object

Set the Upsert flag to ‘True’ to create a new document if none exists

As we mentioned in the previous section, if no documents are found that match the specified query for an update statement, no documents will be updated. However, you can use the upsert boolean option as the last element in your method call’s tuple object to change this behavior– this flag will instruct MongoDB to insert a new document if the call’s query doesn’t find a document that matches. Let’s use the upsert option in the next example:

# the document's content will change to this:
new_val = {"some text": "ObjectRocket: Database Management and Hosting"}

# pass the 'new_val' obj to the method call
result = col.update_one(
{"_id" : ObjectId("5cfbb46d6fb0f3245fd8fd34")},
{"$set": new_val},
upsert=True)

You can verify whether or not a new document _id was inserted by accessing the results.UpdateResult object’s upserted_id attribute:

# upserted_id is 'None' if ID already existed at time of call
print ("Upserted ID:", result.upserted_id)

If a new document was inserted (with a BSON ObjectId for its _id), then the results object’s upserted_id attribute should be a bson.objectid.ObjectId object; otherwise, the value of this attribute will simply be a Python NoneType object.

Use the $currentDate operator to update or insert a MongoDB field with the current date

Let’s look at an example where find_one_and_update() method call uses the $currentDate operator. This call will accomplish two things: to insert or create a MongoDB document with a date-type field (using the name "some date"), and to give that new field a date string value with the current date and time:

col.find_one_and_update(
{"_id" : ObjectId("5cfa4c0ad9edff487939dda0")},
{"$currentDate": {"some date": True}},
upsert = True
)

Note that this example uses the upsert = True option to ensure that the document will be inserted if that particular timestamp _id doesn’t exist.

The $currentDate operator returns a string in a yyyy-MM-dd HHTmm:ss.SSS format (e.g. 2019-06-08T14:09:07.247+00:00).

Conclusion

Updating a document is a common task when you’re using MongoDB to store your data, and it’s easy to get this job done with a simple Python script. With the help of the PyMongo library, you can use the update_one() and find_one_and_update() methods to easily update MongoDB documents in Python. Using the instructions provided in this tutorial, you’ll be ready to update documents with PyMongo using both of these handy methods.

Just the Code

In this article, we’ve looked at the example code section by section. Here’s the complete Python script that can be used to update a MongoDB document:

#!/usr/bin/env python3
#-*- coding: utf-8 -*-

# import the MongoClient class of the PyMongo library
from pymongo import MongoClient

# import ObjectID from MongoDB's BSON library
# (use pip3 to install bson)
from bson import ObjectId

# create a client instance of the MongoClient class
mongo_client = MongoClient('mongodb://localhost:27017')

# create database and collection instances
db = mongo_client.some_database
col = db["some_collection"]

# see if the MongoDB collection has documents on it
total_docs = col.count_documents({})
print (col.name, "has", total_docs, "total documents.")

# call the Collection class's methods using the db object
try:
# pass a doc's ObjectId to find exact doc match
find_result = db["Some Collection"].find_one(
ObjectId("5cfa4c0ad9edff487939dda5")
)
except NameError as err:
find_result = None
print (err, "-- Use pip3 to install bson")
print ("Import the 'ObjectId' class from the 'bson' library")

# print the document's contents if found
if find_result != None and type(find_result) == dict:
print ("found doc:", find_result)



# change date to current date
doc = col.find_one_and_update(
{"_id" : ObjectId("5cfa4c0ad9edff487939dda0")},
{"$currentDate": {"some date": True}},
upsert = True
)

# print the document's contents if found
if find_result != None and type(find_result) == dict:
print ("found doc:", find_result)


"
MONGODB find_one_and_update METHOD TO
UPDATE MONGODB DOCUMENTS AND RETURN
THE DOCUMENT'S CONTENT AS A DICT
"

doc = col.find_one_and_update(
{"_id" : ObjectId("5cfbb46d6fb0f3245fd8fd34")},
{"$set":
{"some field": "OBJECTROCKET ROCKS!!"}
},upsert=True
)

# print the doc dict returned by API call
if type(doc) == dict:
print ("doc dict obj:", doc)


"
MONGODB update_one METHOD TO
UPDATE MONGODB DOCUMENTS AND RETURN
A RESULTS OBJECT ABOUT THE API CALL
"

new_val = {"some text": "ObjectRocket: Database Management and Hosting"}
result = col.update_one(
{"_id" : ObjectId("5cfbb46d6fb0f3245fd8fd34")},
{"$set": new_val},
upsert=True)

# boolean confirmation that the API call went through
print ("acknowledged:", result.acknowledged)

# integer of the number of docs modified
print ("number of docs updated:", result.modified_count)

# dict object with more info on API call
print ("raw_result:", result.raw_result)


# incremental change to a MongoDB document's value
result = db["Some Collection"].update_one(
{"num": 41}, {"$inc":
{'num': 1} # new value will be 42
}
)

# dict object with more info on API call
print ("raw_result:", result.raw_result)

Pilot the ObjectRocket platform free for 30 Days

It's easy to get started. Imagine the time you'll save by not worrying about database management. Let's do this!

PILOT FREE FOR 30 DAYS

Keep in the know!

Subscribe to our emails and we’ll let you know what’s going on at ObjectRocket. We hate spam and make it easy to unsubscribe.