CRUD Operations

For a walkthrough of the main CRUD operations please refer to the Quick Start guide.

Driver CRUD operations are defined as the operations performed to create, read, update, and delete documents. This tutorial covers both the basic CRUD methods and the specialized findAndModify based methods as well as the new Bulk API methods for efficient bulk write operations.

Write Methods

Write methods are divided into those which insert documents into a collection, those which update documents in a collection, and those which remove documents from a collection.

Insert Documents

The insertOne and insertMany methods exist on the Collection class and are used to insert documents into MongoDB.


const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://localhost:27017';
// Database name
const dbName = 'myproject';

const client = new MongoClient(url);

(async function() {
  try {
    await client.connect();
    console.log("Connected correctly to server");
    const db = client.db(dbName);

    // Insert a single document
    let r = await db.collection('inserts').insertOne({a:1});
    assert.equal(1, r.insertedCount);

    // Insert multiple documents
    var r = await db.collection('inserts').insertMany([{a:2}, {a:3}]);
    assert.equal(2, r.insertedCount);

    // Close connection
    client.close();
  } catch(err) {
    console.log(err.stack);
  }
})();

The first insert inserts a single document into the inserts collection. Notice that there’s no need to explicitly create a new inserts collection, as the server will create it implicitly when the first document is inserted. The method db.createIndex is only necessary when creating non-standard collections, such as capped collections or where parameters other than the defaults are necessary.

The insertOne and insertMany methods also accept a second argument which can be an options object. This object can have the following fields:

Parameter Type Description
w {Number/String, > -1 || ‘majority’} the write concern for the operation where < 1 returns an acknowledgment of the write with not results {ok:1} and w >= 1 or w = ‘majority’ acknowledges the write with full write results.
wtimeout {Number, 0} set the timeout for waiting for write concern to finish (combines with w option).
j (Boolean, default:false) write waits for journal sync.
serializeFunctions (Boolean, default:false) serialize functions on an object to mongodb, by default the driver does not serialize any functions on the passed in documents.
forceServerObjectId (Boolean, default:false) Force server to assign _id values instead of driver.

The following example shows how to serialize a passed-in function when writing to a replica set.


const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://localhost:27017';
// Database name
const dbName = 'myproject';

const client = new MongoClient(url);

(async function() {
  try {
    await client.connect();
    console.log("Connected correctly to server");
    const db = client.db(dbName);

    // Insert a single document
    const r = await db.collection('inserts').insertOne({
          a:1
        , b: function() { return 'hello'; }
      }, {
          w: 'majority'
        , wtimeout: 10000
        , serializeFunctions: true
        , forceServerObjectId: true
      });

    assert.equal(1, r.insertedCount);
    client.close();
  } catch(err) {
    console.log(err.stack);
  }
})();

Specify a Data Type

The following example specifies a numerical data type when inserting documents with the insertMany method.

Note

The Decimal128 data type requires MongoDB server version 3.4 or higher.


const Long = require('mongodb').Long;
const Decimal = require('mongodb').Decimal128;

const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://localhost:27017';
// Database name
const dbName = 'myproject';

const client = new MongoClient(url);

(async function() {
  try {
    await client.connect();
    console.log("Connected correctly to server");
    const db = client.db(dbName);

    const longValue = Long(1787);
    const decimalValue = Decimal.fromString("27.8892836");

    // Insert multiple documents
    const r = await db.collection('numbers').insertMany([ { a : longValue }, { b : decimalValue } ]);
    assert.equal(2, r.insertedCount);

    // Close connection
    client.close();
  } catch(err) {
    console.log(err.stack);
  }
})();

The above operation inserts the following documents into the numbers collection:

{ "_id" : ObjectId("57d6f63a98724c65a5d7bd7a"), "a" : NumberLong(1787) }
{ "_id" : ObjectId("57d6f63a98724c65a5d7bd7b"), "b" : NumberDecimal("27.8892836") }

Updating Documents

The updateOne and updateMany methods exist on the Collection class and are used to update and upsert documents.


const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://localhost:27017';
// Database name
const dbName = 'myproject';

const client = new MongoClient(url);

(async function() {
  try {
    await client.connect();
    console.log("Connected correctly to server");
    const db = client.db(dbName);

    // Get the updates collection
    const col = db.collection('updates');
    // Insert multiple documents
    let r = await col.insertMany([{a:1}, {a:2}, {a:2}]);
    assert.equal(3, r.insertedCount);

    // Update a single document
    r = await col.updateOne({a:1}, {$set: {b: 1}});
    assert.equal(1, r.matchedCount);
    assert.equal(1, r.modifiedCount);

    // Update multiple documents
    r = await col.updateMany({a:2}, {$set: {b: 1}});
    assert.equal(2, r.matchedCount);
    assert.equal(2, r.modifiedCount);

    // Upsert a single document
    r = await col.updateOne({a:3}, {$set: {b: 1}}, {
      upsert: true
    });
    assert.equal(0, r.matchedCount);
    assert.equal(1, r.upsertedCount);

    // Close connection
    client.close();
  } catch(err) {
    console.log(err.stack);
  }
})();

The update method also accepts a third argument which can be an options object. This object can have the following fields:

Parameter Type Description
w {Number/String, > -1 || ‘majority’} the write concern for the operation where < 1 returns an acknowledgment of the write with not results {ok:1} and w >= 1 or w = ‘majority’ acknowledges the write with full write results.
wtimeout {Number, 0} set the timeout for waiting for write concern to finish (combines with w option).
j (Boolean, default:false) write waits for journal sync.
multi (Boolean, default:false) Update one/all documents with operation.
upsert (Boolean, default:false) Update operation is an upsert.

Just as for insert, the update method allows you to specify a per operation write concern using the w, wtimeout and fsync parameters.

Removing Documents

The deleteOne and deleteMany methods exist on the Collection class and are used to remove documents from MongoDB.


const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://localhost:27017';
// Database name
const dbName = 'myproject';

const client = new MongoClient(url);

(async function() {
  try {
    await client.connect();
    console.log("Connected correctly to server");
    const db = client.db(dbName);

    // Get the removes collection
    const col = db.collection('removes');
    // Insert multiple documents
    let r = await col.insertMany([{a:1}, {a:2}, {a:2}]);
    assert.equal(3, r.insertedCount);

    // Remove a single document
    r = await col.deleteOne({a:1});
    assert.equal(1, r.deletedCount);

    // Update multiple documents
    r = await col.deleteMany({a:2});
    assert.equal(2, r.deletedCount);

    // Close connection
    client.close();
  } catch(err) {
    console.log(err.stack);
  }
})();

The deleteOne and deleteMany methods also accept a second argument which can be an options object. This object can have the following fields:

Parameter Type Description
w {Number/String, > -1 || ‘majority’} the write concern for the operation where < 1 returns an acknowledgment of the write with not results {ok:1} and w >= 1 or w = ‘majority’ acknowledges the write with full write results.
wtimeout {Number, 0} set the timeout for waiting for write concern to finish (combines with w option).
j (Boolean, default:false) write waits for journal sync.
single (Boolean, default:false) Removes the first document found.

Just as for updateOne/updateMany and insertOne/insertMany, the deleteOne/deleteMany method allows you to specify a per operation write concern using the w, wtimeout and fsync parameters.

findOneAndUpdate, findOneAndDelete, and findOneAndReplace

The three methods findOneAndUpdate, findOneAndDelete and findOneAndReplace are special commands which allow the user to update or upsert a document and have the modified or existing document returned. When using these methods, the operation takes a write lock for the duration of the operation in order to ensure the modification is atomic.


const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://localhost:27017';
// Database name
const dbName = 'myproject';

const client = new MongoClient(url);

