I want to create a Schema.statics.random
function that gets me a random element from the collection. I know there is an example for the native MongoDB driver, b
You can use aggregate:
User.aggregate([
{$match: {gender: "male"}},
{$sample: {size: 10}}
], function(err, docs) {
console.log(docs);
});
Or you can use npm package https://www.npmjs.com/package/mongoose-simple-random
User.findRandom({gender: "male"}, {}, {limit: 10}, function(err, results) {
console.log(results); // 10 elements
});
For people looking at this in times of async/await, promises etc.:
MySchema.statics.random = async function() {
const count = await this.count();
const rand = Math.floor(Math.random() * count);
const randomDoc = await this.findOne().skip(rand);
return randomDoc;
};
A shorter and maybe more performant solution
(we don't iterate through the collection once to count and a second time to skip elements, but mongoose might do that behind the scenes):
Use aggregate and $sample:
Model.aggregate([{ $sample: { size: 1 } }])
I found this Mongoose Schema static function in a GitHub Gist, which should achieve what you are after. It counts number of documents in the collection and then returns one document after skipping a random amount.
QuoteSchema.statics.random = function(callback) {
this.count(function(err, count) {
if (err) {
return callback(err);
}
var rand = Math.floor(Math.random() * count);
this.findOne().skip(rand).exec(callback);
}.bind(this));
};
Source: https://gist.github.com/3453567
NB I modified the code a bit to make it more readable.
I've implemented a plugin for mongoose that does this in a very efficient way using a $near query on two randomly generated coordinates using a 2dsphere index. Check it out here: https://github.com/matomesc/mongoose-random.
If you are not wanting to add "test like" code into your schema, this uses Mongoose queries.
Model.count().exec(function(err, count){
var random = Math.floor(Math.random() * count);
Model.findOne().skip(random).exec(
function (err, result) {
// result is random
});
});