Difference between the UpdateOne() and findOneAndUpdate Methods in Mongo DB - mongodb

What is the difference between the UpdateOne() and the findOneAndUpdate() methods in Mongo DB?
I can't seem o understand their differences. Would appreciate it if a demonstrative example using UpdateOne() and findOneAndUpdate could be used.

Insert a document in an otherwise empty collection using the mongo-shell to start:
db.users.insertOne({name: "Jack", age: 11})
UpdateOne
db.users.updateOne({name: "Jack"}, {$set: {name: "Joe"}})
This operation returns an UpdateResult.
{ acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0 }
FindOneAndUpdate
db.users.findOneAndUpdate({name: "Joe"}, {$set: {name: "Jill"}})
This operation returns the document that was updated.
{ _id: ObjectId("62ecf94510fc668e92f3cecf"),
name: 'Joe',
age: 11 }
FindOneAndUpdate is preferred when you have to update a document and fetch it at the same time.

If you need to return the New Document instead of the original document, you can use one of these ways:
db.users.findOneAndUpdate(
{name: "Joe"},
{$set: {name: "Jill"}},
{returnDocument: "after"}
)
returnDocument: "before" --> returns the original document (default).
returnDocument: "after" --> returns the updated document.
Or
db.users.findOneAndUpdate(
{name: "Joe"},
{$set: {name: "Jill"}},
{returnNewDocument: true}
)
returnNewDocument: false --> returns the original document (default).
returnNewDocument: true --> returns the updated document.
Note: If both options are set (returnDocument and returnNewDocument), returnDocument takes precedence.

Related

Mongoose update field if another array field has a size bigger than 5

I'm new to Mongoose and I cannot find a way to achieve a certain update query.
I have the next schema:
{
usersJoined: [{
type: String
}],
status: {
type: Number,
default: 0
},
}
I want to update the status to 1 when the size of the usersJoined array is 5.
I am using the following query to query for the right document:
Model.findOneAndUpdate(
{
status: 0,
usersJoined: {$ne: user}
},
{
$push: {usersJoined: user}
}
);
I want to update the status to 1 when the size of the usersJoined
array is 5
Use array $size operator to filter records, findOneAndUpdate method will update the first matched record. Refer to document findOneAndUpdate. If this not suitable for your usecase check additional update methods
db.collection.findOneAndUpdate(
{"userJoined": {"$size": 5}},
{$set: {"status": 1}},
{returnNewDocument: true} //if true, returns the updated document.
);
Example

How to remove a key with any value in mongo collection? [duplicate]

