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:
1 | 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:
1 | 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:
1 | 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:
1 2 3 4 5 | # 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:
1 2 | 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:
1 2 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # 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:
1 2 | # 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:
1 2 3 4 5 6 | 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:
1 | {'_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:
1 2 3 4 5 6 7 8 | # 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:
1 2 3 4 5 | 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
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:
1 2 3 4 5 6 7 8 9 | # 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:
1 2 | # 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:
1 2 3 4 5 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | #!/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!
Try Fully-Managed CockroachDB, Elasticsearch, MongoDB, PostgreSQL (Beta) or Redis.
Get Started