(async function() {
  try {
    await client.connect();
    console.log("Connected correctly to server");
    const db = client.db(dbName);

    // Get the findAndModify collection
    const col = db.collection('findAndModify');
    // Insert multiple documents
    let r = await col.insert([{a:1}, {a:2}, {a:2}]);
    assert.equal(3, r.result.n);

    // Modify and return the modified document
    r = await col.findOneAndUpdate({a:1}, {$set: {b: 1}}, {
        returnDocument: 'after'
      , sort: [[a,1]]
      , upsert: true
    });
    assert.equal(1, r.value.b);

    // Remove and return a document
    r = await col.findOneAndDelete({a:2});
    assert.ok(r.value.b == null);

    // Close connection
    client.close();
  } catch(err) {
    console.log(err.stack);
  }
})();

The findOneAndUpdate method also accepts a third argument which can be an options object. This object can have the following fields:

Parameter Type Description
w {Number/String, > -1 || ‘majority’} the write concern for the operation where < 1 returns an acknowledgment of the write with not results {ok:1} and w >= 1 or w = ‘majority’ acknowledges the write with full write results.
wtimeout {Number, 0} set the timeout for waiting for write concern to finish (combines with w option).
j (Boolean, default:false) write waits for journal sync.
upsert (Boolean, default:false) Perform an upsert operation.
sort (Object, default:null) Sort for find operation.
projection (Object, default:null) Projection for returned result
returnDocument (String, ‘before’ || ‘after’, default:‘before’) Set to ‘after’ if you want to return the modified document rather than the original. Ignored for remove.

The findOneAndDelete function is designed to help remove a document.


const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://localhost:27017';
// Database name
const dbName = 'myproject';

const client = new MongoClient(url);

(async function() {
  try {
    await client.connect();
    console.log("Connected correctly to server");
    const db = client.db(dbName);

    // Get the findAndModify collection
    const col = db.collection('findAndModify');
    // Insert multiple documents
    let r = await col.insert([{a:1}, {a:2}, {a:2}]);
    assert.equal(3, r.result.n);

    // Remove a document from MongoDB and return it
    r = await col.findOneAndDelete({a:1}, {
        sort: [[a,1]]
      });
    assert.ok(r.value.b == null);

    // Close connection
    client.close();
  } catch(err) {
    console.log(err.stack);
  }
})();

Like findOneAndUpdate, it allows an object of options to be passed in which can have the following fields:

Parameter Type Description
w {Number/String, > -1 || ‘majority’} the write concern for the operation where < 1 returns an acknowledgment of the write with not results {ok:1} and w >= 1 or w = ‘majority’ acknowledges the write with full write results.
wtimeout {Number, 0} set the timeout for waiting for write concern to finish (combines with w option).
j (Boolean, default:false) write waits for journal sync.
sort (Object, default:null) Sort for find operation.

BulkWrite

The bulkWrite function allows a simple set of bulk operations to run in a non-fluent way, in comparison to the bulk API discussed next.


const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://localhost:27017';
// Database name
const dbName = 'myproject';

const client = new MongoClient(url);

(async function() {
  try {
    await client.connect();
    console.log("Connected correctly to server");
    const db = client.db(dbName);

    // Get the collection
    const col = db.collection('bulk_write');
    const r = await col.bulkWrite([
        { insertOne: { document: { a: 1 } } }
      , { updateOne: { filter: {a:2}, update: {$set: {a:2}}, upsert:true } }
      , { updateMany: { filter: {a:2}, update: {$set: {a:2}}, upsert:true } }
      , { deleteOne: { filter: {c:1} } }
      , { deleteMany: { filter: {c:1} } }
      , { replaceOne: { filter: {c:3}, replacement: {c:4}, upsert:true}}]
    , {ordered:true, w:1});
    assert.equal(1, r.insertedCount);
    assert.equal(1, Object.keys(r.insertedIds).length);
    assert.equal(1, r.matchedCount);
    assert.equal(0, r.modifiedCount);
    assert.equal(0, r.deletedCount);
    assert.equal(2, r.upsertedCount);
    assert.equal(2, Object.keys(r.upsertedIds).length);

    // Close connection
    client.close();
  } catch(err) {
    console.log(err.stack);
  }
})();

