Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

The MongoDB Shell's New CRUD API

DZone's Guide to

The MongoDB Shell's New CRUD API

The shell is now more consistent with the drivers than ever before -- and beyond that, the new API is just better all around.

Free Resource

Whether you work in SQL Server Management Studio or Visual Studio, Redgate tools integrate with your existing infrastructure, enabling you to align DevOps for your applications with DevOps for your SQL Server databases. Discover true Database DevOps, brought to you in partnership with Redgate.

Leaf on water

We released the latest development version of MongoDB the other week. The official announcement focuses on bug fixes, but I'm much more excited about a new feature: the mongo shell includes the new CRUD API! In addition to the old insert, update, and remove, the shell now supports insertMany, replaceOne, and a variety of other new methods.

Why do I care about this, and why should you?

MongoDB's next-generation drivers, released this spring, include the new API for CRUD operations, but the shell did not initially follow suit. My reader Nick Milon commented that this is a step in the wrong direction: drivers are now less consistent with the shell. He pointed out, "developers switch more often between a driver and shell than drivers in different programming languages." So I proposed the feature, Christian Kvalheim coded it, and Kay Kim is updating the user's manual.

It's satisfying when a stranger's suggestion is so obviously right that we hurry to implement it.

The shell is now more consistent with the drivers than ever before. But beyond that, the new API is just better. For example, the old insert method could take one document or many, and its return value didn't include the new IDs:

> // the old insert API
> db.test.insert({_id: 1})
WriteResult({ "nInserted" : 1 })
> db.test.insert([{_id: 2}, {_id: 3}, {_id: 4}])
BulkWriteResult({
    "writeErrors" : [ ],
    "writeConcernErrors" : [ ],
    "nInserted" : 3,
    "nUpserted" : 0,
    "nMatched" : 0,
    "nModified" : 0,
    "nRemoved" : 0,
    "upserted" : [ ]
})

The new API better distinguishes single- and bulk-insert, and returns more useful results:

> // the new CRUD API
> db.test2.insertOne({_id: 1})
{
    "acknowledged" : true,
    "insertedId" : 1
}
> db.test2.insertMany([{_id: 2}, {_id: 3}, {_id: 4}])
{ 
    "acknowledged" : true,
    "insertedIds" : [ 2, 3, 4 ]
}

The old insert method remains in the shell indefinitely, however: we know there are heaps of scripts written with the old methods and we don't plan to drop them.

On to the next operation. The shell's update is famously ill-designed. No one can remember the order of the "upsert" and "multi" options, and defaulting "multi" to false stumped generations of new users:

> // the old update API
> db.test.update(
... {_id: 1},
... {$set: {x: 1}},
... true              /* upsert */,
... false             /* multi */
)
WriteResult({
    "nMatched" : 0,
    "nUpserted" : 1,
    "nModified" : 0,
    "_id" : 1
})

The new updateOne method is much easier to use correctly:

> // the new update API
> db.test2.updateOne(
... {_id: 1},
... {$set: {x: 1}},
... {upsert: true}
)
{
    "acknowledged" : true,
    "matchedCount" : 0,
    "modifiedCount" : 0,
    "upsertedId" : 1
}

We introduce updateMany for multi-updates.

Another flaw in the old update was, if you forgot the $-sign on an operator, you replaced a whole document instead of modifying it:

> // the old replace API
> db.test.update(
... {_id: 1},
... {set: {x: 1}}  // OOPS!!
)
WriteResult({
    "nMatched" : 1,
    "nUpserted" : 0,
    "nModified" : 1
})
> // document was replaced
> db.test.findOne()
{ "_id" : 1, "set" : { "x" : 1 } }

Now, replacing a document and updating it are kept distinct, preventing mistakes:

> // the new update API catches mistakes
> db.test2.updateOne(
... {_id: 1},
... {set: {x: 1}}
)
Error: the update operation document must contain atomic operators
>
> // explicitly replace with "replaceOne"
> db.test2.replaceOne(
... {_id: 1},
... {x: 1}
)
{
    "acknowledged" : true,
    "matchedCount" : 1,
    "modifiedCount" : 1
}
> db.test2.findOne()
{ "_id" : 1, "x" : 1 }

The old remove method is full of surprises: it defaults "multi" to true, although "multi" is false for updates:

> // the old delete API
> db.test.remove({})  // remove EVERYTHING!!

The new methods let you say clearly which you want:

> // the new delete API
> db.test2.deleteOne({})
{ "acknowledged" : true, "deletedCount" : 1 }
> db.test2.deleteMany({})
{ "acknowledged" : true, "deletedCount" : 3 }

MongoDB's findAndModify command is powerful, and its options are impossible to learn.

> // the old findAndModify
> db.test.findAndModify({
... query: {_id: 1},
... update: {$set: {x: 1}},
... new: true
... })
{ "_id" : 1, "x" : 1 }
> db.test.findAndModify({
... query: {_id: 1},
...  // REPLACE the document!
... update: {field: 'value'},
... new: false
... })
{ "_id" : 1, "x" : 1 }
> db.test.findAndModify({
... query: {_id: 1},
... remove: true
... })
{ "_id" : 1, "field" : "value" }

So we've split the one overloaded findAndModify into three:

> // the new API
> db.test2.findOneAndUpdate(
... {_id: 1},
... {$set: {x: 1}},
... {returnNewDocument: true}
... )
{ "_id" : 1, "x" : 1 }
> db.test2.findOneAndReplace(
... {_id: 1},
... {field: 'value'},
... {returnNewDocument: false}
... )
{ "_id" : 1, "x" : 1 }
> db.test2.findOneAndDelete({_id: 1})
{ "_id" : 1, "field" : "value" }

This is not a complete description of the changes. The find, findOne, and other query methods have standardized new options while preserving compatibility with old scripts. There's also a new bulkWrite method that's easier and more efficient than the old Bulk API. We'll have complete documentation for the new shell API when we publish the manual for MongoDB 3.2. Meanwhile, read Jeremy Mikola's article about the CRUD API, and the spec itself is quite legible, too.

Since the old insert, update, and remove are so pervasive in our users' code we have no plans to drop them or make backwards-incompatible changes.

I'm so glad we took the time to implement the new CRUD API in the shell. It was a big effort building, testing, and documenting it—the diff for the initial patch alone is frightening—but it's well worth it to give the next generation of developers a consistent experience when they first learn MongoDB. Thanks again to Nick Milon for giving us the nudge.

It’s easier than you think to extend DevOps practices to SQL Server with Redgate tools. Discover how to introduce true Database DevOps, brought to you in partnership with Redgate

Topics:
database ,nosql ,mongodb ,crud api

Published at DZone with permission of A. Jesse Jiryu Davis, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}