Web Development

Server Side

MongoDB

Óscar Belmonte Fernández

Universitat Jaume I

Introduction

MongoDB is a No (only) SQL database system manger.

MongoDB stores information in documents instead of tables.

Queries are written in json format.

Although No-SQL databases are schema-less, you can use schemas to describe the documents stored in the database.

Mongoose is a NodeJS package that eases the interaction with MongoDB by means of schemas.

Content

  1. Installing and using MongoDB.
  2. Mongoose.
  3. Summary.
  4. Exercises

Installing and using MongoDB

To install MongoDB is OS dependent. Take a look at Install MongoDB

MongoDB provides a shell to interact with the MongoDB manager, to lauch it, just type mongo in a console.


$ mongo
MongoDB shell version: 3.2.0
connecting to: test
                

By default, when you start the Mongo shell the test database is automatically selected. If you want to use another one type:


mongo catalog
MongoDB shell version: 3.2.0
connecting to: catalog
                

To see the databases in the MongoDB manager:


> show dbs
local    0.000GB
                

To change/create to a particular database:


> use catalog
switched to db catalog
                

To see the collections in a database:


> show collections
                

We have no products in the collection yet. Let's add the first product:


> db.products.save({"id":"1", "name":"USB drive", "price":10})
WriteResult({ "nInserted" : 1 })
>
                

To find all documents in the collection:


> db.products.find()
{ "_id" : ObjectId("568b64ed3d89d76b0731ec76"), "id" : "1", "name" : "USB drive", "price" : 10 }
                

Note the _id attribute, its an internal key for this particular document in the database.

To update an existing document in the database:


> db.products.update({"id":"1"}, {$set:{"price":11}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
                

Check the update using:


> db.products.find()
{ "_id" : ObjectId("568b64ed3d89d76b0731ec76"), "id" : "1", "name" : "USB drive", "price" : 11 }
                

Alternatively, you can try to find just one document:


> db.products.findOne();
{
    "_id" : ObjectId("568b70fc3d89d76b0731ec77"),
    "id" : "1",
    "name" : "USB drive",
    "price" : 10
}
                

To remove a particular document from the database:


> db.products.remove({"id":"1"})
WriteResult({ "nRemoved" : 1 })
                

Try to find it out again:


> db.products.find()
                

No documents are reported now.

The basic CRUD operations in MongoDB are:

OperationMongoDB command
Createdb.collection.save()
Retrievedb.collection.find()
Updatedb.collection.update()
Deletedb.collection.remove()

You can remove an entire database:


use catalog
db.dropDatabase()
                

You can quit the mongo console typing:


exit
                

You can check a complete introduction in Getting started with MongoDB.

Mongoose

Mongoose is an object modelling framework for MongoDB.

We define schemas in Mongoose that ease to write code.

First, let's install it in our application:


$npm install --save mongoose
                

Now, we need a connection to the MongoDB database in NodeJS.

To organize the code of our project, let's create a new folder in our products express project, name it model. Create a file named db.js and type the following code in it:


var mongoose = require("mongoose");

mongoose.connect("mongodb://localhost/catalog");
var db = mongoose.connection;
db.on("error", console.log.bind(console, "connection error."));
db.on("open", function () {
    console.log("Mongodb connected");
});

module.exports = mongoose;
                

Note the last code line, this means that we are exposing the mongoose variable to be used in any other nodejs file.

Now, let's define the schema for our products. Create a new file, name it as product.js and type in it:


var db = require("./db");

var Product = db.model('Product', {
    id: {type: String, required: true},
    name: {type: String, require: true},
    price: {type: Number, require: true}
});

module.exports = Product;
                

Note the last line of code again, we are exposing the Product variable to be used by any other nodejs file.

Let's use our MongoDB connection and schema. First, lets import the Product module, at the begining of our index.js file type:


var Product = require("./model/product")
                

Now, the code to find out a product by its id:


app.get("/catalog/:id", function(req, res, next) {
    var idProduct = req.params.id;

    Product.findOne({id: idProduct}, function(err, product) {
        if(err) {
            res.status(500);
            next("Internal server error.");
        } else if(product == null) {
            res.status(404); // Not found
            next("No product with code " + idProduct + " found.");
        } else {
            res.status(200);
            res.json(product);
        }
    });
});
                

Although the function we are using is Product.findOne(json, function(err, doc)), this is not a MongoDB function but the wrapper function by Mongoose.

You can check that it works using Postman.

Let's see how to retrieve all products in the database:


app.get("/catalog", function(req, res) {
    var stream = Product.find().stream();
    var results = {};
    stream.on('data', function(doc) {
            results[doc.id] = doc;
        }).on("error", function(err) {
            res.status(500);
            next(err);
        }).on('close', function() {
            res.status(200);
            res.json(results);
    });
});
                

Here, we are using find().stream() instead of just find().

find() returns a javascript array with al documents matching the query condition.

But, we want to return an associative map, so we need some control. We achieve this control by means of a stream.

Exercise

Re-code the REMOVE operation to delete an existing product(resource) in the database using Mongoose


app.delete("/catalog/:id", function(req, res, next) {
    ....
}
                

Hints: if you try Product.remove({"id": idProduct}, function(err, result) {

  • result.result.n must be non-zero.

Try to cover all anomalous results:

  • There is no product with a given id.
  • MongoDB returns an error.

Exercise

Re-code the CREATE operation to create a new product(resource) in the database using Mongoose.


app.post("/catalog", function(req, res, next) {
    ....
}
                

Try to cover all anomalous results:

  • A product with the same id already exists in the database (try to find it first before adding it).
  • MongoDB returns an error.

Exercise

Re-code the UPDATE operation to update an existing product(resource) in the database using Mongoose.


app.put("/catalog/:id", function(req, res, next) {
    ....
}
                

Try to cover all anomalous results:

  • The product id and the request URI do not match.
  • There is no product with the provided id.
  • MongoDB returns an error.

Summary

You are now able to code the basic CRUD operation for a web application using a NoSQL database.

All data of your web application is now persisted to a database, no piece of information will be lost.

Exercises

  1. Add the persistence layer to the GeoTODO project.