The bulkWrite function takes an array of operations which can be objects of either insertOne, updateOne, updateMany, deleteOne, deleteMany, or replaceOne. It also takes a second parameter which takes the following options:

Parameter Type Description
ordered (Boolean, default:true) Execute in order or out of order.
w {Number/String, > -1 || ‘majority’} the write concern for the operation where < 1 returns an acknowledgment of the write with not results {ok:1} and w >= 1 or w = ‘majority’ acknowledges the write with full write results.
wtimeout {Number, 0} set the timeout for waiting for write concern to finish (combines with w option).
j (Boolean, default:false) write waits for journal sync.

Bulk Write Operations

Bulk write operations make it easy to write groups of operations together to MongoDB. There are some caveats and to get the best performance you need to be running against MongoDB version 2.6 or higher, which supports the new write commands. Bulk operations are split into ordered and unordered bulk operations. An ordered bulk operation guarantees the order of execution of writes while the unordered bulk operation makes no assumptions about the order of execution. In the Node.js driver the unordered bulk operations will group operations according to type and write them in parallel.


const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://localhost:27017';
// Database name
const dbName = 'myproject';

const client = new MongoClient(url);

(async function() {
  try {
    await client.connect();
    console.log("Connected correctly to server");
    const db = client.db(dbName);

    // Get the collection
    const col = db.collection('bulkops');
    // Create ordered bulk, for unordered initializeUnorderedBulkOp()
    const bulk = col.initializeOrderedBulkOp();
    // Insert 10 documents
    for(let i = 0; i < 10; i++) {
      bulk.insert({a: i});
    }

    // Next perform some upserts
    for(let i = 0; i < 10; i++) {
      bulk.find({b:i}).upsert().updateOne({b:1});
    }

    // Finally perform a remove operation
    bulk.find({b:1}).deleteOne();

    // Execute the bulk with a journal write concern
    const result = await bulk.execute();

    // Close connection
    client.close();
  } catch(err) {
    console.log(err.stack);
  }
})();

The Bulk API handles all the splitting of operations into multiple writes and also emulates 2.6 and higher write commands for 2.4 and earlier servers.

There are some important things to keep in mind when using the bulk API and especially the ordered bulk API mode. The write commands are single operation type. That means they can only do insert/update and remove. If you f.ex do the following combination of operations:

Insert {a:1}
Update {a:1} to {a:1, b:1}
Insert {a:2}
Remove {b:1}
Insert {a:3}

This will result in the driver issuing four write commands to the server:

Insert Command with {a:1}
Update Command {a:1} to {a:1, b:1}
Insert Command with {a:2}
Remove Command with {b:1}
Insert Command with {a:3}

If you instead organize your ordered in the following manner:

Insert {a:1}
Insert {a:2}
Insert {a:3}
Update {a:1} to {a:1, b:1}
Remove {b:1}

The number of write commands issued by the driver will be:

Insert Command with {a:1}, {a:2}, {a:3}
Update Command {a:1} to {a:1, b:1}
Remove Command with {b:1}

Attention to the order of operations results in more efficient and faster bulk write operation.

For unordered bulk operations this is not important, as the driver sorts operations by type and executes them in parallel.

Read Methods

The main method for querying the database is the find method.

find returns a cursor which allows the user to operate on the data. The cursor also implements the Node.js 0.10.x or higher stream interface, allowing the user to pipe the results to other streams.

The following example materializes all the documents from a query using the toArray method, but limits the number of returned results to two documents.


const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://localhost:27017';
// Database name
const dbName = 'myproject';

const client = new MongoClient(url);

(async function() {
  try {
    await client.connect();
    console.log("Connected correctly to server");
    const db = client.db(dbName);

    // Get the collection
    const col = db.collection('find');
    // Insert multiple documents
    const r = await col.insertMany([{a:1}, {a:1}, {a:1}]);
    assert.equal(3, r.insertedCount);

    // Get first two documents that match the query
    const docs = await col.find({a:1}).limit(2).toArray();
    assert.equal(2, docs.length);

    // Close connection
    client.close();
  } catch(err) {
    console.log(err.stack);
  }
})();

