- Tutorials
- CRUD Operations
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.
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.