MongoDB Upsert: Update if exists, otherwise insert new record

Updated: February 3, 2024 By: Guest Contributor Post a comment

Introduction

Understanding how to effectively manage data in a database is crucial for any developer or database administrator. In MongoDB, one common operation is upsert, which stands for ‘update if exists, otherwise insert.’ This tutorial will guide you through the process of using the upsert feature in MongoDB with various levels of complexity and scenarios. It assumes a basic understanding of MongoDB and database operations.

Basics of Upsert in MongoDB

In MongoDB, the upsert operation is a part of the ‘update’ command, which allows for the creation of a new document if no document matches the update query criteria. The term ‘upsert’ is a portmanteau of ‘update’ and ‘insert.’ To perform an upsert in MongoDB, you set the upsert option to true in an update operation.

Here’s the basic syntax for an upsert:

db.collection.update(
  { <query> }, 
  { <update> }, 
  { upsert: true }
)

In this command, <query> represents the criteria for selecting the document, <update> contains the modifications to be applied, and the option { upsert: true } is what differentiates an upsert from a regular update.

Let’s start with a basic example. Assume we have a collection called ‘products’ with documents structured as follows:

{
  "_id": ObjectId("507f1f77bcf86cd799439011"),
  "name": "Notebook",
  "quantity": 50
}
{ ... }

Now, suppose we want to update the quantity of a product named ‘Tablet.’ If the product doesn’t exist, we want to insert it. Here’s how you do that:

db.products.update(
  { name: "Tablet" },
  { $set: { quantity: 10 } },
  { upsert: true }
)

If a document with name ‘Tablet’ exists, its quantity will be updated to 10. If not, a new document with name ‘Tablet’ and quantity 10 will be inserted.

Handling Complex Documents

Upserts are not limited to simple documents. They can also work with complex, nested documents. Let’s assume our products now have a ‘details’ sub-document:

{
  "_id": ObjectId("507f..."),
  "name": "Notebook",
  "details": { "manufacturer": "XYZ Corp", "stock": 50 }
}
{ ... }

To use upsert for updating nested fields, you dot-notation. Here’s an example:

db.products.update(
  { name: "Tablet", "details.manufacturer": "ABC Inc." },
  { $set: { "details.stock": 10 } },
  { upsert: true }
)

If a ‘Tablet’ made by ‘ABC Inc.’ exists, its stock count will be updated. Otherwise, a new ‘Tablet’ with the specified details will be created.

Advanced Upsert Operations

For even more complex scenarios where multiple operations are needed during the upsert, the MongoDB aggregation pipeline (available since version 4.2) can be employed. You can specify an array of the aggregation stages that MongoDB will run on upsert.

An example to illustrate:

db.products.update(
  { name: "Gadget" },
  [
    {
      $set: {
        quantity: { $ifNull: [ "$quantity", 0 ] },
        "details.stock": { $ifNull: [ "$details.stock", 0 ] }
      }
    },
    { $addFields: { "quantity": { $add: [ "$quantity", 10 ] } } },
    { $addFields: { "details.stock": { $add: [ "$details.stock", 10 ] } } }
  ],
  { upsert: true }
)

This command looks for a product named ‘Gadget.’ If found, it increments its quantity and its details.stock by 10. If not found, it creates a new ‘Gadget’ with quantity and details.stock starting at 10 (0 + 10 from the $add operation).

Working with Upsert in Practice

Let’s now walk through a practical example where we integrate the upsert operation into a Node.js application using the MongoDB native driver. Before diving into coding, make sure you have:

  • MongoDB server running
  • Node.js installed
  • MongoDB Node.js driver installed via npm

First, set up the connection and upsert a document:

const { MongoClient } = require('mongodb');

async function main(){
  const client = new MongoClient('mongodb://localhost:27017');
  try {
    await client.connect();
    const db = client.db('shop');
    const result = await db.collection('products').updateOne(
      { name: "Smartphone" },
      { $set: { quantity: 15 } },
      { upsert: true }
    );

    console.log(result);
  } catch (e) {
    console.error(e);
  } finally {
    await client.close();
  }
}

main();

Run this script. If there’s no product named ‘Smartphone’, one will be inserted with a quantity of 15. Otherwise, the quantity of the existing ‘Smartphone’ will be updated.

As seen, the upsert capability can be very powerful when effectively used in MongoDB. By mastering it, you’ll enhance your ability to work with dynamic data in a manner that can save time and handle data conciseness.

Error Handling and Best Practices

When performing upsert operations, it’s important to consider the potential for race conditions and duplicate data. This can especially become an issue in high-concurrency environments. To mitigate such risks, it’s advisable to:

  • Use a unique index on fields that define the uniqueness of a document.
  • Handle exceptions in your application code that might occur due to these unique index constraints.
  • Consider using transactions if your operation needs to be atomic across multiple documents or collections.

The exact error handling and best practices will depend on the specific use case and operational requirements.

Conclusion

In this tutorial, we delved into the MongoDB upsert operation and explained how to use it with a variety of examples. Upserts simplify the process of maintaining robust data sets and are indispensable in scenarios where document existence is uncertain. By making use of upserts, developers can ensure data integrity with minimal effort and streamlined code.