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()
withskip()
andlimit()
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.