Quick Start: Node.js & MongoDB - How to Update Documents

Lauren Schaefer

#Node.js
Quick Start Node.js and MongoDB

We’re halfway through the CRUD (create, read, update, and delete) operations in this Quick Start with MongoDB and Node.js series. In this series, we’ve worked through how to connect to a database, create documents, and read documents.

Today, let’s tackle the update operation. We’ll update a document, upsert a document, and update many documents with a single operation.

Get started with an M0 cluster on Atlas today. It's free forever, and it’s the easiest way to try out the steps in this blog series.

Update a Document

Let’s begin by updating a single Airbnb listing in the listingsAndReviews collection. If you’re new to this series, check out the earlier post where we dove into the details of how data is stored in this sample database.

We can update a single document by calling Collection’s updateOne(). updateOne() has two required parameters:

  1. filter (object): the Filter used to select the document to update. You can think of the filter as essentially the same as the query param we used in findOne() to search for a particular document. You can include zero properties in the filter to search for all documents in the collection, or you can include one or more properties to narrow your search.
  2. update (object): the update operations to be applied to the document. MongoDB has a variety of update operators you can use such as $inc, $currentDate, $set, and $unset among others. See the official documentation for a complete list of update operators and their descriptions.

updateOne() also has an optional options param. See the updateOne() docs for more information on these options.

updateOne() will update the first document that matches the given query. Even if more than one document matches the query, only one document will be updated.

Let’s say we want to update an Airbnb listing with a particular name. We can use updateOne() to achieve this. We’ll include the name of the listing in the filter param. We’ll use the $set update operator to set new values for new or existing fields in the document we are updating. When we use $set, we pass a document that contains fields and values that should be updated or created. The document that we pass to $set will not replace the existing document; any fields that are part of the original document but not part of the document we pass to $set will remain as they are.

Our function to update a listing with a particular name would look like the following:

async function updateListingByName(client, nameOfListing, updatedListing) {
    result = await client.db("sample_airbnb").collection("listingsAndReviews")
                        .updateOne({ name: nameOfListing }, { $set: updatedListing });
 
    console.log(`${result.matchedCount} document(s) matched the query criteria.`);
    console.log(`${result.modifiedCount} document(s) was/were updated.`);
}

Let’s say we want to update our Airbnb listing that has the name “Infinite Views.” We created this listing in a previous post.

{ _id: 5db6ed14f2e0a60683d8fe42,
  name: 'Infinite Views',
  summary: 'Modern home with infinite views from the infinity pool',
  property_type: 'House',
  bedrooms: 5,
  bathrooms: 4.5,
  beds: 5 }

We can call updateListingByName() by passing a connected MongoClient, the name of the listing, and an object containing the fields we want to update and/or create.

await updateListingByName(client, "Infinite Views", { bedrooms: 6, beds: 8 });

Executing this command results in the following output.

1 document(s) matched the query criteria.
1 document(s) was/were updated.

Now our listing has an updated number of bedrooms and beds.

{ _id: 5db6ed14f2e0a60683d8fe42,
  name: 'Infinite Views',
  summary: 'Modern home with infinite views from the infinity pool',
  property_type: 'House',
  bedrooms: 6,
  bathrooms: 4.5,
  beds: 8 }

Upsert a Document

One of the options you can choose to pass to updateOne() is upsert. Upsert is a handy feature that allows you to update a document if it exists or insert a document if it does not.

For example, let’s say you wanted to ensure that an Airbnb listing with a particular name had a certain number of bedrooms and bathrooms. Without upsert, you’d first use findOne() to check if the document existed. If the document existed, you’d use updateOne() to update the document. If the document did not exist, you’d use insertOne() to create the document. When you use upsert, you can combine all of that functionality into a single command.

Our function to upsert a listing with a particular name can be basically identical to the function we wrote above with one key difference: we’ll pass {upsert: true} in the options param for updateOne().

async function upsertListingByName(client, nameOfListing, updatedListing) {
    result = await client.db("sample_airbnb").collection("listingsAndReviews")
                        .updateOne({ name: nameOfListing }, 
                                   { $set: updatedListing }, 
                                   { upsert: true });
    console.log(`${result.matchedCount} document(s) matched the query criteria.`);
 
    if (result.upsertedCount > 0) {
        console.log(`One document was inserted with the id ${result.upsertedId._id}`);
    } else {
        console.log(`${result.modifiedCount} document(s) was/were updated.`);
    }
}

Let’s say we aren’t sure if a listing named “Cozy Cottage” is in our collection or, if it does exist, it may hold old data. Either way, we want to ensure the listing that exists in our collection has the most up-to-date data. We can call upsertListingByName() with a connected MongoClient, the name of the listing, and an object containing the up-to-date data that should be in the listing.

await upsertListingByName(client, "Cozy Cottage", 
                          { name: "Cozy Cottage", bedrooms: 2, bathrooms: 1 });

If the document did not previously exist, the output of the function would be something like the following:

No listings found with the name 'Cozy Cottage'
0 document(s) matched the query criteria.
One document was inserted with the id 5db9d9286c503eb624d036a1

We have a new document in the listingsAndReviews collection:

{ _id: 5db9d9286c503eb624d036a1,
  name: 'Cozy Cottage',
  bathrooms: 1,
  bedrooms: 2 }

If we discover more information about the “Cozy Cottage” listing, we can use upsertListingByName() again.

await upsertListingByName(client, "Cozy Cottage", { beds: 2 });

And we would see the following output.

1 document(s) matched the query criteria.
1 document(s) was/were updated.

Now our document has a new field named "beds."

{ _id: 5db9d9286c503eb624d036a1,
  name: 'Cozy Cottage',
  bathrooms: 1,
  bedrooms: 2,
  beds: 2 }

Update Many Documents

Sometimes you’ll want to update more than one document at a time. In this case, you can use Collection’s updateMany(). Like updateOne(), updateMany() requires that you pass a filter of type object and an update of type object. You can choose to include options of type object as well.

Let’s say we want to ensure that every document has a field named property_type. We can use the $exists query operator to search for documents where the property_type field does not exist. Then we can use the $set update operator to set the property_type to “Unknown” for those documents. Our function will look like the following.

async function updateAllListingsToHavePropertyType(client) {
    result = await client.db("sample_airbnb").collection("listingsAndReviews")
                        .updateMany({ property_type: { $exists: false } }, 
                                    { $set: { property_type: "Unknown" } });
    console.log(`${result.matchedCount} document(s) matched the query criteria.`);
    console.log(`${result.modifiedCount} document(s) was/were updated.`);
}

We can call this function with a connected MongoClient.

await updateAllListingsToHavePropertyType(client);

Below is the output from executing the previous command.

3 document(s) matched the query criteria.
3 document(s) was/were updated.

Now our “Cozy Cottage” document and all of the other documents in the Airbnb collection have the property_type field.

{ _id: 5db9d9286c503eb624d036a1,
  name: 'Cozy Cottage',
  bathrooms: 1,
  bedrooms: 2,
  beds: 2,
  property_type: 'Unknown' }

Listings that contained a property_type before we called updateMany() remain as they were. For example, the “Spectacular Modern Uptown Duplex” listing still has property_type set to Apartment.

{ _id: '582364',
  listing_url: 'https://www.airbnb.com/rooms/582364',
  name: 'Spectacular Modern Uptown Duplex',
  property_type: 'Apartment',
  room_type: 'Entire home/apt',
  bedrooms: 4,
  beds: 7
…
}

Wrapping Up

This post included many code snippets that built on code written in the first post of this MongoDB and Node.js Quick Start series. To get a full copy of the code used in today’s post, visit the Node.js Quick Start GitHub Repo.

Be on the lookout for the next post in this series where we’ll work through the final CRUD operation: delete.

Series versions

The examples in this article were created with the following application versions:

ComponentVersion used
MongoDB4.0
MongoDB Node.js Driver3.3.2
Node.js10.16.3

All posts in the Quick Start: Node.js and MongoDB series: