📜 ⬆️ ⬇️

Mongoose rake

Hacker - a man who steps on a rake, which are hidden in the barn and closed on the castle

Mongoose is the most popular javascript mongodb module. Examples on the site allow you to start using it fairly quickly and successfully, but mongoose has a number of unexpected features that can make the programmer start tearing out the hair on his head. It is about these features that I am going to tell.

1. Naming collections


I will start with the most innocuous and easily detectable features. You create a model:

var mongoose = require('mongoose'); var User = new mongoose.Schema({ email: String, password: String, data: { birthday: { type: Date, default: Date.now }, status: { type: String, default: 'active', enum: ['active', 'unactive'] }, mix: { type: mongoose.Schema.Types.Mixed, default: {} } } }); module.exports = mongoose.model('User', User); 

Create a user:

 var user = new User({email: 'test@test.com', password: '12345'}); user.save(ok(function() { console.log('ok'); })); 

If we now execute the “show collections” command in the mongodb console, we will see that the users collection has been created. Those. mongoose when creating collections brings their names to lowercase and plural.
')

2. Overriding the toJSON () Method


Let us need to modify our instance of the model by adding an attribute that is not described in the model:

 User.findOne({email: 'test@test.com'}, ok(function(user) { user.someArea = 'custom value'; console.log(user.someArea); console.log('===='); console.log(user); })); 

In the console we will see (instead of console.log, res.json may be used):

 custom value ==== { __v: 0, _id: 54fc8c22c90fb7dd025eee7c, email: 'test@test.com', password: '12345', data: { mix: {}, status: 'active', birthday: Thu Mar 12 2015 23:46:06 GMT+0300 (MSK) } } 

As you can see, the object has the attribute someArea, but when it dumped to the console, it disappeared somewhere. The fact is that mongoose overrides the toJson method and all fields not described in the model schema are discarded. A situation may arise when we add an attribute to an object and give it to the client, but the attribute does not reach the client. In order for it to successfully hit the client, it is necessary to modify not a mongoose object. For these purposes, model instances have a toObject method, which returns a native-Object, which can be modified as desired and nothing is lost from it.

3. Comparison _id


It may seem that _id is of type String, however, this is not at all the case. _id is an object and it is necessary to compare the identifiers of instances of mongoose models as objects. Example:

 User.findOne({email: 'test@test.com'}, ok(function(user1) { User.findOne({email: 'test@test.com'}, ok(function(user2) { log(user1._id == user2._id); // false log(user1._id.equals(user2._id)); // true log(user1._id.toString() == user2._id.toString()); // true })); })); 


4. Saving mixed fields


We have one field with the type mixed in the scheme, this is data.mix. If we change it, for example:

 User.findOne({email: 'test@test.com'}, ok(function(user) { user.data.mix = {msg: 'hello world'}; user.save(ok(function() { console.log('ok'); })); })); 

, then the changes will successfully fall into the database.

However, if now we make the change inside data.mix, then the changes in the database will not fall.

 User.findOne({email: 'test@test.com'}, ok(function(user) { user.data.mix.msg = 'Good bye'; user.save(ok(function() { log(user); })); })); 

The user object containing our modifications will be displayed in the console, and a query to the database will show that the user has not been changed. In order for the changes to be in the database, you need to notify mongoose before the save method that we modified the mixed-field:

 user.markModified('data.mix'); 

The same operation must be performed with objects of type Date when they are modified by the built-in methods (setMonth, setDate, ...), this is stated in the documentation

5. Defaults for arrays


Suppose, when describing the model scheme, we decided that we should have an array of objects in the field. We need to register defaults for the array itself and for all objects embedded in it. In mongoose, the special key type is used for this:

 var Lib = new mongoose.Schema({ userId: mongoose.Schema.ObjectId, images: { //           images type: [{ uploaded: { type: Date, default: Date.now }, src: String }], //  -   images default: [{uploaded: new Date(2012, 11, 22), src: '/img/default.png'}] } }); module.exports = mongoose.model('Lib', Lib); 

Similarly, using the type keyword, we can create multi-level defaults for objects.

6. Streaming update


Sometimes it is necessary to update a very large collection from code. Download the entire collection - not enough memory. You can manually set limits, load documents in batches and update, but mongoose has very convenient interfaces for this operation - streaming.

 emusers.find({}).stream() .on('data', function(user) { var me = this; me.pause(); //        user.save(function(err) { me.resume(err); }); }) .on('error', function(err) { log(err); }) .on('close', function() { log('All done'); }); 

(However, if we extract users in batches, edit and save via async.parallel, this will work a little faster, but less readable).

6. Disable automatic index building


To ensure the uniqueness of the fields in mongodb unique indexes are used. Using mongoose is very easy to create. Mongoose generally creates a high level of abstraction when working with data. However, our shortcomings are extensions of our merits and many forget to disable the automatic creation of indexes in the production mode, although the official documentation clearly states this.
For these purposes, mongoose even has a special flag {autoIndex: false}, which must be specified when describing the data scheme:

 var User = new mongoose.Schema({ email: { type: String, unique: true, required: true }, password: String }, { autoIndex: process.env('mode') == 'development' }); 

Now automatic building of indexes will work only in development mode.

7. Do not forget about reserved keys.


Probably, not everyone is faced with a similar problem, but nevertheless I’ll draw attention to the fact that in mongoose objects there is a set of reserved names for attributes, they are given in the documentation . We had to deal with the naming of attributes by keys from the list of reserved ones, after which it was necessary to scrub calls to these keys throughout the code. Mongoose does not swear at all about using reserved keys. The rake I stepped on this list of keys turned out to be the options key.

Source: https://habr.com/ru/post/253395/


All Articles