Sling Academy
Home/Node.js/Mongoose: How to get a random document

Mongoose: How to get a random document

Last updated: December 30, 2023

Fetching a random document from a Mongoose collection is a common requirement and there are multiple ways to achieve it. This article discusses three methods, highlighting the pros and cons of each.

Aggregation Pipeline

This solution employs the aggregation framework to randomize documents. The steps are as follow:

  • Create an aggregation pipeline.
  • Add a $sample stage to the pipeline to select one random document.
  • Execute the aggregation.

Example:

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

const YourSchema = new Schema({ /* your schema definition */ });
const YourModel = mongoose.model('YourModelName', YourSchema);

async function getRandomDocument() {
  const randomDoc = await YourModel.aggregate([{ $sample: { size: 1 } }]);
  return randomDoc[0];
}

Pros: Simple and utilizes built-in MongoDB functionality.

Cons: Performance may degrade on large collections without an index. Not as random on sharded clusters.

Skip and Limit Method

This method involves skipping a random number of documents and retrieving one.

The steps:

  • Count the total number of documents.
  • Generate a random number to skip.
  • Use find() with skip() and limit() to retrieve the document.

Example:

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

const YourSchema = new Schema({ /* your schema definition */ });
const YourModel = mongoose.model('YourModelName', YourSchema);

async function getRandomDocument() {
  const count = await YourModel.countDocuments();
  const random = Math.floor(Math.random() * count);
  const randomDoc = await YourModel.findOne().skip(random).limit(1);
  return randomDoc;
}

Pros: Simple and effective for small to medium collections.

Cons: Inefficient for large collections; performance cost for skip() grows with the value.

Random Field Method

Add a field to store a random number and query based on it. Below’s the process to follow:

  • Add a random number field in your schema and fill it whenever a document is saved.
  • Generate a random number and find a document with a field value greater or equal to the random number.
  • Sort by the random field and limit the result to one document.

Code example:

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

const YourSchema = new Schema({
  /* your schema definition */,
  random: { type: Number, default: () => Math.random() }
});
YourSchema.pre('save', function (next) {
  this.random = Math.random();
  next();
});

const YourModel = mongoose.model('YourModelName', YourSchema);

async function getRandomDocument() {
  const random = Math.random();
  const randomDoc = await YourModel.findOne({ random: { $gte: random } }).sort({ random: 1 }).limit(1);
  if (randomDoc) return randomDoc;
  return YourModel.findOne().sort({ random: 1 }).limit(1);
}

Pros: Good performance even on large collections, truly random.

Cons: Requires schema modification and additional storage/maintenance for the random field.

Conclusion

When selecting a method for fetching a random document, consider your collection size and performance needs. The aggregation pipeline is best for simplicity and mostly random results. On smaller collections, skipping might be acceptable. For regular use on large collections, the random field method is likely the best approach as it offers consistent performance.

Next Article: How to Store JSON data with Mongoose

Previous Article: Mongoose: How to find and update subdocuments

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