{
name: 'book',
tags: {
words: ['abc','123'],
lat: 33,
long: 22
}
}
Suppose this is a document. How do I remove "words" completely from all the documents in this collection? I want all documents to be without "words":
{
name: 'book',
tags: {
lat: 33,
long: 22
}
}
Try this: If your collection was 'example'
db.example.update({}, {$unset: {words:1}}, false, true);
Refer this:
http://www.mongodb.org/display/DOCS/Updating#Updating-%24unset
UPDATE:
The above link no longer covers '$unset'ing. Be sure to add {multi: true} if you want to remove this field from all of the documents in the collection; otherwise, it will only remove it from the first document it finds that matches. See this for updated documentation:
https://docs.mongodb.com/manual/reference/operator/update/unset/
Example:
db.example.update({}, {$unset: {words:1}} , {multi: true});
In the beginning, I did not get why the question has a bounty (I thought that the question has a nice answer and there is nothing to add), but then I noticed that the answer which was accepted and upvoted 15 times was actually wrong!
Yes, you have to use $unset operator, but this unset is going to remove the words key which does not exist for a document for a collection. So basically it will do nothing.
So you need to tell Mongo to look in the document tags and then in the words using dot notation. So the correct query is.
db.example.update(
{},
{ $unset: {'tags.words':1}},
false, true
)
Just for the sake of completion, I will refer to another way of doing it, which is much worse, but this way you can change the field with any custom code (even based on another field from this document).
db.example.updateMany({},{"$unset":{"tags.words":1}})
We can also use this to update multiple documents.
db.collection.updateMany({}, {$unset: {"fieldName": ""}})
updateMany requires a matching condition for each document, since we are passing {} it is always true. And the second argument uses $unset operator to remove the required field in each document.
To remove or delete field in MongoDB
For single Record
db.getCollection('userData').update({}, {$unset: {pi: 1}})
For Multi Record
db.getCollection('userData').update({}, {$unset: {pi: 1}}, {multi: true})
Starting in Mongo 4.2, it's also possible to use a slightly different syntax:
// { name: "book", tags: { words: ["abc", "123"], lat: 33, long: 22 } }
db.collection.updateMany({}, [{ $unset: ["tags.words"] }])
// { name: "book", tags: { lat: 33, long: 22 } }
Since the update method can accept an aggregation pipeline (note the squared brackets signifying the use of an aggregation pipeline), it means the $unset operator used here is the aggregation one (as opposed to the "query" one), whose syntax takes an array of fields.
The solution for PyMongo (Python mongo):
db.example.update({}, {'$unset': {'tags.words':1}}, multi=True);
I was trying to do something similar to this but instead remove the column from an embedded document. It took me a while to find a solution and this was the first post I came across so I thought I would post this here for anyone else trying to do the same.
So lets say instead your data looks like this:
{
name: 'book',
tags: [
{
words: ['abc','123'],
lat: 33,
long: 22
}, {
words: ['def','456'],
lat: 44,
long: 33
}
]
}
To remove the column words from the embedded document, do this:
db.example.update(
{'tags': {'$exists': true}},
{ $unset: {'tags.$[].words': 1}},
{multi: true}
)
or using the updateMany
db.example.updateMany(
{'tags': {'$exists': true}},
{ $unset: {'tags.$[].words': 1}}
)
The $unset will only edit it if the value exists but it will not do a safe navigation (it wont check if tags exists first) so the exists is needed on the embedded document.
This uses the all positional operator ($[]) which was introduced in version 3.6
To Remove a field you can use this command(this will be applied to all the documents in the collection) :
db.getCollection('example').update({}, {$unset: {Words:1}}, {multi: true});
And to remove multiple fields in one command :
db.getCollection('example').update({}, {$unset: {Words:1 ,Sentences:1}}, {multi: true});
Because I kept finding this page when looking for a way to remove a field using MongoEngine, I guess it might be helpful to post the MongoEngine way here too:
Example.objects.all().update(unset__tags__words=1)
In mongoDB shell this code might be helpful:
db.collection.update({}, {$unset: {fieldname: ""}} )
By default, the update() method updates a single document. Set the Multi Parameter to update all documents that match the query criteria.
Changed in version 3.6.
Syntax :
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ]
}
)
Example :
db.getCollection('products').update({},{$unset: {translate:1, qordoba_translation_version:1}}, {multi: true})
In your example :
db.getCollection('products').update({},{$unset: {'tags.words' :1}}, {multi: true})
And for mongomapper,
Document: Shutoff
Field to remove: shutoff_type
Shutoff.collection.update( {}, { '$unset' => { 'shutoff_type': 1 } }, :multi => true )
It's worth mentioning, if you are using Typegoose/Mongoose your field needs to be declared in your model otherwise $unset will silently fail.
After a refactoring of a database this caught me unaware. The redundant fields were no longer in my model and I was running an $unset but nothing was happening.
So if you are doing some refactoring make sure you $unset your redundant fields before removing them from the model.
{
name: 'book',
tags: {
words: ['abc','123'],
lat: 33,
long: 22
}
}
Ans:
db.tablename.remove({'tags.words':['abc','123']})
Checking if "words" exists and then removing from the document
db.users.update({"tags.words" :{$exists: true}},
{$unset:{"tags.words":1}},false,true);
true indicates update multiple documents if matched.
you can also do this in aggregation by using project at 3.4
{$project: {"tags.words": 0} }
To reference a package and remove various "keys",
try this
db['name1.name2.name3.Properties'].remove([
{
"key" : "name_key1"
},
{
"key" : "name_key2"
},
{
"key" : "name_key3"
}
)]

Auto calculating fields in mongodb

