MongoDB is a store for JSON documents, and allows these documents to be queried and manipulated, using JSON itself as the query language.

MongoLab

MongoDB is a noSQL database, which means that it does not use traditional SQL and tables with rows and columns. Instead, it is a store for JSON documents, and allows these documents to be queried and manipulated, using JSON itself as the query language.

Running your own MongoDB cluster requires a significant amount of system administration work. You can outsource this to the cloud by using a MongoDB hosting provider. The provider MongoLAB offers free test databases that you can use to complete this practical.

MongoLab is a fully managed MongoDB-as-a-Service. It supports many of the features you expect including scalability, data protection, performance and availabilty. It also has a free sandbox feature that you can take advantage of to develop your application.

Create the database

  1. Create a free MongoLab account by following the instructions at MongoLab .
  2. Create a new database called contacts_db with the following configuration:
  3. cloud provider: Amazon, location: EU(Ireland)
  4. plan: Single Node, Sandbox (free)
  5. database name: contacts_db
  6. Select the database and click on the users tab.
  7. Create a new database user with username: test and password: ewd15.

Create a Mongo client

  1. Download Mongo from here and install on your machine.
  2. We are only going to use the mongo client in this lab. To check it’s installed, go to the command line in the bin folder and run the following command: mongo –version.
  3. To connect to your new cloud database, use the connection string indicated in MongoLAB for connection using the shell. copy it into a terminal/command line as shown below:

  4. The MongoDB command line as a Javascript console. Try the following to illustrate this:

> var foo = 1+2
> foo
3
  1. Now insert some data into the DB using the following command: ```

    db.contacts.insert({ name: "Joe Bloggs", address: "1 Bank Lane", age: 21, phone_number: "051-123456 "}) db.contacts.find() { "_id" : ObjectId("55076970a67574f02cf6a6e9"), "name" : "Joe Bloggs","age": 21, "address" : "1 Bank Lane", "phone_number" : "051-123456 " } ` The db object is provided by MongoDB and represents the current database in use. You can implicitly reference properties on the db object – for examples: db.contacts This creates a “collection”, which is like a traditional SQL table. Thusdb.contactsreferences a collection object, which has its own methods, one of which is “insert”. To insert a document into the collection, just provide a JavaScript object literal. The document can be as complex as you like, and can contain sub objects and arrays. Thefind()`` method, when used without arguments, returns a list of all documents in the collection. MongoDB automatically creates a unique identifier for each document – the ObjectID, with property _id. Go back to the mongolab website and click on the collections option to see the new contacts collection.

  2. Insert some more data:
> db.contacts.insert({ name: "Ellen Bliggs", address: "2 River Road", age:23,  phone_number: [{type: "home", number: "051 12345"},{type: "mobile", number: "086 12345"}], email: "ebliggs@wit.ie"})
> db.contacts.find()
{ "_id" : ObjectId("5507d9e158a4e53928c21182"), "name" : "Joe Bloggs", "address" : "1 Bank Lane", "age" : 21, "phone_number" : "051-123456 " }
{ "_id" : ObjectId("5507da502d443a12b40d8b71"), "name" : "Ellen Bliggs", "address" : "2 River Road", "age" : 23, "phone_number" : [ { "type" : "home", "number" : "051 12345" }, { "type" : "mobile", "number" : "086 12345" } ], "email" : "ebliggs@wit.ie" }

Unlike traditional databases, MongoDB does not require a schema – you are not required to define your data structures in advance. You can insert any document into any collection.

Querying Data

MongoDB offers an easy query language. Of course, joins are not possible! Instead you “denormalize” by replicating data where needed.

  1. To find matching documents, provide an example document:
> db.contacts.find({name:'Joe Bloggs'})
{ "_id" : ObjectId("55076970a67574f02cf6a6e9"), "name" : "Joe Bloggs","age":23,  "address" : "1 Bank Lane", "phone_number" : "051-123456 " }
  1. Query conditions are specified using the $conditionName:{criteria...}syntax. For example, to find numeric values greater than 10, use: fieldName:{$gt:10}
> db.contacts.find({age:{$gt:21}})
{ "_id" : ObjectId("5507da502d443a12b40d8b71"), "name" : "Ellen Bliggs", "address" : "2 River Road", "age" : 23, "phone_number" : [ { "type" : "home", "number" : "051 12345" }, { "type" : "mobile", "number" : "086 12345" } ], "email" : "ebliggs@wit.ie" }
> db.contacts.find({age:{$lt:22}})
{ "_id" : ObjectId("5507d9e158a4e53928c21182"), "name" : "Joe Bloggs", "address" : "1 Bank Lane", "age" : 21, "phone_number" : "051-123456 " }

