DEV Community

Rittwick Bhabak
Rittwick Bhabak

Posted on

Introduction to MongoDB

1 Introduction to MongoDB

MongoDB is a NoSQL Database.
Instead of storing data in the form of table and rows, in mongoDB data in stored in the form of objects.
Mongoose is ODM, which makes it easier to communicate with MongoDB.

In this series we will learn about
MongoDB and Mongoose
CRUD application
and a testing framework called Mocha.

2 Installing MongoDB Locally & connecting to MongoDB

  1. npm init -- to track the dependencies.
  2. Install mongoose npm install mongoose --save
  3. Connecting to database: Mongoose do not automatically knows about database. we've to tell it explicitly.
//connection.js
const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost/testaroo');

mongoose.connection.once('open', function(){
    console.log('Connection is made');
}).on('error', function(error){
    console.log('Connection error', error);
})
Enter fullscreen mode Exit fullscreen mode

3 Models and Collection

File: models/marioChar.js

const mongoose = require('mongoose');

const Schema = mongoose.Schema;

const MarioCharSchema = new Schema({
    name: String,
    weight: Number
})

const MarioChar = mongoose.model('mariochar', MarioCharSchema)

module.exports = MarioChar;

// Now we can do the following
// var myChar = new MarioChar({ name: "Rayan", weight: 100 });
Enter fullscreen mode Exit fullscreen mode

4 Introduction to Mocha testing

Mocha is a testing framework.
We use it to perform within our application
To make sure everything works correctly
First install Mocha npm install mocha --save
Keep the test script demo_test.js to a folder /test.
demo_test.js

const mocha = require('mocha');
const assert = require('assert');

describe('Some demo tests', function(){
    //Create tests
    it('adds two numbers together', function(){
        assert(2+3===5)
    })
})
Enter fullscreen mode Exit fullscreen mode

Set the test property to mocha in package.json
and run npm run test

5 Saving data to MongoDB

const mocha = require('mocha');
const assert = require('assert');
const MarioChar = require('../models/mariochar');

describe('Some demo tests', function(){

    //Create tests
    it('Saves a record to the database', function(done){
        var character = new MarioChar({
            name: "Mario",
        })
        character.save().then(function(){
            assert(character.isNew === false)
            done();
        })
    })
})
Enter fullscreen mode Exit fullscreen mode
character.save().then(function(){
        assert(character.isNew === false)
        done();
})
Enter fullscreen mode Exit fullscreen mode

The above function saves the character in the database.
character.isNew returns true when the object is created but not present in the database.
and returns false when the object is found in the database.
As save is an asynchronous function, to perform next tests we have to explicitly tell when it is finished through done method.

Using ES6 promise instead using mogoose's own promise

const mongoose = require('mongoose');

// Using ES6 promise instead mogoose's promise
mongoose.Promise = global.Promise 

// Connect to mongodb
mongoose.connect('mongodb://localhost/testaroo');
mongoose.connection.once('open', function(){
    ...
}).on('error', function(error){
  ...  
})
Enter fullscreen mode Exit fullscreen mode

At this point our test is being ran even before the connection is established.
To do that we have to put our connection code inside before

before(function(done){
     mongoose.connect('mongodb://localhost/testaroo');
    mongoose.connection.once('open', function(){
        console.log('Conneciton is made');
        done();
    }).on('error', function(error){
        console.log('Connection error', error);
    })
})
Enter fullscreen mode Exit fullscreen mode

Note that we've also used done to explicitly tell when the connection is established.

6 Dropping a Collection

To drop a collection:
mongoose.connection.collections.mariochars.drop()
note that we're writing mariochars (pluralized) and not mariochar but we've declared the collection as mariochar. This is the way mongodb works.

To drop the collection before every test so that the result of one test do not affect the other test by using beforeEach

const mongoose = require('mongoose');mongoose.Promise = global.Promise 
before(function(done){
    // Connect to mongodb
    ...
})
beforeEach(function(done){
    mongoose.connection.collections.mariochars.drop()
    done()
})
Enter fullscreen mode Exit fullscreen mode

