Mongoose: Is there a way to default lean to true (always on)? - node.js

I have a read-only API that I'd like Mongoose to always have lean queries for.
Can I enable this either on a schema or connection level to be true by default?

The easiest way is to monkey patch mongoose.Query class to add default lean option:
var __setOptions = mongoose.Query.prototype.setOptions;
mongoose.Query.prototype.setOptions = function(options, overwrite) {
__setOptions.apply(this, arguments);
if (this.options.lean == null) this.options.lean = true;
return this;
};
Mongoose creates new instance of mongoose.Query for every query and setOptions call is a part of mongoose.Query construction.
By patching mongoose.Query class you'll be able to turn lean queries on globally. So you won't need to path all mongoose methods (find, findOne, findById, findOneAndUpdate, etc.).
Mongoose uses Query class for inner calls like populate. It passes original Query options to each sub-query, so there should be no problems, but be careful with this solution anyway.

A hack-around could be performed something like this:
Current way of executing the query:
Model.find().lean().exec(function (err, docs) {
docs[0] instanceof mongoose.Document // false
});
Fiddle with the Model's find method:
var findOriginal = Model.prototype.find;
Model.prototype.find = function() {
return findOriginal.apply(this, arguments).lean();
}
New way of executing the query:
Model.find().exec(function (err, docs) {
docs[0] instanceof mongoose.Document // false
});
I have not tested this code, but if you have tried to override library functionality in JavaScript before, you will easily grasp where I'm getting

I have to use My nestjs Application like this. Its perfectly working for me.
import { Query } from 'mongoose'
Query.prototype.setOptions = function(options: any) {
__setOptions.apply(this, arguments)
if (!this.mongooseOptions().lean) this.mongooseOptions().lean = true
return this
}

Refer to the above comments:
mongoose.Query.prototype.setOptions = function(options, overwrite) {
options = Object.assign({}, options);
if (!options.hasOwnProperty('lean')) {
options.lean = true;
}
__setOptions.call(this, options, overwrite);
return this;
};

If you are worried about the accepted answer by #Leonid Beschastny, the places where you should not use lean, according to Mongoose docs are:
When you modify the query result
You use custom getters or transforms
Source: https://mongoosejs.com/docs/tutorials/lean.html#when-to-use-lean

Related

Is it possible to disable automatic type casting for Mongoose SchemaTypes?

For a model with this Schema...
{
name: { type: String }
}
...the following automatically casts the provided value to a string instead of enforcing the type:
document.name = 2;
document.validate(err => {
// Err is null, document.name === '2'
})
Is there a simple way to disable this behaviour?
Just in case anyone else stumbles upon this, it looks like mongoose will be supporting this according to this issue.
you can use lean() method with your find/findOne queries.
lean() will remove all the effect a mongoose schema has, i.e. it will return data as it is saved in MongoDB without any typecasting.
Note:- After using lean() you will not be able to call update or save on that returned data.
Also, this will increase your query performance.
example
Model.find().lean().exec((err, result) => {
console.log(result); //data without any typecasting
/*some operations on result*/
result.save(); // this will not work
});

MongooseJS: Hide Specific Documents From Query

I have a User schema with a activated field of Boolean type. I want queries to only return documents which have activated: true. And I hope there is a more efficient and DRY way of doing so than adding a conditional to every find, findOne, or findById.
What would be the most effective approach?
while there may be some way to do this, it is generally a bad idea to always hide this information.
speaking from experience trying to do this with other languages and database systems, you will, at some point, want / need to load items that are not actived. but if you always and only return activated items, you'll never be able to get the list you need.
for your purposes, i would recommend creating a findActive method on your schema:
someSchema.static("findActive", function(query, cb){
// check if there is a query and callback
if (!cb){
cb = query;
query = {};
}
// set up an empty query, if there isn't one provided
if (!query) { query = {}; }
// make sure you only load activated items
query.activated = true;
// run the query
this.find(query, cb);
});
with this method, you will have a findActive method the same as findOne, but it will always filter for activated items.
MyModel.findActive(function(err, modelList){ ... });
and it optionally supports additional query filters
MyModel.findActive({some: "stuff"}, function(err, modelList){ ... });
You might want to look at Mongoose Query middleware here
Query middleware is supported for the following Model and Query
functions.
count
find
findOne
...
For example:
User.pre('find', function() {
console.log(this instanceof mongoose.Query); // true
this.activated = true;
});

