Sling Academy
Home/Node.js/Mongoose Upsert: Update if Exists, Insert if Not

Mongoose Upsert: Update if Exists, Insert if Not

Last updated: December 30, 2023

This concise, example-based article will walk you through a few different approaches to do upsert with Mongoose (update if exist, insert if not).

Solution 1: Using Model.findOneAndUpdate()

This approach leverages the findOneAndUpdate() method provided by Mongoose, combined with the upsert option set to true. By using this function, Mongoose will attempt to find an existing document matching given criteria. If it exists, it updates it; if not, it will insert a new document.

TL;DR:

  1. Define the query for locating the document.
  2. Specify the update operations.
  3. Set the upsert option to true.

Code example:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true, useUnifiedTopology: true });
const Schema = mongoose.Schema;
const exampleSchema = new Schema({ key: String, value: String });
const Example = mongoose.model('Example', exampleSchema);

async function upsertDocument(query, update) {
  const result = await Example.findOneAndUpdate(query, update, { upsert: true, new: true });
  return result;
}

// Example usage:
upsertDocument({ key: 'exampleKey' }, { $set: { value: 'newValue' } })
  .then(console.log)
  .catch(console.error);

Pros: Straightforward to use; built-in upsert capability.

Cons: Might not be suitable for bulk upsert operations.

Solution 2: Using Model.updateOne()

This method employs the Model.updateOne() function with the upsert option. This performs a similar operation to findOneAndUpdate but is more oriented towards scenarios where you do not need to return the document after upserting.

TL;DR:

  1. Create the filter to match the document.
  2. Prepare the update specifications.
  3. Enable the upsert feature using options.

Code example:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true, useUnifiedTopology: true });
const Schema = mongoose.Schema;
const exampleSchema = new Schema({ key: String, value: String });
const Example = mongoose.model('Example', exampleSchema);

async function upsertDocument(filter, update) {
  const result = await Example.updateOne(filter, update, { upsert: true });
  return result;
}

// Example usage:
upsertDocument({ key: 'exampleKey' }, { $set: { value: 'newValue' } })
  .then(console.log)
  .catch(console.error);

Pros: Efficient for cases not requiring the modified document.

Cons: Does not return the updated/new document.

Solution 3: Combining InsertMany with BulkWrite

To perform upsert operations in bulk, this strategy uses Mongoose’s bulkWrite method. Each operation in the array passed to bulkWrite can be customized to either update or insert.

TL;DR:

  1. Construct an array of update operations, setting upsert to true for each.
  2. Execute the bulkWrite with the array.
  3. Handle the result which includes information on how many documents were matched and upserted.

Code example:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true, useUnifiedTopology: true });
const Schema = mongoose.Schema;
const exampleSchema = new Schema({ key: String, value: String });
const Example = mongoose.model('Example', exampleSchema);

async function upsertMany(documents) {
  const operations = documents.map(doc => ({
      updateOne: {
        filter: { key: doc.key },
        update: { $set: { value: doc.value }},
        upsert: true
      }
  }));
  const result = await Example.bulkWrite(operations);
  return result;
}

// Example usage:
upsertMany([
  { key: 'exampleKey1', value: 'newValue1' },
  { key: 'exampleKey2', value: 'newValue2' }
])
  .then(console.log)
  .catch(console.error);

Pros: Ideal for bulk operations; efficient performance.

Cons: More complex syntax than single upserts.

Next Article: How to prevent injection attacks in Mongoose

Previous Article: Understanding the Mongoose exec() Function: Guide With Examples

Series: Mongoose.js Tutorials

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates