Sunday 24 May 2015

Mongoose tutorial with node.js and mongodb

In recent times NoSQL has gained popularity and of these mongodb is the clear favorite. But for people using relational databases with different object-relational models(ORMs) for some time might find it difficult to switch from schema-based to schema less approach. You may have tried to use mongodb but faced problems due to its lack of structure or schema.

    There are official drivers present for mongodb in different languages such as C#, C++, Java, Python, Ruby, Perl, PHP and Node.js on the mongodb website. This is how native mongodb driver for node.js can be used to connect to mongodb:

var MongoClient = require('mongo').MongoClient;

MongoClient.connect("mongodb://localhost:27017//somedb", function(err, db){
   if(!err){
      console.log("Connection Successful");
   }
});

So why not use native mongodb driver for your application, well sometimes you should but however if you need validations, associations and other high-level data modeling functions, then an Object Document Mapper may be helpful.

According to mongoose website it provides a straight-forward, schema-based solution to modeling your application data and includes built-in type casting, validation, query building, business logic hooks and more, out of the box.

Now let's take a small look at the mongoose schema. Suppose we want to store customer information in mongodb. The schema might look as below:

var mongoose = require("mongoose");

var Schema = mongoose.Schema();
var customerSchema = new Schema({
                 name: String,
                 address: String,
                 phone: Number,
                 createdOn: Date,
                 isActive: Boolean
});

First we require the mongoose to gain access to the schema object. Next we created a simple flat customer schema. The schema takes the field name and the schema type. The schema types present in mongoose are String, Date, Number, Boolean, ObjectId, Buffer(similar to Object in javascript), Array and Mixed. When you need a flexibility of data types to be stored in a single field, you can use Mixed data type for that. Let's look at a complex schema for customer information storage.

 var Schema = mongoose.Schema();

var addressSchema = new Schema({
                  city: String,
                  state: String,
                  country: String,
                  postalCode: String
});

var customerSchema = new Schema({
                 name: {
                      first: String,
                      last: String
                 },
                 address: addressSchema,
                 phone: Number,
                 createdOn: { type: Date, default: Date.now},
                 isActive: { type: Boolean, default: true}
});

Here, first we dissolved the name property into first and last fields, address becomes a nested schema and it's always a good practice to define the nested schemas separately. Mongoose also provides us with the facility to define the default value for a field. For createdOn and isActive fields we have defined an object instead of a schema type containing the type and the default value for that field. 

We can also add fields to the schema using the add method of schema object. For example,

var customerSchema = new Schema;
customerSchema.add({ name: String});

This allows you to define the schema dynamically depending on some of the known conditions.Next we will connect to the test db in mongo using the mongoose.

mongoose.connect('mongodb://localhost:27017/test');

Next we will create a model using the schema previously defined. The model function takes first argument as the model name and schema as the second. It also contains an optional third argument wherein you can name the collection to be used for this model. We can create multiple models from the schema definition. 

var Customer = mongoose.model('customer', customerSchema);

customerSchema.add({ premiumCode: Number});
var premiumCust = mongoose.model('premiumCustomer', customerSchema);

A document in mongoose is nothing but an instance of a model. This mongoose document is converted into a mongodb document which can then be stored in mongodb.

                   schema --> model --> mongoose document --> mongodb document


Taking our customer model, we now create an instance of the model below:

var Customer = mongoose.model('customer', customerSchema);
var abd = new Customer({
            name: 'AB deVilliers',
            address: 'South Africa',
            phone: 1233567890,
            isActive: true
});

Here as you can see 'abd' is an instance of the Customer model derived from the customerSchema. Next to save this document to mongodb call the save function on the document.

abd.save();

Optionally, the save function also takes a callback as an argument. This can be useful to debug the errors. The save() function with a callback is shown below:

abd.save(function (err) {
         if (err) return console.log(err);
});

Now to retrieve a document from the mongodb, mongoose provides with four most commonly used functions with the model object. These are find, findById, findOne, and where.
The signature of these methods can be seen below:

Model.find(conditions, [fields], [options], [callback])
Model.findOne(conditions, [fields], [options], [callback])
Model.findById(id, [fields], [options], [callback])
Model.where(path, [val])

Remember the Customer variable which holds the model object. We will use this variable to retrieve the documents from mongodb. Here we are using a callback which contains the retrieved data or errors if any.

//This returns all the documents from the collection that was used for the model.

Customer.find(function(err, results){
              if(err) throw err;
              console.log(results);
});


Note: If you don't give the collection name while creating your model, the model name appended with an 's' is taken as the default collection name. For example, in our case the model name is customer and thus it will be assigned to the default 'customers' collection.


//In this case we are passing in some query conditions to narrow down the results.

Customer.find({ name: 'Sachin' }, function(err, results){
});

//This will only return the specified fields from the document.

Customer.find({ name: 'Sachin' }, 'name address' function(err, results){
});


Note: To remove a field from the results, prefix the field name with a '-' sign.


//The find method without any arguments returns only the query object. Then we can call the exec method on the query object to get the results in the form of a callback.

var query = Customer.find();
query.exec(function(err, results){
});

Similarly for the findOne method, as the name suggests it will only return the first matched document.

//Returns the first document that has the name value of Sachin

Customer.findOne({ name: 'Sachin' }, function(err, results){
});

If you want to get any document based on its id, you can use the findById method.

//Returns the document with the specified id field

var id = '3485fgwtr4cnm8175890';
Customer.findById(id, function(err, results){
});


Note: We can also use the mongodb comparison query operators while retrieving the documents
Customer.find({ age: { $gte: 12 }}, function(err, results){
});


The where method works as follows. It expects a field path to be queried against and the query value
Customer.where('age').gte(12).exec(function(err, results){
              if(err) throw err;
              console.log(results);
})

You can also chain your query for retrieving documents such as:

Customer.where('age').gte(12).where('name', 'Rahul').exec(function(err, results){
              if(err) throw err;
              console.log(results);
})

Note: The path in the method signature signifies the field we are querying against. This can also be a nested field like name.first or address.city


I hope after reading this you will feel comfortable with using mongoose and mongodb.
In case you've got any doubts do comment below and I'll be happy to help :)


2 comments:

  1. This is the exact information I am been searching for, Thanks for sharing the required infos with the clear update and required points. To appreciate this I like to share some useful information

    Mongodb Training in Bangalore

    ReplyDelete