Reputation: 11717
I am struggling from last 2 hours to update my nested collection. Can someone please try to guide me in right direction?
var ChoiceSchema = new Schema ({
body: {type: String, required: true},
correct: {type: Boolean, required: true},
timesDisplayed: {type: Number, default: 0},
timesSelected: {type: Number, default: 0},
images: {}
});
var QuestionSchema = new Schema({
contentId: {type: String, required: true},
questionBody: {type: String, required: true},
timesAnswered: {type: Number, default: 0},
timesCorrect: {type: Number, default: 0},
timesIncorrect: {type: Number, default: 0},
timesSkipped: {type: Number, default: 0},
explanation: {
contentId: {type: String, required: true},
body: {type: String, required: true},
images: {}
},
images: {},
choices: [ChoiceSchema]
});
var ExamSchema = new Schema ({
subject: {type: String, required: true},
dateCreated: { type: Date, default: Date.now },
examNumber: Number,
section1: {
part1: {
timeInMinutes: Number,
instructions: {type: String, required: true},
questions: [QuestionSchema]
},
part2: {}
},
section2: {}
});
I am trying to update the timesAnswered
property in QuestionsSchema
.
Exam.findById(req.params.id, function (err, exam) {
var ids=JSON.parse(req.body.ids);
if(err) { return handleError(res, err); }
if(!exam) { return res.send(404); }
if(ids.length) {
for(var i=0;i<ids.length;++i){
Exam.update({'section1.part1.questions.$._id':ids[i]},
{ $set: {
'section1.part1.questions.$.timesAnswered': 1 // <== and here
}}, function (err, numAffected) {
if(err) throw err;
}
);
}
}
return res.json(exam);
});
where ids is an array containing question ids
[ '54db8ee6529b197018822eb4',
'54db8ee6529b197018822ea7',
'54db8ee6529b197018822ea0' ]
I references this question but I don't know why its not working out for me. Mongoose nested document update failing?
Upvotes: 1
Views: 741
Reputation: 7862
Your code has two problems.
First, as @yazarubin pointed out, your update condition has an unnecessary $
, so just remove it:
Exam.update({'section1.part1.questions._id':id},
Second, you're running an update task (asynchronous task), inside a standard for
(synchronous), so it won't wait the update tasks to be completed. In this case you can use the async.each
function:
var async = require('async'); // be sure to install and require the async module
async.each(ids, function (id, callback) {
Exam.update({'section1.part1.questions._id':id},
{ $set: {
'section1.part1.questions.$.timesAnswered': 1 // <== and here
}}, function (err, numAffected) {
if(err) throw err;
callback();
}
);
}, function (error) {
if (error) res.send(500);
res.send(exam); // send the response just when all tasks has finished
});
, or you could use some kind of promises library to accomplish this same task.
Upvotes: 1
Reputation: 11677
This query should accomplish what you're trying to do (you didn't need the $ in the find clause):
Exam.update({'section1.part1.questions._id':ids[i]}, {
$set: {
'section1.part1.questions.$.timesAnswered':1
}}, function(err, numAffected){
})
Upvotes: 1
Reputation: 2298
Can you try:
Exam.findById(req.params.id, function (err, exam) {
var ids=JSON.parse(req.body.ids);
if(err) { return handleError(res, err); }
if(!exam) { return res.send(404); }
if(ids.length) {
for(var i=0;i<ids.length;++i){
var question=exam.section1.part1.questions.id(ids[i]);
question.timesAnswered=1;
}
exam.save(function(err) {
if(err) throw err;
});
}
return res.json(exam);
});
Upvotes: 0