What is the difference between methods and statics in Mongoose?

What is the difference between methods and statics?
Mongoose API defines statics as
Statics are pretty much the same as methods but allow for defining functions that exist directly on your Model.
What exactly does it mean? What does existing directly on models mean?
Statics code example from documentation:
AnimalSchema.statics.search = function search (name, cb) {
return this.where('name', new RegExp(name, 'i')).exec(cb);
}
Animal.search('Rover', function (err) {
if (err) ...
})
Think of a static like an "override" of an "existing" method. So pretty much directly from searchable documentation:
AnimalSchema.statics.search = function search (name, cb) {
return this.where('name', new RegExp(name, 'i')).exec(cb);
}
Animal.search('Rover', function (err) {
if (err) ...
})
And this basically puts a different signature on a "global" method, but is only applied when called for this particular model.
Hope that clears things up a bit more.
It seems like
'method' adds an instance method to documents constructed from Models
whereas
'static' adds static "class" methods to the Model itself
From the documentation:
Schema#method(method, [fn])
Adds an instance method to documents constructed from Models compiled from this schema.
var schema = kittySchema = new Schema(..);
schema.method('meow', function () {
console.log('meeeeeoooooooooooow');
})
Schema#static(name, fn)
Adds static "class" methods to Models compiled from this schema.
var schema = new Schema(..);
schema.static('findByName', function (name, callback) {
return this.find({ name: name }, callback);
});

mongoosejs perform query within methods

