MongoDB update only a few records, create a new attribute, assign a value - mongodb

I wasn't lucky finding any help on this anywhere else.
Basically I want to update only the few documents that do not have a given attribute already.
And the value for the update comes from a field that is already on the document.
This is what I tried but it didn't like the "from a field already on the document" part. Saying Cn doesn't exist.
db.getCollection('test').update(
// query
{ "id2" : { $exists: false } },
// update
{ id2: Cn },
// options
{
"multi" : true, // update all documents
"upsert" : false // don't insert new documents
}
);
Here is my test data
/* 1 */
{
"_id" : ObjectId("5912132c4a58677726d37168"),
"Cn" : "CA",
"id2" : "CAAB",
"Prov" : "AB"
}
/* 2 */
{
"_id" : ObjectId("591213404a58677726d37172"),
"Cn" : "CA",
"id2" : "CANZ",
"Prov" : "NZ"
}
/* 3 */
{
"_id" : ObjectId("591213534a58677726d37180"),
"Cn" : "CA",
"id2" : "CAMB",
"Prov" : "MB"
}
/* 4 */
{
"_id" : ObjectId("591213674a58677726d3718c"),
"Cn" : "US"
}
/* 5 */
{
"_id" : ObjectId("591213894a58677726d371a3"),
"Cn" : "MX"
}
All this should do is create a id2 on US & MX and give those new id2 attributes the corresponding values 'US' & 'MX'.
This would not be a big deal but I have able 144 countries & 10,000+ documents to add id2 to.

You can't update multiple items with an operator that only contains expressions, and self referencing doesn't work either.
Check out this question for work around solutions to get what you need done: Update MongoDB field using value of another field

You can try this :
db.your_collection.aggregate( [
{ $match : { id2 : { $exists: false }}},
{ $addFields: {
id2 : "$Cn"
}
},
{ $out : "your_collection" }
]);
This will remove all documents in collection which have id2 field.
You can do this in mongoose in a longer way :
db.getCollection('test').find({ "id2" : { $exists: false } }, function(err, docs){
if(err){
//handle errror
}
else if(!docs){
// no docs found
}
else{
for(var i=0; i< docs.length; i++){
db.getCollection('test')
.findByIdAndUpdate(
docs[i]._id
, { $set : { id2 : docs[i].Cn}}
, { new : true}
, function(err, doc){
if(err){
//handle error
}
else{
//doc was updated
}
});
}
}
});

I would not have gotten to this without Mihir Bhende pushing. Thanks for that.
var c = db.getCollection('test').find(
{ "id2" : { $exists: false }}
);
c.forEach(function(myDoc) {
print("doc", myDoc.Cn);
db.getCollection('test').update( {_id: myDoc._id}, {$set: { "id2": myDoc.Cn }}, function (err) {
if (err) { print("err"); }
});
});

Related

MongoDB - Update an Key

I have been trying to update an Object for this collection. Below is the collection. Looking for Server 3.6 version.
Here The ask is Need to update the class name from "HISTORY" to " HISTORY_NEW". Need to do, for some students in the class. Need a query that will select all student records in student collection with "HISTORY" class in it and update them to "HISTORY_NEW ". I have around 30,000 records and not getting a bulk update method.
{
"_id" : ObjectId("611f90aa43f77a728879c395"),
"studentId" : "stu1",
"classes" : {
"History" : {
"TeacherName" : "T1",
"Marks" : [
{
"Internal": 15
}
]
},
"Geography" : {
"TeacherName" : "T2",
"Marks" : [
{
"Internal" : 20
}
]
}
},
"updateDate" : ISODate("2021-10-12T11:40:47.156Z")
}
This is the result I am expecting
{
"_id" : ObjectId("611f90aa43f77a728879c395"),
"studentId" : "stu1",
"classes" : {
"HISTORY_NEW" : {
"TeacherName" : "T1",
"Marks" : [
{
"Internal": 15
}
]
},
"Geography" : {
"TeacherName" : "T2",
"Marks" : [
{
"Internal" : 20
}
]
}
},
"updateDate" : ISODate("2021-10-12T11:40:47.156Z")
}
.Or is that even possible with the kind of collection above or going via code route?
So far this is what I have, without any success.
Get all students' Ids and then update the Class name. But that is also not working and don't think it is smart to update DB 30,000 times.
var studentIds =[];
db.studentSubject.find({"classes.History":{$exists:true}})
.forEach(function(u) { studentIds.push(u.studentId) })
studentIds.forEach(function(studentId) {
var result;
try {
result =db.studentSubject.updateOne(
{studentId:studentId},
{ $set : {"classes.History": "HISTORY_NEW",}},
{ upsert: false});
} catch (e) {
print(e);
}
});
From your scenario, you need $rename operator.
As discussed in the comment, you don't need to fetch each document to get studentId and then pass it to update each document. Just bulk update by checking the document has classes.History field.
db.collection.update({
"classes.History": {
$exists: true
}
},
{
$rename: {
"classes.History": "classes.HISTORY_NEW"
}
},
{
upsert: false,
multi: true
})
Sample Mongo Playground