Let's say there are documents in MongoDB, that look something like this:
{
"lastDate" : ISODate("2013-14-01T16:38:16.163Z"),
"items":[
{"date":ISODate("2013-10-01T16:38:16.163Z")},
{"date":ISODate("2013-11-01T16:38:16.163Z")},
{"date":ISODate("2013-12-01T16:38:16.163Z")},
{"date":ISODate("2013-13-01T16:38:16.163Z")},
{"date":ISODate("2013-14-01T16:38:16.163Z")}
]
}
Or even like this:
{
"allAre" : false,
"items":[
{"is":true},
{"is":true},
{"is":true},
{"is":false},
{"is":true}
]
}
The top level fields "lastDate" and "allAre" should be recalculated every time the data in array changes. "lastDate" should be the biggest "date" of all. "allAre" should be equal to true only if all the items have "is" as true.
How should I build my queries to achieve such a behavior with MongoDB?
Is it considered to be a good practice to precalculate some values on insert, instead of calculating them during the get request?
MongoDB cannot make what you are asking for with 1 query.
But you can make it in two-step query.
First of all, push the new value into the array:
db.Test3.findOneAndUpdate(
{_id: ObjectId("58047d0cd63cf401292fe0ad")},
{$push: {"items": {"date": ISODate("2013-01-27T16:38:16.163+0000")}}},
{returnNewDocument: true},
function (err, result) {
}
);
then update "lastDate" only if is less then the last Pushed.
db.Test3.findOneAndUpdate (
{_id: ObjectId("58047d0cd63cf401292fe0ad"), "lastDate":{$lt: ISODate("2013-01-25T16:38:16.163+0000")}},
{$set: {"lastDate": ISODate("2013-01-25T16:38:16.163+0000")}},
{returnNewDocument: true},
function (err, result) {
}
);
the second parameter "lastDate" is needed in order to avoid race condition. In this way you can be sure that inside "lastDate" there are for sure the "highest date pushed".
Related to the second problem you are asking for you can follow a similar strategy. Update {"allAre": false} only if {"_id":yourID, "items.is":false)}. Basically set "false" only if some child of has a value 'false'. If you don't found a document with this property then do not update nothing.
// add a new Child to false
db.Test4.findOneAndUpdate(
{_id: ObjectId("5804813ed63cf401292fe0b0")},
{$push: {"items": {"is": false}}},
{returnNewDocument: true},
function (err, result) {
}
);
// update allAre to false if some child is false
db.Test4.findOneAndUpdate (
{_id: ObjectId("5804813ed63cf401292fe0b0"), "items.is": false},
{$set: {"allAre": false}},
{returnNewDocument: true},
function (err, result) {
}
);

Increment a {key: value} within an array of objects with MongoDB Mongoose driver

I have a document that looks similar to this:
{
'user_id':'{1231mjnD-32JIjn-3213}',
'name':'John',
'campaigns':
[
{
'campaign_id':3221,
'start_date':'12-01-2012',
'worker_id': '00000'
},
{
'campaign_id':3222,
'start_date':'13-01-2012',
'worker_id': '00000'
},
...
etc
...
]
}
Say I want to increment the campaign_id of element 7 in the campaigns array.
Using the dot with the mongoose driver, I thought I could do this:
camps.findOneAndUpdate({name: "John"}, {$inc: {"campaigns.7.campaign_id": 1}}, upsert: true, function(err, camp) {...etc...});
This doesn't increment the campaign_id.
Any suggestions on incrementing this?
You've got the right idea, but your upsert option isn't formatted correctly.
Try this:
camps.findOneAndUpdate(
{name: "John"},
{$inc: {"campaigns.7.campaign_id": 1}},
{upsert: true},
function(err, camp) {...etc...});

How to remove a field completely from a MongoDB document?

