CRUD

The four basic functions of persistent storage and their corresponding mongoDB queries :

Create => insert

Read => find

Update => update

Delete => remove

Inserting doc

db.fruit.insert({name:'apple', color:'red', shape:'round'})

This query insert a document in the fruit collection.

EDIT March, 29th 2016 : You can insert one doc using insertOne() or several using insertMany(). insertMany() takes an array of objects has a first parameter. It could also takes a second paremeter. The second parameter is an object, it looks like this :

{
  ordered: false,
}

It means if mongoDB encounters an error, it will try to insert the rest of the documents. Without this second argument, mongo has the ordered key value set to true by default. Meaning that the insertion will be stopped when an error occurs.

Introduction to findOne

db.users.findOne({username:'dwight'}, {email: true, _id:false})

This query find in the users collection the document which the username has 'dwight' as value then only displays his email property and value.
findOne result displays by default the document ID, so you have to hide it if you don't want it (set it to false) in the result.

Finding doc

Basic query :

db.scores.find({type:'essay',score:50},{student: true, _id:false})

This query finds all documents with type: essay and score: 50 and only retrieve the student field.

The second argument is called projection. It could also be written like this :

{student: 1, _id:0}

Querying Using $gt and $lt

db.scores.find({ score : { $gt : 50 , $lte : 60 } } );

This query use a subdocument { $gt : 50 , $lte : 60 } to filter documents with a score between 50 (exclusive => greater than) and 60 (inclusive => lower than equal). You also can apply these operators on strings (based on utf-8 ASCII table) :