Using MongoDB $set to update multiple subdocuments

I have such Article-documents:
{
"_id" : "rNiwdR8tFwbTdr2oX",
"createdAt" : ISODate("2018-08-25T12:23:25.797Z"),
"title" : "Happy",
"lines" : [
{
"id" : "5efa6ad451048a0a1807916c",
"text" : "Test 1",
"align" : "left",
"indent" : 0
},
{
"id" : "ae644f39553d46f85c6e1be9",
"text" : "Test 2"
},
{
"id" : "829f874878dfd0b47e9441c2",
"text" : "Test 3"
},
{
"id" : "d0a46ef175351ae1dec70b9a",
"text" : "Test 4"
},
{
"id" : "9bbc8c8d01bc7029220bed3f",
"text" : "Test 5"
},
{
"id" : "6b5c02996a830f807e4d8e35",
"text" : "Test 6",
"indent" : 0
}
]
}
I need to update some Lines.
For example I have array with ids of the line which must be updated.
let lineIds = [
"5efa6ad451048a0a1807916c",
"829f874878dfd0b47e9441c2",
"6b5c02996a830f807e4d8e35"
];
So I try to update attributes "attr" for the "lines" and I do following:
'articles.updateLines': function (articleId, lineIds, attr, value) {
return Articles.update({
'_id': articleId,
'lines.id': { $in: lineIds }
},
{
$set: {
['lines.$.' + attr]: value
}
},
{ multi: true }
);
}
The problem is that just the first line (with id="5efa6ad451048a0a1807916c") is updated.
Any ideas? Thanks! :)
You can use $[]. This will works only MongoDB version 3.6 and above.
Refer link :
https://docs.mongodb.com/manual/reference/operator/update/positional-all/
You can also see this stackoverflow question reference:
How to add new key value or change the key's value inside a nested array in MongoDB?
You can convert below query in your function
db.col.update(
{ '_id':"rNiwdR8tFwbTdr2oX", },
{ $set: { "lines.$[elem].text" : "hello" } },
{ arrayFilters: [ { "elem.id": { $in: lineIds } } ],
multi: true
})
'lines.id': { $in: lineIds }
This won't work, because lines is an array.
What I understand from your question, you can prepare a new array with proper processing and replace the lines array with the new one. Here is an idea how to do this:
'articles.updateLines': function (articleId, lineIds, attr, value) {
let lines = Articles.findOne(articleId).lines;
// prepare a new array with right elements
let newArray = [];
for(let i=0; i<lines.length; i++){
if(lineIds.includes(lines[i].id)){
newArray.push(value)
}
else newArray.push(lines[i])
}
return Articles.update({
'_id': articleId,
},
{
$set: {
lines: newArray
}
}
);
}

How to use aggregation function mongo db-query

I am new in MongoDB and I would like to use the aggregation function where I want to check type == topic and get the following output
Expected output
[
{
conceptName : 59d98cfd1c5edc24e4024d00
totalCount : 2
},
{
conceptName : 59d98cfd1c5edc24e4024d03
totalCount : 1
}
]
Sample input db.GroupContents
{
"_id" : "5a0948bb1c5edc7a5000521a",
"type" : "topic",
"groupID" : "5a0948bb1c5edc7a5000521a",
"pedagogyID" : "59d98cfa1c5edc24e40249a3",
}
Sample input db.PedagogyNodes
{
"_id" : "59d98cfa1c5edc24e40249a3",
"latestVersion" : "59d98cfa1c5edc24e402497f_1",
"createdAt" : "2017-10-08 04:27:06",
"updatedAt" : "2017-10-08 04:27:06"
}
Sample input db.PedagogyVersions
{
"_id" : "59d98cfa1c5edc24e402497f_1",
"type" : "topic",
"contentNodes" : {
"LearningNodes" : [
"59d98cfd1c5edc24e4024d00",
"59d98cfd1c5edc24e4024d03",
"59d98cfd1c5edc24e4024d00",
]
},
"createdAt" : "2017-10-08 04:27:06",
"updatedAt" : "2017-10-08 04:27:06"
}
What I have tried so far
var groupID = "5a0948bb1c5edc7a5000521a"; // Step 1
var records;
var pnDoc;
var pvDoc;
db.GroupContents.find({groupID : groupID}).forEach(function (doc){ // Step 2
var pedagogyID = doc.pedagogyID;
var records = db.getSiblingDB('PedagogyService');
records.PedagogyNodes.find({_id : pedagogyID}).forEach(function (pnDoc) { // Step 3
var latestVersion = pnDoc.latestVersion;
// addded aggregate function here
records.PedagogyVersions.aggregate([
{
$match:{_id:latestVersion} // Step 4
},
{
$unwind:"$contentNodes.LearningNodes"
},
{
$group:
{
_id:"$contentNodes.LearningNodes",
count:{$sum:1}
}
}
])
})
});
I am unable to write db query based on my expected answer, please help.
Understand my requirement
Step : 1 => I am passing `groupID = 5a0948bb1c5edc7a5000521a`
Step : 2 => we have to check from GroupContents where groupID = groupID then we have to take `pedagogyID`
Step : 3 => we have to check from PedagogyNodes where _id = pedagogyID then we have to take `latestVersion`
Step : 4 => we have to check from PedagogyVersions where _id = latestVersion then we have to take `contentNodes->LearningNodes`
Step : 5 => Finally we have to do the aggregation then we have display the result
Try to unwind the LearningNodes array and then count them by grouping them together
db.PedagogyNodes.aggregate([
{
$unwind:"$contentNodes.LearningNodes"
},
{
$group:
{
_id:"$contentNodes.LearningNodes",
count:{$sum:1}
}
}
])
In case you need to do any matches you can use the $match stage
db.PedagogyNodes.aggregate([
{
$match:{type:"topic"}
},
{
$unwind:"$contentNodes.LearningNodes"
},
{
$group:
{
_id:"$contentNodes.LearningNodes",
count:{$sum:1}
}
}
])
Answering the edited question =>
You were not able to view the output on the console since mongoshell does not print script output on the screen. To do this, do the following:
var result = records.PedagogyVersions.aggregate([......]);
result.forEach(function(resultDoc){
print(tojson(resultDoc))
})
To see the result of your aggregation you have to pass the callback to be executed as parameter.
records.PedagogyVersions.aggregate([
{
$match:{_id:latestVersion} // Step 4
},
{
$unwind:"$contentNodes.LearningNodes"
},
{
$group:
{
_id:"$contentNodes.LearningNodes",
count:{$sum:1}
}
}
], function(err, results) {
console.log(results);
});

