Update query in mongodb - mongodb

Suppose I have this document in mongodb collection:
{
"_id": {
"playerId": "113069",
"tournamentId": "197831"
},
"__v": 0,
"playerName": "David WArner",
"pointsTotal": 426
}
I update it using this update query(using mongoose):
await PointsTotal.findOneAndUpdate(
{
_id: {
playerId: player._id,
tournamentId: tournamentId,
}
},
{
$set: {
playerName: playerName,
pointsTotal: totalPoints
},
},
{
upsert: true,
}
);
}
But, if the playerName and totalPoints is same as before updating, will it perform update operation to overwrite these fields and I am able to listen this update in change stream or not?

No, if there is no change to the document MongoDB will not perform a write, there will be no operation in the oplog, and nothing will appear in the change stream.

Related

MongoDB: Get mactched document from findOneAndUpdate before update

I'm performing a MongoDB query to update a document like below -
await this.activity.findOneAndUpdate(
{ _id: activityId },
{
$set: { isFlagged: //boolean_value },
},
);
In the update part of this query, is there a way to get the matched document from the previous step?
Basically, to do something like this -
const data = await this.activity.findOne({_id: activityId})
await this.activity.findOneAndUpdate(
{ _id: activityId },
{
$set: { isFlagged: !data.isFlagged }, //toggle between the previous boolean value
},
);
Is there a way to achieve this in a single query?
Use $not in an update with aggregation pipeline.
db.collection.update({
"activityId": 1
},
[
{
$set: {
isFlagged: {
$not: "$isFlagged"
}
}
}
])
Mongo Playground

modifiedCount is 1 even though the document didn't change

I am using a simple updateOne query where I pull an ObjectId out of an array.
await this.portfolioModel.updateOne(
{
_id: portfolioId,
'watchlist.custom._id': watchlistId
},
{
$pull: {
'watchlist.custom.$.assets': assetId
}
}
)
But I want to detect if the document actually changed or if the assetIds wasn't in the array to begin with. The problem is that even though the document hasn't changed MongoDB still returns modifiedCount with 1.
{
acknowledged: true,
modifiedCount: 1,
upsertedId: null,
upsertedCount: 0,
matchedCount: 1
}
I checked the document and the __v doen't increment with the updates so I don't know why MongoDB thinks the document has changed.
This is how the document looks (before and after update):
{
"_id": {
"$oid": "600188dab0b1c70050084e3e"
},
"watchlist": {
"all": [],
"custom": [
{
"assets": [],
"_id": {
"$oid": "624d7a18c89b3937212b3a55"
}
}
]
},
"__v": 18,
"createdAt": {
"$date": "2021-05-11T22:19:05.450Z"
},
"updatedAt": {
"$date": "2022-04-06T11:45:37.415Z"
}
}
I encountered this same issue using mongoose 6.3.5 and MongoDB serverless version 5.3.1.
The fix for me was removing {timestamps: true} from my schema. This causes the document to be 'modified' even if no change was made to its values. I believe it is because the 'updatedAt' value would change whenever updateOne() would access the document.

How to convert string field to object Id inside embedded document of already existing record?

I have a collection tblTesting with records saved like
{
"_id": ObjectId("5de9f044af647f21780056e1"),
"name": "abc",
"creditAccountDetails": {
"creditAccountNumber": "0200040671890190",
"creditAccountNumberId": "5db2efb5590a065abc006b12"
}
}
The embedded document "creditAccountDetails" has been wrongly saved. Now I am trying to update them by using mongodb command like
db.tblTesting.updateMany
{},
[
{ $set: { creditAccountDetails: [[ 'creditAccountNumberId' : ObjectId ($creditAccountNumber) ]] } },
{}
]
)
Basically I want that the command should be able to update all the records like
{
"_id": ObjectId("5de9f044af647f21780056e1"),
"name": "abc",
"creditAccountDetails":[ {
"creditAccountNumber": "0200040671890190",
"creditAccountNumberId": ObjectId("5db2efb5590a065abc006b12")
}
]
}
Please help!!!
Note that I am using mongo db 4.0
Since you are on mongodb version 4.0, which does not allow referring document fields in an update. A way to do this is iterate via cursor on the collection and update the field.
var cursor = db.collection.find({});
while (cursor.hasNext()) {
var doc = cursor.next();
db.collection.updateOne(
{
_id: doc._id
},
{
$set: {
creditAccountDetails: [
{
creditAccountNumber: doc.creditAccountDetails.creditAccountNumber,
creditAccountNumberId: ObjectId(
doc.creditAccountDetails.creditAccountNumberId
)
}
]
}
}
);
}
For readers who are on Mongodb 4.2.0+ which allows using aggregation pipeline ops in update methods updateOne, updateMany where document fields can be used as part of $set.
db.collection.updateMany({}, [
{
$set: {
creditAccountDetails: [
{
creditAccountNumber: "$creditAccountDetails.creditAccountNumber",
creditAccountNumberId: {
$toObjectId: "$creditAccountDetails.creditAccountNumberId"
}
}
]
}
}
]);
tblTesting's schema should have define type ObjectId for creditAccountNumberId
below example I used mongoose
const tblTesting = mongoose.Schema({
....
creditAccountNumberId: {
type: mongoose.Schema.Types.ObjectId,
},
....
}, {
collection: 'tblTesting',
timestamps: true,
strict: false,
})

