Sling Academy
Home/Node.js/How to create a fixed-size collection with MongooseJS

How to create a fixed-size collection with MongooseJS

Last updated: February 01, 2024

Introduction

Dealing with fast-growing databases can present significant challenges, particularly when you want to maintain a fixed-size collection where old data is seamlessly removed to make room for new entries. This behavior is especially useful in systems where only the most recent data is relevant, such as logging systems or caches.

This tutorial will guide you through the process of creating a fixed-size collection in MongoDB using MongooseJS, a popular ODM (Object Document Mapper) for Node.js. We’ll also explore strategies for old document removal and the use of capped collections.

Prerequisites

  • Basic knowledge of JavaScript and Node.js.
  • Node.js and npm (Node Package Manager) installed.
  • MongoDB database and MongooseJS set up in your project.

Working with Capped Collections

In MongoDB, capped collections are fixed-size collections that support high-throughput operations. They automatically remove the oldest documents when they reach the size limit. To employ this feature using Mongoose, you must define a capped collection at creation.

Step 1: Defining a Schema for a Capped Collection

const mongoose = require('mongoose');
const { Schema } = mongoose;

const logSchema = new Schema({
  // Your schema fields
}, { capped: { size: 1024, max: 5000, autoIndexId: true } });

const Log = mongoose.model('Log', logSchema);

The capped option takes an object with several parameters:

  • size – The maximum size in bytes of the collection.
  • max – The maximum number of documents allowed in the collection.
  • autoIndexId – A Boolean indicating if the _id field should be indexed. This is true by default for capped collections in MongoDB.

Note that once a capped collection has been created, modifying these capped options isn’t possible; the collection must be dropped and re-created for changes to take effect.

Step 2: Creating and Inserting Documents

const logEntry = new Log({
  message: 'This is a log message.',
  level: 'info',
  createdAt: new Date()
});

logEntry.save(err => {
  if (err) console.error(err);
  console.log('Log entry saved successfully.');
});

As you add log entries to the collection, MongoDB manages the document removal process to ensure the collection size does not exceed the specified limits.

Step 3: Handling Full Collections

When a capped collection fills up, the oldest documents will be removed as new ones come in. For many use cases, this is the desired behavior since it creates a ‘rolling’ data window. However, this also means you can potentially lose data before consuming or processing it. It is therefore crucial to anticipate this behavior in your application logic.

Using Custom Logic to Manage Fixed-size Collections

If the default behavior of capped collections does not meet your needs, you might elect to manage collection size with custom application logic.

Custom Logic Example:

Log.create({/* document data */}, (err, newLog) => {
  if (err) {
    // Handle errors
  } else {
    Log.countDocuments({}, (err, count) => {
      if (count > 5000) {
        Log.findOne().sort('createdAt').exec((err, oldest) => {
          if (oldest) oldest.remove();
        });
      }
    });
  }
});

This code manually checks the number of documents after each insertion. If the document count exceeds the threshold, it finds and removes the oldest document. This process is less efficient than using a capped collection, since it adds overhead to each write operation.

Managing Indexes and Performance

When working with fixed-size collections, indexing is vital for performance – particularly for searching and sorting operations. For capped collections, MongoDB automatically creates an index on the _id field. For custom collection management, ensure that your fields, which are commonly queried or sorted on, are properly indexed.

Conclusion

Fixed-size collections are an excellent way to manage data that has a limited shelf-life or where only the most recent records are needed. Using MongooseJS with MongoDB’s inbuilt capped collection feature, you can create fixed-size collections that automatically handle the removal of old data. However, if capped collections do not offer the flexibility your application needs, you can alternatively implement custom logic to prune older documents.

Remember to review the performance and indexing considerations as part of this process. Whether you go for the simplicity of capped collections or the fine-grained control of custom logic, you now know how to ensure your MongoDB collections remain within a manageable and predictable size.

Next Article: How to use Mongoose without a schema

Previous Article: Fixing Mongoose Error: Missing ‘cursor’ Option

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