7 Finding Records

To search we can use find or findOne method to the model.

MarioChar.findOne({ name: "Mario"}).then(result => {
    ...
})
Enter fullscreen mode Exit fullscreen mode

This code, finds the first match where name==="Mario"
Our finding_test.js file is

const assert = require('assert');
const MarioChar = require('../models/mariochar');

describe('Some demo tests', function(){

    beforeEach(function(done){
        var character = new MarioChar({
            name: "Mario",
        })
        character.save().then(function(){
            assert(character.isNew === false)
            done();
        })
    })

    //Create tests
    it('Finding a record form the database', function(done){
        MarioChar.findOne({ name: "Mario"}).then(result => {
            assert(result.name === "Mario")
            done();
        })
    })
})
Enter fullscreen mode Exit fullscreen mode

8 ObjectId

To retrieve data by ID from the collection:

MarioChar.findOne({ _id: character._id }).then(result => {
    assert(result._id === character._id)
    done();
})
Enter fullscreen mode Exit fullscreen mode

But the above code is not going to work as
result._id===character._id is going to return false as
result._id is of type ObjectID and character._id is of type String. So we've to convert both of them to String and then compare.
result._id.toString()===character._id.toString()
will return true

9 Deleting Record

Mongoose has 3 records to delete records

  • char.remove()
  • MarioChar.remove() Inside the bracket we are going to pass the options
  • MarioChar.findOneAndRemove() Inside the bracket we're going to pass the options

Use of findOneAndRemove()

MarioChar.findOneAndRemove({ name:"Mario" }).then(function(){
            MarioChar.findOne({ name:"Mario" })
        })
Enter fullscreen mode Exit fullscreen mode

10 Updating Record

Various mongoose methods

  • char.update()
  • MarioChar.update()
  • MarioChar.findOneAndUpdate()
// Saving the data
character = new MarioChar({
    name: "Mario",
    weight: 60
})
character.save().then(function(){
    ...
})

// Updating the data
MarioChar.findOneAndUpdate({ name:"Mario"}, { name:"new_name"}).then(function(){
    MarioChar.findOne({ name:"Mario" }).then(result => {
        ...
    })
})
Enter fullscreen mode Exit fullscreen mode

The the new changes will be injected and this will not overwrite the complete objects. This update results to the object:
{ name: "new_name", weight: 100 } and not just { name: "new_name" }

11 Relational Data

Suppose, Author(name, age, books) and Book(title, pages) are two relations.
In the following way we construct it in MongoDB

const mongoose = require('mongoose');

const Schema = mongoose.Schema;

const BookSchema = new Schema({
    title: String,
    pages: Number 
});

const AuthorSchema = new Schema({
    name: String,
    age: Number, 
    books: [BookSchema] 
})

const Author = mongoose.model('author', AuthorSchema);

module.exports = Author;
Enter fullscreen mode Exit fullscreen mode

books: [BookSchema] tells us that books property is an array where the array elements will be of the form BookSchema

12 Nesting Documents

const mongoose = require('mongoose');
const assert = require('assert')
const Author = require('../models/author');

describe('Nesting Documents', function(){

    //Create tests
    it('Create an author with sub-documents', function(done){

        var pat = new Author({
            name: 'Patrick Ruffus',
            books:[{title:'Name of the wind', pages: 400}]
        });
        pat.save().then(function(){
            Author.findOne({name:'Patrick Ruffus'}).then(function(record){
                assert(record.books.length === 1);
                done();
            })
        })
    })

    it('Add a book to an existing author', function(done){

        var rus = new Author({
            name: 'Ruskin Bond',
            books:[{title:'The eyes have it', pages: 400}]
        });
        rus.save().then(function(){
            Author.findOne({name:'Ruskin Bond'}).then(function(record){
                record.books.push({title:"7 husband", pages:200});
                record.save().then(function(){
                    Author.findOne({ name: 'Ruskin Bond' }).then(function(record){
                        assert(record.books.length===2);
                        done();
                    })
                })
            })
        })
    })
})
Enter fullscreen mode Exit fullscreen mode

Top comments (0)