db.users.find( { name : { $gte : "F" , $lte : "Q" } }

Using regexes, $exists, $type

db.users.find({name:{$regex: '^A'}, email: {$exists: true}, gender: {type: 2}})

This query find all documents with the name that start with a 'A', contains the email property and the gender property that has the type value of 2. (2 is a string value that refer to the bson type documentation).

Using $or

db.scores.find({$or: [{score:{$lt:50}}, {score:{$gt:90}}]})

This query find all documents with the score lower than 50 OR greater than 90.

Using $and

db.scores.find({$and: [{score:{$gte:50}}, {level:{$lt:10}}]})

This query works the same than a classical query but it is less performance. Notes that is exists.

Querying Inside Arrays

If our document looks like :

{   
    job: 'web developer'
    tags: ['JavaScript', 'MongoDB','Node.js']
}

The following query will matches this document :

db.scores.find({tags:'MongoDB'})

You also can target a specific array element using the dot notation as follow :

db.scores.find({'tags.0':'JavaScript'})

The query will match documents with 'JavaScript' in the index position 0 of the array tags.

Using $in and $all

If our document looks like :

{
    name : "Cliff" ,
    friends : [ "Pete" , "Joe" , "Tom" , "Bob" ],
    favorites : [ "pickles", "cycling" ]
}

The following query :

db.users.find( { friends : { $all : [ "Joe" , "Bob" ] }, favorites : { $in : [ "running" , "pickles" ] } } )

will match documents that contains Joe AND Bob in their friends list; These documents also need running OR pickles in the favorites list.

Queries with Dot Notation

The purpose of query with dot notation is to allow to target a specific property inside a nested document. No matters if this document contains other properties.

{ 
    product : "Super Duper-o-phonic", 
    price : 100000000000,
    reviews :   [ 
                  { user : "fred", comment : "Great!" , rating : 5 },
                  { user : "tom" , comment : "I agree with Fred, somewhat!" ,rating : 4 } 
                ],
      ... 
}

This is a query example that matches the above document using the dot notation :

db.catalog.find({price: {$gt:10000},'reviews.rating':{$gte: 5}})

Querying, Cursors

Put the query result in a cursor (a variable) :

cur = db.people.find(); null;

Write null at the end to output null that allows to do this following query in the mongo shell: cur.hasNext(), prints the first result query. We can now do stuffs like this :

while (cur.hasNext()) printjson(cur.next())

We can also do stuffs like that :

db.scores.find({type:'exam'}).sort({score : -1}).skip(50).limit(20)

This query retrieves exam documents in the scores collection. Sorted by score in descending order, skipping the first 50 and showing the next 20.

Counting Results

db.scores.count({type:'essay', score:{$gt:90}})

This query counts the documents in the scores collection where the type is "essay" and the score is greater than 90.

Wholesale Updating of a Document

{
    "_id" : "Texas",
    "population" : 2500000,
    "land_locked" : 1
}

We issued the following query on the above document :

db.foo.update({_id:"Texas"},{population:30000000})

the update result will be this :

{ 
    "_id" : "Texas",
    "population" : 30000000
}

The reason is because the update() query will remove all properties for the document(s) with the Texas _id value. Then replace them by the second argument properties. Note that we always keep the _id property.

Using the $set and $inc Commands

{
    "_id" : "myrnarackham",
    "phone" : "301-512-7434",
    "country" : "US",
    "level" : 4
}

We want to update the country property in the above document but we just know the _id value. We should use the $set command :

db.users.update({_id:'myrnarackham'},{$set: {country:'RU'}})

In this case, the $set command only update the country value. If the country property doesn't exist, it creates the property.

We can also use the $inc command that increases a value:

db.users.update({_id:'myrnarackham'},{$inc: {level:2}})

If the level property doesn't exist, it creates it and set it to 2. This is the result of the above query :

{
    "_id" : "myrnarackham",
    "phone" : "301-512-7434",
    "country" : "US",
    "level" : 6
}

Using the $unset Command

{ 
    "_id" : "jimmy" , 
    "favorite_color" : "blue" , 
    "interests" : [ "debating" , "politics" ] 
}

To remove the interests property according to the above example, we use this query :

db.users.update({_id:"jimmy"},{$unset:{interests:"debating"}})

In genral, to remove a property we use this query (setting to 1 the value of the property to delete) :

db.users.update({_id:"jimmy"},{$unset:{interests:1}})

Using $push, $pop, $pull, $pullAll, $addToSet

{
    _id:"0",
    a:['monkey', 'bird', 'cat', 'dog']
}

This following query will manipulate the 3rd element of the array "a", having change the 3rd element, from "cat" to "turtle" :

db.arrays.update({_id:"0"},{$set:{a.2: "turtle"}})

This following query will add an element with the value "tiger" at the end of the array "a" :

db.arrays.update({_id:"0"},{$push:{a: "tiger"}})

This following query will remove the last element of the array "a" :

db.arrays.update({_id:"0"},{$pop:{a: 1}})

This following query will remove the first element of the array "a" :

db.arrays.update({_id:"0"},{$pop:{a: -1}})

This following query will add several elements at the end of the array "a" :

db.arrays.update({_id:"0"},{$pushAll:{a: ["rat", "horse", "pig"]}})

This following query will remove the value "bird" of the array "a" :

db.arrays.update({_id:"0"},{$pull:{a: "bird"}})

This following query will remove several elements at the end of the array "a" :

db.arrays.update({_id:"0"},{$pullAll:{a: ["rat", "horse", "pig"]}})

This following query will add the value "lion" at the end of the array "a". If this value already exists, this query has not effect :

db.arrays.update({_id:"0"},{$addToSet:{a: "lion"}})

Upserts

The update method could take a third argument : upsert.

This following query will set the "interest" array of bar "username". If the document doesn't exists, and the "upsert" value is true, this document will be created.

db.foo.update( { username : 'bar' }, { '$set' : { 'interests': [ 'cat' , 'dog' ] } } , { upsert : true } );

Multi-update

The update method could take a third argument : multi.

The default behaviour to update in mongoDB is to update just one document in the collection.

This following query will set the "skills" array of all documents in the collection :

db.foo.update( {}, { '$set' : { 'skills': [ 'node.js' , 'mongoDB' ] } } , { multi : true } );

Example => We have a score collection with documents such as this one :

{
    "_id" : ObjectId("50844162cb4cf4564b4694f8"),
    "student" : 0,
    "type" : "exam",
    "score" : 75
}

This following query update every document with a score less than 70 and increment them with an extra 20 points :

db.scores.update({score:{$lt: 70}}, {$inc:{score:20}}, {multi:true})

Removing Data

This following query removes each document with a score lower than 60 :

db.scores.remove({score:{$lt:60}})

Note:

db.scores.remove({})

The above query will not remove document from the entire collection. mongo will return you an error. You ccan find more infos about this at the end of this page.

If you want to remove a collection you can drop it :

  1. Use the database that you want to drop:

    use databaseName
    
  2. Run this command :

    db.runCommand( { dropDatabase: 1 } )
    

Node.js Driver: find, findOne, and cursors

The following query imports the results.json content in the players collection in the game database.

mongoimport -d game -c players results.json

The following example shows how to make a findOne query with Node.js + mongoDB :

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

MongoClient.connect('mongodb://localhost:27017/course', function(err, db) 
{
     if(err) throw err;

     var query = { 'grade' : 100};

     function callback(err, doc) {
          if(err) throw err;

          console.dir(doc);

          db.close();   
     }

     db.collection('grades').findOne(query, callback);
});

Node.js Driver: Using Field Projection

The following queries will cause only the 'grade' field to be returned :

db.collection('grades').find({}, {'grade':1, '_id':0}, callback);

Node.js Driver: Using $gt and $lt

The following example shows how works $gt and $lt using Node.js driver :

var MongoClient = require('mongodb').MongoClient;
MongoClient.connect('mongodb://localhost:27017/course', function(err, db) {
    if(err) throw err;

    var query = {grade: {$gt: 69, $lt: 80}};

    db.collection('grades').find(query).each(function(err, doc){
        if(err) throw err;
        if(doc == null) {
            return db.close();
        }
        console.dir(doc);
    });
});

Importing external json file, Reddit example

var MongoClient = require('mongodb').MongoClient;
var request =     require('request');

MongoClient.connect('mongodb://localhost:27017/course', function(err, db) {
    if(err) throw err;

    request('http://www.reddit.com/r/technology/.json', function(error, response, body) {
        if (!error && response.statusCode == 200) {
            var obj = JSON.parse(body);
            var stories = obj.data.children.map(function (story) { return story.data; });

            db.collection('reddit').insert(stories, function(err, data) {
                if(err) throw err;

                console.dir(data);

                return db.close();
            });
        }
});

In the above example, we use the request middleware to retrieve the Reddit datas.

Node.js Driver: Skip, Limit and Sort

The following example shows how works sort(), skip() and limit() using Node.js driver :

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

MongoClient.connect('mongodb://localhost:27017/course', function(err, db) {
    if(err) throw err;

    var grades = db.collection('grades');
    var options = {
      'skip': 1,
      'limit': 4,
      'sort': [['grade', 1], ['student', -1]]
    }

    var cursor = grades.find({}, {}, options);

    cursor.each(function (err, doc) {
      if(err) throw err;
      if (doc === null) {
        return db.close();
      };
      console.dir(doc);
    })

});

Note that no matter the order when those functions will be applied to the cursor. Mongo will change automatically their order and run sort() first, then skip() and limit().

Also, the sort() method actually sort by grade (asc order), then by student name (desc order). Brackets are used to respect this order.

Node.js Driver: Inserting, _id

The following example shows how insert several documents using Node.js driver :

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

MongoClient.connect('mongodb://localhost:27017/course', function(err, db) {
    if(err) throw err;

    var docs = [ { '_id' : 'George', 'age' : 6 },
                 { '_id' : 'george', 'age' : 7 } ];

    db.collection('students').insert(docs, function(err, inserted) {
        if(err) throw err;

        console.dir("Successfully inserted: " + JSON.stringify(inserted));

        return db.close();
    });
});

Note that we are using an array to insert several documents. If we don't specify the _id field, Mongo generates it automatically. Mongo returns error if we insert documents with the same _id. In this case, both documents will be inserted because mongo is case sensitive.

Node.js Driver: Updating

The following example shows how update multiple documents using Node.js driver :

var MongoClient = require('mongodb').MongoClient;
MongoClient.connect("mongodb://localhost:27017/course", function(err, db) {
     if(err) throw err;

     var query = { 'assignment': 'hw1'};
     var operator = {'$set': {'date_graded': new Date() } };
     var options = {  multi : true };

     db.collection('grades').update(query, operator, options, function(err, updated) {
          if(err) throw err;

          console.dir("Successfully updated " + updated + " document!");

          return db.close();
     });
});

Note that if we don't use the $set operator, the entiere document will be replaced with the new properties. If we use both, mongo will return an error. The updated parameter in the callback function will print the number of documents matched by the update.

Node.js Driver: Upserts

As we mentioned above, "upserts" insert a new document instead of update it if the document is not match by the query.

We also can use save:

db.collection('grades').save({'_id': '[email protected]', 'name': 'Joe'}, callback);

The above query will upsert to insert or replace the document. The result is similar to an update method using "upsert".

Node.js Driver: findAndModify

The findAndModify purpose is to update a document and render it atomically. We need findAndModify for particular use cases like a counter incrementation :

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

MongoClient.connect('mongodb://localhost:27017/course', function(err, db) {
    if(err) throw err;

    var query = {'name': 'comments'};
    var sort = [];
    var operator = {$inc: {'counter': 1}};
    var options = {new: true};

    db.collection('counters').findAndModify(query, sort, operator, options, function (err, doc) {
      if(err) throw err;

      if (!doc) {
        console.log('No counter found for comments.');
      }
      else {
        console.log('Number of comments : ', doc.counter);
      }
      return db.close();
    })

});

This query is able to modify one document at a time. That's why it takes a sort argument. Because the query could match several documents so the sort argument give us precisely which document is targeted.

The following calls to findAndModify will add the "dropped" field to the homework document with the lowest grade. Then call the given callback with the resulting document.

db.collection('homeworks').findAndModify({}, [[ 'grade' , 1 ]], { '$set' : { 'dropped' : true } }, { 'new' : true }, callback);

Note that because we passing new: true, the doc in the callback result will be the modified result.

Node.js Driver: Remove

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

MongoClient.connect('mongodb://localhost:27017/course', function(err, db) {
    if(err) throw err;

    var query = {'assignment': 'hw3'};

    db.collection('grades').remove(query, function (err, removed) {
      if(err) throw err;

      console.dir('successfully removed ' + removed + 'documents.');

      return db.close();
    })

});

Each of these 3 following query will remove all documents in the 'foo' collection :

db.collection('foo').remove(callback);
db.collection('foo').remove({ 'x' : { '$nin' : [] } }, callback);
db.collection('foo').remove({}, callback);

More infos about $nin (not in) operator.