The condition names are as follows: $gt = greater than, $gte = greater than or equal to, $lt = less than, $lte = less than or equal to,etc. See here for more comprehensive instructions for querying.

Sorting Data

  1. To sort data, use the chained sort method:
> db.contacts.find({}).sort({name:1})
{ "_id" : ObjectId("5507da502d443a12b40d8b71"), "name" : "Ellen Bliggs", "address" : "2 River Road", "age" : 23, "phone_number" : [ { "type" : "home", "number" : "051 12345" }, { "type" : "mobile", "number" : "086 12345" } ], "email" : "ebliggs@wit.ie" }
{ "_id" : ObjectId("5507d9e158a4e53928c21182"), "name" : "Joe Bloggs", "address" : "1 Bank Lane", "age" : 21, "phone_number" : "051-123456 " }
> db.contacts.find({}).sort({name:-1})
{ "_id" : ObjectId("5507d9e158a4e53928c21182"), "name" : "Joe Bloggs", "address" : "1 Bank Lane", "age" : 21, "phone_number" : "051-123456 " }
{ "_id" : ObjectId("5507da502d443a12b40d8b71"), "name" : "Ellen Bliggs", "address" : "2 River Road", "age" : 23, "phone_number" : [ { "type" : "home", "number" : "051 12345" }, { "type" : "mobile", "number" : "086 12345" } ], "email" : "ebliggs@wit.ie" }

If you have too many results, you can limit the number of results using the limit() function:

> db.contacts.find({}).sort({name:1}).limit(1)
{ "_id" : ObjectId("5507da502d443a12b40d8b71"), "name" : "Ellen Bliggs", "address" : "2 River Road", "age" : 23, "phone_number" : [ { "type" : "home", "number" : "051 12345" }, { "type" : "mobile", "number" : "086 12345" } ], "email" : "ebliggs@wit.ie" }

To return a subset of the document properties, use a second argument to the find function:

> db.contacts.find({}, {name:1})
{ "_id" : ObjectId("5507d9e158a4e53928c21182"), "name" : "Joe Bloggs" }
{ "_id" : ObjectId("5507da502d443a12b40d8b71"), "name" : "Ellen Bliggs" }

Updating Data

Because MongoDB does not provide traditional database ACID semantics, updates need more care than other database interactions. MongoDB does provide a set of “atomic” operations, in that these operations are guaranteed to complete before matching documents are modified by other updates. These can be used to provide certain forms of data integrity. 1. Update operations consist of a query object and then an update object, providing the new data. The following updates the entire record for Joe Bloggs:

> db.contacts.update({name:"Joe Bloggs"},{ name: "Joe Bloggs", address: "1 Bank Lane", age: 27,  phone_number: "051-123456 "})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

To update only specific properties, use $set and $unset

> db.contacts.update({name:"Joe Bloggs"},{$set:{age: 30} })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
rs-ds039311:PRIMARY> db.contacts.find({name:"Joe Bloggs"})
{ "_id" : ObjectId("5507d9e158a4e53928c21182"), "name" : "Joe Bloggs", "address" : "1 Bank Lane", "age" : 30, "phone_number" : "051-123456 " }

You can also perform “upsert” operations. In this case, a new document is created if no documents match the query. This is useful for intialising and updating counters using the $inc operator, which increments properties atomically:

> db.counter.update({name:'one'},{$inc:{count:1}},true)
> db.counter.find()
{ "_id" : ObjectId("4f3a4f9cc801a4b87bee6a12"), "count" : 1, "name" : "one" }
> db.counter.update({name:'one'},{$inc:{count:1}},true)
> db.counter.find()
{ "_id" : ObjectId("4f3a4f9cc801a4b87bee6a12"), "count" : 2, "name" : "one" }

An upsert is performed if the third argument to update is true: db.counter.update({name:'one'},{$inc:{count:1}},true)

Removing Data

Remove operations are similar to find and update in that the first argument is a pattern object to match against. All matching documents are removed. 1. Remove an object from the contacts collection

> db.contacts.remove({name:"Joe Bloggs"})

or

> db.contacts.remove({_id:ObjectId("4f3a4983199668116a823447")})

In the second script, the object is removed using its _id unique identifier. In general, MongoDB operations are most efficient if the query used the _id value to specify the object.

MongoDB and Node.js

This section requires that you have the web service from lab 7 completed. We now want to increase the functionality of the API and add integration to Mongo DB.

Set up