I'm using mongoosejs and have custom methods to populate some data during pre save. Is there a way to query the db, within methods?
For example:
UserSchema.methods.createRandom = function(callback) {
var random = 123;
this.findOne({random: random}, function(err, doc) {
if (!doc) return callback(random);
this.createRandom(callback);
});
}
UserSchema.pre('save', function(next) {
this.createRandom(function(random) {
this.random = random;
next();
});
}
This is basically what I'm trying to acheive, but this in methods does not reference the model, it references the object to be saved. Anyway to access the model for the findOne().
Thanks!
It's a bit cryptic and I don't know if it's documented anywhere, but I've reliably done this in the past by accessing an instance's model via its constructor property:
this.constructor.findOne({random: random}, function(err, doc) {

How to get all count of mongoose model?

How can I know the count of a model that data has been saved? there is a method of Model.count(), but it doesn't seem to work.
var db = mongoose.connect('mongodb://localhost/myApp');
var userSchema = new Schema({name:String,password:String});
userModel =db.model('UserList',userSchema);
var userCount = userModel.count('name');
userCount is an Object, which method called can get a real count?
Thanks
The reason your code doesn't work is because the count function is asynchronous, it doesn't synchronously return a value.
Here's an example of usage:
userModel.count({}, function( err, count){
console.log( "Number of users:", count );
})
The code below works. Note the use of countDocuments.
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/myApp');
var userSchema = new mongoose.Schema({name:String,password:String});
var userModel =db.model('userlists',userSchema);
var anand = new userModel({ name: 'anand', password: 'abcd'});
anand.save(function (err, docs) {
if (err) {
console.log('Error');
} else {
userModel.countDocuments({name: 'anand'}, function(err, c) {
console.log('Count is ' + c);
});
}
});
You should give an object as argument
userModel.countDocuments({name: "sam"});
or
userModel.countDocuments({name: "sam"}).exec(); //if you are using promise
or
userModel.countDocuments({}); // if you want to get all counts irrespective of the fields
For the older versions of mongoose, use
userModel.count({name: "sam"});
The collection.count is deprecated, and will be removed in a future version. Use collection.countDocuments or collection.estimatedDocumentCount instead.
userModel.countDocuments(query).exec((err, count) => {
if (err) {
res.send(err);
return;
}
res.json({ count: count });
});
Background for the solution
As stated in the mongoose documentation and in the answer by Benjamin, the method Model.count() is deprecated. Instead of using count(), the alternatives are the following:
Model.countDocuments(filterObject, callback)
Counts how many documents match the filter in a collection. Passing an empty object {} as filter executes a full collection scan. If the collection is large, the following method might be used.
Model.estimatedDocumentCount()
This model method estimates the number of documents in the MongoDB collection. This method is faster than the previous countDocuments(), because it uses collection metadata instead of going through the entire collection. However, as the method name suggests, and depending on db configuration, the result is an estimate as the metadata might not reflect the actual count of documents in a collection at the method execution moment.
Both methods return a mongoose query object, which can be executed in one of the following two ways. Use .exec() if you want to execute a query at a later time.
The solution
Option 1: Pass a callback function
For example, count all documents in a collection using .countDocuments():
someModel.countDocuments({}, function(err, docCount) {
if (err) { return handleError(err) } //handle possible errors
console.log(docCount)
//and do some other fancy stuff
})
Or, count all documents in a collection having a certain name using .countDocuments():
someModel.countDocuments({ name: 'Snow' }, function(err, docCount) {
//see other example
}
Option 2: Use .then()
A mongoose query has .then() so it’s “thenable”. This is for a convenience and query itself is not a promise.
For example, count all documents in a collection using .estimatedDocumentCount():
someModel
.estimatedDocumentCount()
.then(docCount => {
console.log(docCount)
//and do one super neat trick
})
.catch(err => {
//handle possible errors
})
Option 3: Use async/await
When using async/await approach, the recommended way is to use it with .exec() as it provides better stack traces.
const docCount = await someModel.countDocuments({}).exec();
Learning by stackoverflowing,
Using mongoose.js you can count documents,
count all
const count = await Schema.countDocuments();
count specific
const count = await Schema.countDocuments({ key: value });
The highest voted answers here are perfectly fine I just want to add up the use of await so that the functionality asked for can be achieved:
const documentCount = await userModel.count({});
console.log( "Number of users:", documentCount );
It's recommended to use countDocuments() over 'count()' as it will be deprecated going on. So, for now, the perfect code would be:
const documentCount = await userModel.countDocuments({});
console.log( "Number of users:", documentCount );
Model.count() method is deprecated in mongoose version 6.2.0. If you want to count the number of documents in a collection, e.g. count({}), use the estimatedDocumentCount() function instead. Otherwise, use the countDocuments() function instead.
Model.estimatedDocumentCount() Estimates the number of documents in the MongoDB collection. It is Faster than using countDocuments() for large collections because estimatedDocumentCount() uses collection metadata rather than scanning the entire collection.
Example:
const numAdventures = await Adventure.estimatedDocumentCount();
reference : https://mongoosejs.com/docs/api.html#model_Model.estimatedDocumentCount
As said before, your code will not work the way it is. A solution to that would be using a callback function, but if you think it would carry you to a 'Callback hell', you can search for "Promisses".
A possible solution using a callback function:
//DECLARE numberofDocs OUT OF FUNCTIONS
var numberofDocs;
userModel.count({}, setNumberofDocuments); //this search all DOcuments in a Collection
if you want to search the number of documents based on a query, you can do this:
userModel.count({yourQueryGoesHere}, setNumberofDocuments);
setNumberofDocuments is a separeted function :
var setNumberofDocuments = function(err, count){
if(err) return handleError(err);
numberofDocs = count;
};
Now you can get the number of Documents anywhere with a getFunction:
function getNumberofDocs(){
return numberofDocs;
}
var number = getNumberofDocs();
In addition , you use this asynchronous function inside a synchronous one by using a callback, example:
function calculateNumberOfDoc(someParameter, setNumberofDocuments){
userModel.count({}, setNumberofDocuments); //this search all DOcuments in a Collection
setNumberofDocuments(true);
}

Resources