The cursor returned by the find method has several methods that allow for chaining of options for a query. Once the query is ready to be executed you can retrieve the documents using the next, each and toArray methods. If the query returns many documents it’s preferable to use the next or each methods, as the toArray method will materialize all the documents into memory before calling the callback function, potentially using a lot of memory if the query returns many documents.

collection.find({}).project({a:1})                             // Create a projection of field a
collection.find({}).skip(1).limit(10)                          // Skip 1 and limit 10
collection.find({}).batchSize(5)                               // Set batchSize on cursor to 5
collection.find({}).filter({a:1})                              // Set query on the cursor
collection.find({}).comment('add a comment')                   // Add a comment to the query, allowing to correlate queries
collection.find({}).addCursorFlag('tailable', true)            // Set cursor as tailable
collection.find({}).addCursorFlag('oplogReplay', true)         // Set cursor as oplogReplay
collection.find({}).addCursorFlag('noCursorTimeout', true)     // Set cursor as noCursorTimeout
collection.find({}).addCursorFlag('awaitData', true)           // Set cursor as awaitData
collection.find({}).addCursorFlag('exhaust', true)             // Set cursor as exhaust
collection.find({}).addCursorFlag('partial', true)             // Set cursor as partial
collection.find({}).addQueryModifier('$orderby', {a:1})        // Set $orderby {a:1}
collection.find({}).max(10)                                    // Set the cursor max
collection.find({}).maxTimeMS(1000)                            // Set the cursor maxTimeMS
collection.find({}).min(100)                                   // Set the cursor min
collection.find({}).returnKey(10)                              // Set the cursor returnKey
collection.find({}).setReadPreference(ReadPreference.PRIMARY)  // Set the cursor readPreference
collection.find({}).showRecordId(true)                         // Set the cursor showRecordId
collection.find({}).sort([['a', 1]])                           // Sets the sort order of the cursor query
collection.find({}).hint('a_1')                                // Set the cursor hint

All options are chainable, so you can combine settings in the following way:

collection.find({}).maxTimeMS(1000).skip(1).toArray(..)

More information can be found in the Cursor API documentation.

The following example uses the next method.

In ECMAScript 6, The new `generator` functions allow for what is arguably a much cleaner and easier way to read iteration code.

const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://localhost:27017';
// Database name
const dbName = 'myproject';

const client = new MongoClient(url);

(async function() {
  try {
    await client.connect();
    console.log("Connected correctly to server");
    const db = client.db(dbName);

    // Get the collection
    const col = db.collection('find');
    // Insert multiple documents
    const r = await col.insertMany([{a:1}, {a:1}, {a:1}]);
    assert.equal(3, r.insertedCount);

    // Get the cursor
    const cursor = col.find({a:1}).limit(2);

    // Iterate over the cursor
    while(await cursor.hasNext()) {
      const doc = await cursor.next();
      console.dir(doc);
    }

    // Close connection
    client.close();
  } catch(err) {
    console.log(err.stack);
  }
})();

The next method allows the application to read one document at a time using callbacks.

The following example uses the each method.

const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://localhost:27017';

// Database Name
const dbName = 'myproject';

// Create a new MongoClient
const client = new MongoClient(url);

// Use connect method to connect to the Server
client.connect(function(err, client) {
  assert.equal(null, err);
  console.log("Connected correctly to server");

  const db = client.db(dbName);

  const col = db.collection('find');
  // Insert multiple documents
  col.insertMany([{a:1}, {a:1}, {a:1}], function(err, r) {
    assert.equal(null, err);
    assert.equal(3, r.insertedCount);

    // Get first documents from cursor using each
    col.find({a:1}).limit(2).each(function(err, doc) {
      if(doc) {
        // Got a document
      } else {
        client.close();
        return false;
      }
    });
  });
});

The each method calls the supplied callback until there are no more documents available that satisfy the query. Once the available documents are exhausted it will return null for the second parameter in the callback. If you wish to terminate the each early you should return false in your each callback. This will stop the cursor from returning documents.