{
name: 'book',
tags: {
words: ['abc','123'],
lat: 33,
long: 22
}
}
Suppose this is a document. How do I remove "words" completely from all the documents in this collection? I want all documents to be without "words":
{
name: 'book',
tags: {
lat: 33,
long: 22
}
}
Try this: If your collection was 'example'
db.example.update({}, {$unset: {words:1}}, false, true);
Refer this:
http://www.mongodb.org/display/DOCS/Updating#Updating-%24unset
UPDATE:
The above link no longer covers '$unset'ing. Be sure to add {multi: true} if you want to remove this field from all of the documents in the collection; otherwise, it will only remove it from the first document it finds that matches. See this for updated documentation:
https://docs.mongodb.com/manual/reference/operator/update/unset/
Example:
db.example.update({}, {$unset: {words:1}} , {multi: true});
In the beginning, I did not get why the question has a bounty (I thought that the question has a nice answer and there is nothing to add), but then I noticed that the answer which was accepted and upvoted 15 times was actually wrong!
Yes, you have to use $unset operator, but this unset is going to remove the words key which does not exist for a document for a collection. So basically it will do nothing.
So you need to tell Mongo to look in the document tags and then in the words using dot notation. So the correct query is.
db.example.update(
{},
{ $unset: {'tags.words':1}},
false, true
)
Just for the sake of completion, I will refer to another way of doing it, which is much worse, but this way you can change the field with any custom code (even based on another field from this document).
db.example.updateMany({},{"$unset":{"tags.words":1}})
We can also use this to update multiple documents.
db.collection.updateMany({}, {$unset: {"fieldName": ""}})
updateMany requires a matching condition for each document, since we are passing {} it is always true. And the second argument uses $unset operator to remove the required field in each document.
To remove or delete field in MongoDB
For single Record
db.getCollection('userData').update({}, {$unset: {pi: 1}})
For Multi Record
db.getCollection('userData').update({}, {$unset: {pi: 1}}, {multi: true})
Starting in Mongo 4.2, it's also possible to use a slightly different syntax:
// { name: "book", tags: { words: ["abc", "123"], lat: 33, long: 22 } }
db.collection.updateMany({}, [{ $unset: ["tags.words"] }])
// { name: "book", tags: { lat: 33, long: 22 } }
Since the update method can accept an aggregation pipeline (note the squared brackets signifying the use of an aggregation pipeline), it means the $unset operator used here is the aggregation one (as opposed to the "query" one), whose syntax takes an array of fields.
The solution for PyMongo (Python mongo):
db.example.update({}, {'$unset': {'tags.words':1}}, multi=True);
I was trying to do something similar to this but instead remove the column from an embedded document. It took me a while to find a solution and this was the first post I came across so I thought I would post this here for anyone else trying to do the same.
So lets say instead your data looks like this:
{
name: 'book',
tags: [
{
words: ['abc','123'],
lat: 33,
long: 22
}, {
words: ['def','456'],
lat: 44,
long: 33
}
]
}
To remove the column words from the embedded document, do this:
db.example.update(
{'tags': {'$exists': true}},
{ $unset: {'tags.$[].words': 1}},
{multi: true}
)
or using the updateMany
db.example.updateMany(
{'tags': {'$exists': true}},
{ $unset: {'tags.$[].words': 1}}
)
The $unset will only edit it if the value exists but it will not do a safe navigation (it wont check if tags exists first) so the exists is needed on the embedded document.
This uses the all positional operator ($[]) which was introduced in version 3.6
To Remove a field you can use this command(this will be applied to all the documents in the collection) :
db.getCollection('example').update({}, {$unset: {Words:1}}, {multi: true});
And to remove multiple fields in one command :
db.getCollection('example').update({}, {$unset: {Words:1 ,Sentences:1}}, {multi: true});
Because I kept finding this page when looking for a way to remove a field using MongoEngine, I guess it might be helpful to post the MongoEngine way here too:
Example.objects.all().update(unset__tags__words=1)
In mongoDB shell this code might be helpful:
db.collection.update({}, {$unset: {fieldname: ""}} )
By default, the update() method updates a single document. Set the Multi Parameter to update all documents that match the query criteria.
Changed in version 3.6.
Syntax :
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ]
}
)
Example :
db.getCollection('products').update({},{$unset: {translate:1, qordoba_translation_version:1}}, {multi: true})
In your example :
db.getCollection('products').update({},{$unset: {'tags.words' :1}}, {multi: true})
And for mongomapper,
Document: Shutoff
Field to remove: shutoff_type
Shutoff.collection.update( {}, { '$unset' => { 'shutoff_type': 1 } }, :multi => true )
It's worth mentioning, if you are using Typegoose/Mongoose your field needs to be declared in your model otherwise $unset will silently fail.
After a refactoring of a database this caught me unaware. The redundant fields were no longer in my model and I was running an $unset but nothing was happening.
So if you are doing some refactoring make sure you $unset your redundant fields before removing them from the model.
{
name: 'book',
tags: {
words: ['abc','123'],
lat: 33,
long: 22
}
}
Ans:
db.tablename.remove({'tags.words':['abc','123']})
Checking if "words" exists and then removing from the document
db.users.update({"tags.words" :{$exists: true}},
{$unset:{"tags.words":1}},false,true);
true indicates update multiple documents if matched.
you can also do this in aggregation by using project at 3.4
{$project: {"tags.words": 0} }
To reference a package and remove various "keys",
try this
db['name1.name2.name3.Properties'].remove([
{
"key" : "name_key1"
},
{
"key" : "name_key2"
},
{
"key" : "name_key3"
}
)]