Node.js – Storing data with MongoDB

Node.js – Storing data with MongoDB

The last few weeks we have focused on web service design using Node.js with Express. So far we have covered getting started with express, using EJS to create web pages, and creating a push notification server. Today we will go over how to save data on your node project by using MongoDB.

This post will assume that you have MongoDB installed on your machine already. Check out the MongoDB Installation page if you do not have it setup.

First, let’s go over MongoDB a bit. MongoDB is a document oriented database. This allows you to just shove whatever JSON you want into it. You don’t have to declare a table structure like you do in SQL. You don’t even need to declare your collections at all. If you try to use a collection that doesn’t exist it just gets made for you immediately. Documents are stored in collections that you define, but documents within a collection don’t have to be similar at all. MongoDB also features an amazing query system. Imagine we want to store information about fruit. We could do the following:

db.fruit.insert({name : "apple", color : ["red", "yellow"], source : "tree"})

We now have a fruit collection containing one fruit: an apple. Let’s add a few more:

db.fruit.insert({name : "strawberry", color : ["red"], source : "bush"})
db.fruit.insert({name : "orange", color : ["orange"], source : "tree"})

Now we have a strawberry and an orange in our fruit collection. To get all fruits in our collection that come from trees we would do:

db.fruit.find({source : "tree"})

If we wanted to find all fruits that are red we would do:

db.fruit.find({color : "red"})

And to find all fruits that are yellow or orange we would do:

db.fruit.find({color : {$in : ["yellow", "orange"]}})

Now apples can be green too. Let’s modify our apple entry to reflect that:

db.fruit.update({name : "apple"}, {$push : {color : "green"}})

There are a few interesting things to be aware of when updating a document. By default, mongodb will only update the first entry it finds. You can set the multi option to force it to update every entry that matches the query. Also, note the $push operator I have included in the update object. That is telling mongo it will be adding an entry to an array on the document. If you left off push and just did:

db.fruit.update({name : "apple"}, {color : "green"})

You would be very disappointed. Setting no update operator will replace the document completely:

{
    "color" : "green"
}

So what if you wanted to update a field that isn’t an array? You use the $set update operator:

db.fruit.update({name : "apple"}, {$set : {name : "pear"}}

MongoDB is very powerful and an excellent document store, but it lacks the join power that SQL brings to the table. You can join documents together by their _id field using MonoDB’s populate, but it’s not nearly as fast as what you can do with a few SQL joins. I encourage you to explore MongoDB as an option for your databases, but it’s important to approach your database design differently with MongoDB than with SQL. Be sure to check out the documentation for all you can do with MongoDB.

So how to we start using Mongo with Node? There are several libraries available on npm for connecting to Mongo, but I will be focusing on Mongoskin and Mongoose.

Let’s go ahead and get Mongoskin out of the way. Mongoskin is what you want if you just want to run basic MongoDB commands and nothing more. To get started add Mongoskin as a dependency:

npm install mongoskin --save

And then in node:

var mongo = require('mongoskin');
var db = mongo.db("mongodb://localhost:27017/integration_tests", {native_parser:true});
db.collection('fruit).find({name : "orange"}).toArray(function(err, fruits){
    console.log("I have "+fruits.length+" fruits in my collection:");
    for(var i=0; i<fruits.length; i++){
        console.log(fruit.name);
    }
});

Mongoskin is very easy to use and most MongoDB commands are supported. It has its issues, but if you want something simple to connect to a mongo database look no further. If you want the best MongoDB experience you can find with Node, keep reading!

On to Mongoose. As they say on their website: “Mongoose 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.” Mongoose is a delight. To get started add Mongoose as a dependency:

npm install mongoose --save

It’s nice that MongoDB doesn’t need to have schemas, but it’s great that Mongoose gives them to you. Let’s put together a schema for our fruit collection:

var mongoose = require('mongoose');

var FruitSchema = new mongoose.Schema({
    name : String,
    color : [String],
    source : {
        type : String,
        default : 'tree'
    }
});

FruitSchema.virtual('numberOfColors').get(function(){
    return this.color.length;
});

mongoose.model('Fruit', EventSchema);

Most of the schema above should be straight forward. Name must be a string or Mongoose will remove the property from the object before using it as a model. Color must be an array of strings. Source must be a string, and if it’s not present Mongoose will automatically set it to “tree”. Default values will not get stored in the actual database, but they will be applied when Mongoose pulls them out. The virtual “numberOfColors” will add a property automatically. Virtuals are great for properties that can be calculated based on the existing data on the model.

To use the fruits model:

mongoose.model('Fruit').find({name : "orange"}, function(err, fruits){
    console.log("I have "+fruits.length+" fruits in my collection:");
    for(var i=0; i<fruits.length; i++){
        console.log(fruit.name + " comes in " + fruit.numberOfColors + " colors");
    }
});

The examples above barely scrape the surface of what you can do with MongoDB and Mongoose. Do yourself a favor and explore the possibilities of a document oriented database backed by MongoDB.