I have this Schema structure
const QuizQuestionSchema = new Schema({
course: { type: String, required: true, trim: true },
point: { type: Number, required: true, trim: true, min: 1, max: 20 },
question: { type: String, required: true, trim: true },
codeBoard: { type: String, default: null, nullable: true },
answers: { type: [String], required: true, trim: true },
correctAnswer: { type: String, required: true, trim: true }
});
from client I get array id of documents and selected answer [{id, answer}...]
.
I need to find out how many points the user has collected, comparing correctAnswer with answer if match add score. how can this be done with an aggregate ?
example
client
[
{ id: "61bc09994da5e9ffe47fccb9", answer: "1 2 4 3 5" },
{ id: "61bc0af14da5e9ffe47fccbb", answer: "1 4 3 2" },
...
];
server documents
{
_id: new ObjectId("61bc09994da5e9ffe47fccb9"),
course: 'JavaScript',
point: 10,
question: 'What will we see in the console ?',
codeBoard: '...',
answers: [ '1 2 4 3 5', '1 5 4 3 2', '2 1 4 3 5', '1 5 3 4 2' ],
correctAnswer: '2 1 4 3 5',
__v: 0
},
{
_id: new ObjectId("61bc0af14da5e9ffe47fccbb"),
course: 'JavaScript',
point: 10,
question: 'What will we see in the console ?',
codeBoard: '...',
answers: [ '1 4 3 2', '1 2 4 3', '1 2 3 4', '1 4 2 3' ],
correctAnswer: '1 4 3 2',
__v: 0
}
CodePudding user response:
If the client side is sending each question id in String with the chosen answer you should map that array to:
{
id: new ObjectId("61bc09994da5e9ffe47fccb9"),
correctAnswer: "2 1 4 3 5"
}
Remenber to define ObjectId with:
const ObjectId = require('mongoose').Types.ObjectId;
Then add the full array inside an $or
operator and finally group every match adding the points.
db.collection.aggregate({
"$match": {
$or: [
{
id: new ObjectId("61bc09994da5e9ffe47fccb9"),
correctAnswer: "2 1 4 3 5"
},
{
id: new ObjectId("61bc0af14da5e9ffe47fccbb"),
correctAnswer: "1 4 3 2"
},
. . .
]
}
},
{
"$group": {
"_id": null, // This means that every document is grouped, because there is no condition to group by
"points": {
$sum: "$point"
}
}
})
CodePudding user response:
You can achieve this with a pretty straight forward pipeline, using $lookup
, like so:
db.users.aggregate([
{
$match: {
user_id: 1
}
},
{
"$lookup": {
"from": "quiz",
let: {
quizId: "$id",
userAnswer: "$answer"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$$quizId",
"$_id"
]
},
{
$eq: [
"$$userAnswer",
"$correctAnswer"
]
}
]
}
}
}
],
"as": "matched"
}
},
{
$group: {
_id: null,
total: {
$sum: 1
},
correct: {
$sum: {
$size: "$matched"
}
}
}
}
])
You didn't give exact input and output required so this will require some minor changes.