FindAndModify, return array of Objects

I'm trying to modify multiple documents with findAndModify, and then return the new Documents modified.
My query is:
db.users.findAndModify({
query: {
_id: {
$in: [
ObjectId("54061f3c27afac4b44688c1d"),
ObjectId("54061f3c27afac4b44688c1e")
]
}
},
update: {
$inc: {
i: 1
}
},
new: true
});
but can retrieve only one document. My aim is to modify multi documents, and return all of them. Is it possible to retrieve an array of documents?
As explained in this page in the documents, findAndModify "modifies and returns a single document". If you want to modify multiple documents using findAndModify you will have to run it once per document. In the mongo shell, you can achieve that with some JavaScript like the following:
var oids = [ObjectId("54061f3c27afac4b44688c1d"),
ObjectId("54061f3c27afac4b44688c1e")];
docs = [];
for (var i in oids) {
id = oids[i];
doc = db.e.findAndModify({
"query": { "_id": id },
"update": { "$inc": { "i": 1 }},
"new": true
});
docs.push(doc);
}
printjson(docs);
The other option would be to run update using multi as an option and then retrieve the documents. Your code would look something like the following:
db.users.update(
{ "_id": {
"$in": [ObjectId("54061f3c27afac4b44688c1d"),
ObjectId("54061f3c27afac4b44688c1e")]
}
},
{ "$inc": { "i": 1 }},
{ "multi": true }
);
db.users.find(
{ "_id": {
"$in": [ObjectId("54061f3c27afac4b44688c1d"),
ObjectId("54061f3c27afac4b44688c1e")]
}
}
);

Mongo Update $set not working

I'm trying to mass update some mongo documents.
I'm using the query
db.articles.update(
{
'categories.id': ObjectId("51cd5272222wb6zs464fa4d9"),
'source.importer': 'pa'
},
{
$set :
{
'source.expires-at': ISODate("2014-01-01T08:39:45Z")
}
}
)
This query does not update the source.expires-at field, however the where part of the statement works fine.
The document structure is
{
"_id": ObjectId("5211dc100000044707000015"),
"categories": {
"0": {
"id": ObjectId("51cd5272222wb6zs464fa4d9")
}
},
"source": {
"importer": "pa",
"expires-at": ISODate("2013-09-18T08:49:32.0Z")
}
}
Try this:
db.articles.update(
{
'categories.id': ObjectId("51cd5272222wb6zs464fa4d9"),
'source.importer': 'pa'
},
{
$set: {
'source.expires-at': ISODate("2014-01-01T08:39:45Z")
}
},
{ multi: true }
)
You will need to pass additional option argument {multi: true};
Look in to this https://education.10gen.com/courses/10gen/M101JS/2013_August/courseware/CRUD/Multi-update/