Create a new folder called lab8 and copy the contents from the lab7 folder that you completed last week. Make sure it works to spec. by testing the app with a Rest client.

Mongo from Node..js

To add connection capability to Mongo, we need the MongoDB package. Install this package by running npm install mongodb --save in the Lab8 folde . The save ensures that the package.json file is updated to incude this dependency. You can get plenty of information about mongodb module at http://mongodb.github.io/node-mongodb-native/. We will reuse the contacts collection from the last section. Make sure you have some data to work with in this collection.

Connecting to the cloud data store

Last week you were using an in memory collection. We will keep the same API and update the index.js file to create a connection to the MongoLab db from your API code.

// Get list of contacts
exports.index = function(req, res) {

} ;

// Creates a new contact in datastore.
exports.create = function(req, res) {

};

// Update an existing contact in datastore.
exports.update = function(req, res) {

};

// delete an existing contact in datastore.
exports.delete = function(req, res) {

};
var mongo = require('mongodb');
var BSON = mongo.BSONPure;
// get mongo client
var mongoClient = mongo.MongoClient;
var mongoDb;
mongoClient.connect("mongodb://test:ewd15@<YOUR_DOMAIN>.mongolab.com:39311/contacts_db", function(err, db) {
  if(!err) {
    console.log("We are connected");
    mongoDb = db;
  }
  else
  {
    console.log("Unable to connect to the db");
  }
});

This code uses the MongoDB module to create a connection to your database. We will also use the BSON object, binary-encoded serialization of JSON-like documents, to encode, decode the Mongo ID. For this to work, You will need to update this script so that it connects to your Mongo DB. To do this, get the connection information from MongoLab. See here for more info. - Now run the service to check if the connection is working. You should see the following in the terminal, command line:

Get Contact Details

We need to provide the data access logic for each route, modify index.js index function to use the data contained in the Mongo database. Place the following code in the index function:

      // Connect to the db
    if (mongoDb){
      var collection = mongoDb.collection('contacts');
      collection.find().toArray(function(err, items) {
        res.send(items);
      });
    }
    else
    {
        console.log('No database object!');
    }

This code checks the mongoDb exists and, if so, executes a find() query. We chain the toArray() function after the find() to covert the resonse to an array. This is then returned in the response (i.e. res.send(items) Test with your Rest client by running a HTTP GET request. You should see something similar to the following in the response:

Create a Contact

Modify the create function to create data contained in the Mongo database. Place the following code in the create function:

    var contact = req.body;
    console.log('Adding contact: ' + JSON.stringify(contact));
    if (mongoDb){
      var collection = mongoDb.collection('contacts');
      collection.insert(contact, {w:1}, function(err, result) {
            if (err) {
                res.send({'error':'An error has occurred'});
            } else {
                console.log('Success: ' + JSON.stringify(result[0]));
                res.send(result[0]);
            }
        });
    }
  else
  {
    console.log('No database object!');
  }

Test that the create contact works by posting the following contact JSON document to the service using a HTTP POST in the Rest Client:

{"name":"Bob Hope","address":"2 Bob Road", "age":55, "phone_number": [{"type":"home","number":"051 12345"}, {"type":"mobile","number":"086 12345"}], "email":"bhope@wit.ie"}

All going well, you should see a new contact record in your database. Check this by running a HTTP GET request.

Update a Contact

  var id = req.params.id;
    var contact = req.body;
    console.log('Updating contact: ' + id);
    console.log(JSON.stringify(contact));
    var collection = mongoDb.collection('contacts');
    collection.update({'_id':new BSON.ObjectID(id)}, contact, {safe:true}, function(err, result) {
            if (err) {
                console.log('Error updating contact: ' + err);
                res.send({'error':'An error has occurred'});
            } else {
                console.log('' + result + ' document(s) updated');
                res.send(contact);
            }
    });

Test this works as before by updating the last contact(Bob Hope) you entered to the following:

{"name":"Bob Hope","address":"22 Bob Road","age":55,"phone_number":[{"type":"home","number":"051 12345"},{"type":"mobile","number":"086 12345"}],"email":"bhope@wit.ie"}

Delete a Contact

    var id = req.params.id;
    console.log('Deleting contact: ' + id);
    var collection = mongoDb.collection('contact');
    collection.remove({'_id':new BSON.ObjectID(id)}, {safe:true},     function(err, result) {
        if (err) {
            res.send({'error':'An error has occurred - ' + err});
        } else {
            console.log('' + result + ' document(s) deleted');
            res.send(req.body);
        }
    });

Again, check this works using the Rest client.

Enhancing the Service.

Enhance the API functions to provide the following: - Get a contacts details.