I am new to mongo so excuse me if it's a noobish question. I have wrongfully updated a specific flag in mongo with "true"/"false" (type strings). I want a query so that I could update my collection and change the type of the flag from string "true" to boolean true.
Example:
{flag: "true"} to { flag : true}
So I have 2 questions:
Can I do that with a query?
If I can, how?
For relatively small collections, perform an update if the type of field is string:
db.collection.find({ "flag": { $type : 2 } }).forEach(function (doc){
var isTrueSet = (doc.flag === "true");
doc.flag = isTrueSet; // convert field to Boolean
db.collection.save(doc);
});
For medium/large collections you can use the bulkWrite API as
var cursor = db.collection.find({ "flag": { "$exists": true, "$type": 2 } }),
ops = [];
cursor.forEach(function(doc){
var isTrueSet = (doc.flag === "true");
ops.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": { "$set": { "flag": isTrueSet } }
}
});
if (ops.length == 1000) {
db.collection.bulkWrite(ops);
ops = [];
}
});
if (ops.length > 0) { db.collection.bulkWrite(ops); }
Just call these two queries:
db.coll.update({
flag: "true"
}, {
$set: {flag: true}
}, { multi: true })
db.coll.update({
flag: "false"
}, {
$set: {flag: false}
}, { multi: true })
First one changes all "true" to true, second you will get it.
For a medium/big collection this will work significantly faster than already suggested foreach statement.
See MongoDB Manual - Modify Documents. Example:
db.collectionName.update({_id: "yourid"}, {$set: {flag : true}})
Strings can be converted to boolean in MongoDB v4.0 using $toBool operator. In this case
db.col.aggregate([
{
$project: {
_id: 0,
flagBool: { $toBool: "$flag" }
}
}
])
Outputs:
{ "flagBool" : true }
Related
I am trying to update multiple sub documents by given array of sub documents id's. I tried multiple approaches but it's not working.
In my scenario i need to update multiple sub documents by given array of id's. Here is my query as below:
Approach 1. (No elements were updating)
var updated = await ModelName.update(
{
'subDocArray._id' : { $in: req.body.elementId }
},
{
$set: {
'subDocArray.$[elem].abc': req.body.abcValue,
'subDocArray.$[elem].xyz': req.body.xyzValue
},
},{ "arrayFilters": [{ "elem._id": { $in: req.body.elementId } }], "multi": true, "upsert": true }
).lean().exec();
Approach 2: (Only First occurred element is updating)
var updated = await ModelName.update(
{
'subDocArray._id' : { $in: req.body.elementId }
},
{
$set: {
'subDocArray.$.abc': req.body.abcValue,
'subDocArray.$.xyz': req.body.xyzValue
},
},{ multi: true}
).exec();
Here req.body.elementId is array of sub doc id's.
Approach 1 was almost right. I was passing array of elementId's are which are in string format so i converted them in ObjectId form and then it works.
var arrOfObjectId = [];
req.body.elementId.forEach(elem => {
arrOfObjectId.push(Types.ObjectId(elem))
});
To find the difference between both of the array i printed both in console which were showing like below:
console.log(req.body.elementId)
Result: ['xxxxxxxxxxxxxxxxxxxxxxxx','yyyyyyyyyyyyyyyyyyyyyyyy'] //WRONG
console.log(arrOfObjectId)
Result: [ObjectId('xxxxxxxxxxxxxxxxxxxxxxxx'),ObjectId('yyyyyyyyyyyyyyyyyyyyyyyy')] //RIGHT
var updated = await ModelName.update(
{
'subDocArray._id' : { $in: arrOfObjectId }
},
{
$set: {
'subDocArray.$[elem].abc': req.body.abcValue,
'subDocArray.$[elem].xyz': req.body.xyzValue
},
},{ "arrayFilters": [{ "elem._id": { $in: arrOfObjectId } }], "multi": true, "upsert": true }
).lean().exec();
I have a MongoDB data like this
{
_id: "5aa8f087e1eee70004a99e1d"
users: [{facebookId: "-1", unread: 0},{facebookId: "323232321", unread: 1}]
}
I want to increment users.unread where facebookId in not "-1"
I tried this query
chat.update({ "_id": { "$in": chatId }, "users.facebookId": { "$ne": "-1" } },{"$inc": { "users.$.unread": 1 } }, { multi: true, upsert: true }, function(err, data) {
if (err) throw err;
console.log(data);
});
From MongoDB docs:
If the query matches the array using a negation operator, such as $ne, $not, or $nin, then you cannot use the positional operator to update values from this array.
However, if the negated portion of the query is inside of an $elemMatch expression, then you can use the positional operator to update this field
Below code should work if you want to increment first matching element:
db.chat.update({ "_id":"5aa8f087e1eee70004a99e1d", "users": { $elemMatch: { "facebookId": { "$ne": "-1" } }}},{"$inc": { "users.$.unread": 1 }})
Otherwise you can use array filters in MongoDB 3.6
db.col.update({ "_id":"5aa8f087e1eee70004a99e1d" }, { "$inc" : { "users.$[cond].unread" : 1 } }, { arrayFilters: [ { "cond.facebookId": { $ne: "-1" } } ] })
This query is doing the job fine :
db.collection.update(
{ "_id": oneIdProvided },
{ $inc: { "field": 5 } },{ upsert: true }
)
Now I would like to do the same operation multiple time with different IDs, I thought the good way was to use $in and therefore I tried :
db.collection.update(
{ "_id": { $in: oneArrayOfIds} },
{ $inc: { "field": 5 } },{ upsert: true }
)
Problem is : if one of the provided ID in the array is not existing in the collection, a new document is created (which is what I want) but will be attributed an automatic ID, not using the ID I provided and was looking for.
One solution I see could be to do first an insert query with my array of ID (those already existing would not be modified) and then doing my update query with upsert: false
Do you see a way of doing that in only one query ?
We can do this by performing multiple write operations using the bulkWrite() method.
function* range(start, end, step) {
for (let val=start; val<end; val+=step)
yield val
}
let oneArrayOfIds; // For example [1, 2, 3, 4]
let bulkOp = oneArrayOfIds.map( id => {
return {
"updateOne": {
"filter": { "_id": id },
"update": { "$set": { "field": 5 } },
"upsert": true
}
};
});
const limit = 1000;
const len = bulkOp.length;
let chunks = [];
if (len > 1000) {
for (let index of range(0, len, limit)) {
db.collection.bulkWrite(bulkOp.slice(index, index+limit));
}
} else {
db.collection.bulkWrite(bulkOp);
}
{
"_id" : 1,
"users" : 2329255
},
{
"_id" :2,
"users" : 2638831
}
how to update all documents users field divided by 100.
result will be
{
"_id" : 1,
"users" : 23292.55
},
{
"_id" : 2,
"users" : 26388.31
}
db.coll.update({}, {$set: {'users': {'$divide': ['$users', 100]}}})
----its not working
Try below query:
db.coll.find().snapshot().forEach(
function (e) {
e.users = e.users/100;
// save the updated document
db.coll.save(e);
}
)
Above query will change/update the data in DB. If you want to fetch records with devided value then use $ project:
db.coll.aggregate(
[
{ $project: { users: { $divide: [ "$users", 100 ] } } }
]
)
this will not update the data but will return you desired value.
Use as per your requirement.
The $divide operator is only valid for the aggregate() function, not the update() function. What you want to do is use the aggregate() method to create a computed field, iterate the results from
the aggregate() cursor to create bulk update operations that you can send to the server in one request, rather that sending each update request with each item in the result.
The following example demonstrates this:
var bulkUpdateOps = [];
db.coll.aggregate([
{ "$match": { "users": { "$exists": true } } }
{
"$project": {
"computed_field": {
"$divide": ["$users", 100]
}
}
}
]).forEach(function(doc){
bulkUpdateOps.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": { "$set": { "users": doc.computed_field } }
}
});
if (bulkUpdateOps.length === 500) {
db.coll.bulkWrite(bulkUpdateOps);
bulkUpdateOps = [];
}
});
if (bulkUpdateOps.length > 0) db.coll.bulkWrite(bulkUpdateOps);
Or for MongoDB 2.6.x and 3.0.x releases, use this version of Bulk operations:
var bulk = db.coll.initializeUnorderedBulkOp(),
counter = 0;
db.coll.aggregate([
{ "$match": { "users": { "$exists": true } } }
{
"$project": {
"computed_field": {
"$divide": ["$users", 100]
}
}
}
]).forEach(function(doc) {
bulk.find({ "_id": doc._id })
.updateOne({ "$set": { "users": doc.computed_field } });
if (counter % 500 === 0) {
bulk.execute();
bulk = db.coll.initializeUnorderedBulkOp();
}
});
if (counter % 500 !== 0 ) bulk.execute();
The Bulk operations API in both cases will help reduce the IO load on the server by sending the requests only once in every 500 documents in the collection to process.
I wonder if mongoose has some method to update multiple documents by id set. For example:
for (var i = 0, l = ids.length; i < l; i++) {
Element.update({'_id': ids[i]}, {'visibility': visibility} ,function(err, records){
if (err) {
return false;
} else {
return true;
};
});
};
What i want to know, that if mongoose can do something like this:
Element.update({'_id': ids}, {'visibility': visibility}, {multi: true} ,function(err, records){
if (err) {
return false;
}
});
where ids is an array of ids, like ['id1', 'id2', 'id3'] - sample array.
Same question for find.
Most probably yes. And it is called using $in operator in mongodb query for update.
db.Element.update(
{ _id: { $in: ['id1', 'id2', 'id3'] } },
{ $set: { visibility : yourvisibility } },
{multi: true}
)
All you need is to find how to implement $in in mongoose.
in updateMany function no need of { multi: true }
db.collectionName.updateMany(
{
_id:
{
$in:
[
ObjectId("your object id"),
ObjectId("your object id")
]
}
},
{
$inc: { quantity: 100 }
})
I want to add one more point, you can use $in to fetch multiple document
db.collectionName.find(
{
_id:
{
$in:
[
ObjectId("your object id"),
ObjectId("your object id")
]
}
})
Updates all documents that match the specified filter for a collection.
let ids = ["kwwe232244h3j44jg3h4", "23h2u32g2h3b3hbh", "fhfu3h4h34u35"];
let visibility = true;
Element.updateMany({_id: {$in: ids}},
{ $set: { visibility } },
{multi: true} ,
function(err, records){
if (err) {
return false;
}
});
know more