How to add key-value pair to object in MongoDB

If I have a document with the following basic structure:
{
...
Monday: { a:1, b:2 },
Tuesday: { c:3, d:4 }
...
}
Am I able to 'push' an additional key:value pair to Monday's value? Result would be:
{
Monday: { a:1, b:2, z:8 },
Tuesday: { c:3, d:4 }
...
}
The $push operator seems to only work for arrays.
Just do something like that
db.foo.update({"_id" :ObjectId("...") },{$set : {"Monday.z":8}})
How to add a new key:value pair to all existing objects of a mongoDB documents
Old Key and Value Pairs
> db.students.find().pretty();
{ "_id" : ObjectId("601594f5a22527655335415c"), "name" : "Doddanna" }
{ "_id" : ObjectId("601594f5a22527655335415d"), "name" : "Chawan" }
Update New Key and Value Pairs Using updateMany() and $set
> db.students.updateMany({},{$set:{newKey1:"newValue1", newKey2:"newValue2", newKeyN:"newValueN"}});
{ "acknowledged" : true, "matchedCount" : 2, "modifiedCount" : 2 }
Have a look on Updated pretty result
> db.students.find().pretty();
{
"_id" : ObjectId("601594f5a22527655335415c"),
"name" : "Doddanna",
"newKey1" : "newValue1",
"newKey2" : "newValue2",
"newKeyN" : "newValueN"
}
{
"_id" : ObjectId("601594f5a22527655335415d"),
"name" : "Chawan",
"newKey1" : "newValue1",
"newKey2" : "newValue2",
"newKeyN" : "newValueN"
}
I know this might be irrelevant to the question but as a matter of fact, I opened this page because I was looking for an exact query with mongoose. here is my answer with mongoose.
If we have an abstract model (mongoose schema) named week in our javascript application then the code will be:
// javascript with mongoose
...
const key = "z";
const KeyValue = 8;
await week.updateOne({
_id, // mongoDb document id
},
{
$set:{
[`Monday.${key}`]: KeyValue,
},
},
{
upsert: true // options
},
);
...
var json = {
Monday: { a:1, b:2 },
Tuesday: { c:3, d:4 } }
json['Monday']['z'] = 8;
console.log(json);

Mongodb: Finding and updating object property from array

I have a collection with multiple documents which follow this structure:
{
"_id" : {
"oid" : XXX
},
"name" : "Name",
"videos" [
{
"id" : 1,
"thumbnail" : "thumbnail.jpg",
"name" : "Name here"
},
{
"id" : 2,
"thumbnail" : "thumbnail.jpg",
"name" : "Name here"
},
{
"id" : 3,
"thumbnail" : "thumbnail.jpg",
"name" : "Name here"
}
]
}
I want to find and update the a thumbnail of a video, of which I only know the id, but not which document it is in.
This is what I've tried so far, but it's not working properly. All the examples I found relied on knowing the document id, and the array position of the object to update. I also found that doing a query like this found the document okay, but set the whole document as the new thumbnail!
db.collection(COLLECTION-NAME, function(err, collection){
collection.update(
{ 'videos.id' : 2 },
{ $set: { thumbnail: "newThumbnail.jpg" } },
function(err, result){
if (!err){
console.log('saved', result)
} else {
console.log('error', err);
}
}
);
});
Use the $ positional operator to update the value of the thumbnail field within the embedded document having the id of 2:
db.collection.update(
{ "videos.id": 2 },
{ "$set": { "videos.$.thumbnail" : "newThumbnail.jpg" } }
)