MongoDB Update
A quick update to the developer notebook: have you tried using the MongoDB update command with-and-without the $set operator?
Generally, when you want to update a document in a collection, you will select from:
- updateMany: updates multiple documents in a collection based on a filtered match
- updateOne: updates the first document in a collection based on a filtered match
- replaceOne: replaces the schema of a document document based on a filtered match
However, the update
operation is also available, and it has significantly different behavior depending on whether or not you specify the $set operator in your query. For reference: MongoDB Update.
The Setup
Let’s suppose we have some clients that are described by the following JSON array:
[ { "name":"ABC Corp", "address":"123 ABC Lane" }, { "name":"XYZ Corp", "address":"789 XYZ Street" } ]
Populate a clients
DB with a collection of these documents and you will have the following:
{ "_id" : ObjectId("5fa..."), "name" : "ABC Corp", "address" : "123 ABC Lane" } { "_id" : ObjectId("5fa..."), "name" : "XYZ Corp", "address" : "789 XYZ Street" }
updateOne and updateMany Operations
The updateOne and updateMany operations require the $set
operator to be specified. Things work if you include $set
:
> db.blogclients.updateOne( {"_id": ObjectId("5fa...")}, {$set: {"name": "New Corp"}}) { "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 } > db.blogclients.find().pretty() { "_id" : ObjectId("5fa..."), "name" : "ABC Corp", "address" : "123 ABC Lane" } { "_id" : ObjectId("5fa..."), "name" : "New Corp", "address" : "789 XYZ Street" }
But, if you were to try and execute this command without the $set
, you’d receive:
> db.blogclients.updateOne( {"_id": ObjectId("5fa...")}, {"name": "Another New Corp"}) uncaught exception: Error: the update operation document must contain atomic operators : DBCollection.prototype.updateOne@src/mongo/shell/crud_api.js:565:19 @(shell):1:1
The same is true for updateMany. You could for example set a property for all documents where the name
property is specified:
> db.blogclients.updateMany( {"name":{$ne:""}}, {$set:{"confirmed": true}}) { "acknowledged" : true, "matchedCount" : 2, "modifiedCount" : 2 } > db.blogclients.find().pretty() { "_id" : ObjectId("5fa..."), "name" : "ABC Corp", "address" : "123 ABC Lane", "confirmed" : true } { "_id" : ObjectId("5fa..."), "name" : "New Corp", "address" : "789 XYZ Street", "confirmed" : true }
Though, if you were to try it without the $set
operator, you’d get the same error as with the updateOne
command:
> db.blogclients.updateMany( {"name":{$ne:""}}, {"prepared": true}) uncaught exception: Error: the update operation document must contain atomic operators : DBCollection.prototype.updateMany@src/mongo/shell/crud_api.js:655:19 @(shell):1:1
So, we see that updateOne and updateMany cannot be executed without specifying the $set
operator.
MongoDB Update
Unlike updateOne
and updateMany
, update
does support operations without the $set
operator.
If you specify the $set
operator, then it will update the first document in the collection that it matches. Basically, it behaves like updateOne
:
> db.blogclients.update({"confirmed": true},{$set:{launched: false}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.blogclients.find().pretty() { "_id" : ObjectId("5fa..."), "name" : "ABC Corp", "address" : "123 ABC Lane", "confirmed" : true, "launched" : false } { "_id" : ObjectId("5fa..."), "name" : "New Corp", "address" : "789 XYZ Street", "confirmed" : true }
Notice how the “launched” property is only specified the first matched document of “confirmed” equal to “true”.
Okay, so now, I want to set the “reviewed” property of the first “confirmed= true” document to false. But, I don’t specify the $set
operator. Unlike its “update” cousins, this is totally legal syntax, and as you see, it behaves more like the replaceOne
function. That is, it completely replaces the schema of the first matched element:
> db.blogclients.find().pretty() { "_id" : ObjectId("5fa..."), "name" : "ABC Corp", "address" : "123 ABC Lane", "confirmed" : true, "launched" : false } { "_id" : ObjectId("5fa..."), "name" : "New Corp", "address" : "789 XYZ Street", "confirmed" : true } > db.blogclients.update( {"confirmed": true},{launched: false}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.blogclients.find().pretty() { "_id" : ObjectId("5fa..."), "launched" : false } { "_id" : ObjectId("5fa..."), "name" : "New Corp", "address" : "789 XYZ Street", "confirmed" : true }
Maybe what you wanted…but maybe not. For me, I’ll generally just use the replaceOne
command if I intend to update a document’s schema in this manner.
Thanks for reading.
Categories: MongoDB, Software Development
Leave a Reply