Mongoose is a Node.js library that provides MongoDB object mapping features. To put it simple, it creates extensible JavaScript Object representations of the documents stored in a MongoDB instance.
It sounds a tidy bit more complicated that what it actually is, let’s get started with a few examples straight away. If you are familiar with the MongoDB in Node.js, you probably know that managing documents can become a bit cumbersome. Here’s what creating a simple user object would look like using the MongoDB driver:
var MongoClient = require('mongodb').MongoClient; var myUser = { firstName: 'John', lastName: 'Doe', email: 'john@doe.com' }; MongoClient.connect('mongodb://localhost:27017/test_db', function(err, db) { db.collection('users').insert(myUser, { w: 1 }, function(err, records) { if (err) { console.log(err); } else { console.log('Document inserted successfully'); } }); });
Schemas: A world of possibilities
Mongoose, on the other hand, provides Schemas and Models that give us more semantic ways of saving and updating data. The set-up process is longer, but it pays off in the long run:
var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost:27017/test_db'); // We define the Schema first var userSchema = mongoose.Schema({ firstName: String, lastName: String, email: String }); // Then, we create the model var User = mongoose.model('User', userSchema); // Now, we can create model instances and easily save // them to the database. Moongose will take care of collection // names etc. var joe = new User({ firstName: 'Joe', lastName: 'Doe', email: 'joe@doe.com' }); joe.save(function(err) { if (err) { console.log(err); } else { console.log('Success!'); } });
Schemas open up a world of possibilities, as we can add methods to them:
userSchema.methods.sayHi = function() { console.log('Hello! My name is ' + this.firstName + ' ' + this.lastName + '!'); };
This method is not persisted to the database, and it can be very powerful:
var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost:27017/test_db'); var userSchema = mongoose.Schema({ firstName: String, lastName: String, email: String }); userSchema.methods.sayHi = function() { console.log('Hello! My name is ' + this.firstName + ' ' + this.lastName + '!'); }; var User = mongoose.model('User', userSchema); // Assuming that a user named Joe already exists in the database: User.findOne({ name: 'Joe' }, function(err, user) { if (err) { throw new Error(err); } else { user.sayHi(); // Hello! My name is Joe Doe! } });
This can be extended to more complex cases and queries:
var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost:27017/test_db'); var userSchema = mongoose.Schema({ firstName: String, lastName: String, email: String, sex: String, age: Number }); userSchema.methods.fullName = function() { return this.firstName + ' ' + this.lastName; }; var User = mongoose.model('User', userSchema); User .find({ age: { $gt: 20, $lt: 70 }, sex: 'male' }) .sort({ age: -1 }) .limit(10) .exec(function(err, users) { if (err) { throw new Error(err); } else { console.log('The 10 oldest users between the ages of 21 and 69 are:'); users.forEach(function(user) { console.log(user.fullName()); }); } });
You get the picture, it’s quite powerful and a great tool to have available. The MongoDB driver for Node.js is also very powerful, but your code becomes more convoluted and hard to understand as your application grows. Proper object mapping is an absolute must once a certain point is reached, and refactoring your whole application at this point is a not-so-entertaining task to put it mildly.
Having a well defined scheme architecture in place before starting development is a practice that will save you time and headaches.
[ecko_contrast]This is just a scratch in the surface though, Mongoose provides an amazing array of features. You can take a look at the API docs here.[/ecko_contrast]