I am creating a student management system. I am using nodejs, expressjs, ejs and mongoose. Here is the code for Student model:
const mongoose = require('mongoose')
const studentSchema = mongoose.Schema({
name: {type: String, required: true},
batchInfo: [{
batch_id: {type: mongoose.Schema.Types.ObjectId, ref: "Batch", required: true},
studentID: {type: String, required: true, unique: true},
payments: [{type: Number, required: true}],
active: {type: Boolean, required: true}
}],
})
const Student = mongoose.model('Student', studentSchema, 'students')
module.exports = Student
In the batchInfo
array, there should be different batch's info.
I have a student document like this:
{
"_id": "6683591adb3c21133fd99e12",
"name": "John",
"batchInfo": [
{
"batch_id": "66834a6d4b221111587def7b",
"studentID": "252105",
"payments": [2000, 1000]
"active": false
},
{
"batch_id": "668353316c57406f16e14449",
"studentID": "252301",
"payments": [1000]
"active": true,
}
]
}
I filters this document based on batchInfo
array having "active": true
. I got this document:
{
"_id": "6683591adb3c21133fd99e12",
"name": "John",
"batchInfo": [
{
"batch_id": "668353316c57406f16e14449",
"studentID": "252301",
"active": true,
"payments": [1000]
}
]
}
To achieve this document, I applied this express code:
const mongoose = require('mongoose')
const Student = require("./models/Student")
app.post('/update/:id', async function (req, res) {
try {
const student = await Student.aggregate([
{"$match": {_id: new mongoose.Types.ObjectId(req.params.id)}},
{"$project": {batchInfo: {"$filter": {input: "$batchInfo", as: "item", cond: {"$eq": ["$$item.active", true]}}}, name: 1}}
])
res.send(student[0])
} catch (error) {
res.send(error)
}
})
Now I want to insert an element to the payments
array. So I tried this code:
const mongoose = require('mongoose')
const Student = require("./models/Student")
app.post('/update/:id', async function (req, res) {
try {
const student = await Student.aggregate([
{"$match": {_id: new mongoose.Types.ObjectId(req.params.id)}},
{"$project": {batchInfo: {"$filter": {input: "$batchInfo", as: "item", cond: {"$eq": ["$$item.active", true]}}}, name: 1}},
{"$push": {"batchInfo.payments": 3000}}
])
res.send(student[0])
} catch (error) {
res.send(error)
}
})
After trying this code I got an error like this:
{
"ok": 0,
"code": 40324,
"codeName": "Location40324"
}
How to solve this problem?
There are several different approaches to updating array elements by $push
ing a new value into an array.
If you know you are only finding one array element you can just use the $
positional operator to update the first matching array element in a findOneAndUpdate()
method like so:
const student = await Student.findOneAndUpdate({
_id: new mongoose.Types.ObjectId(req.params.id),
"batchInfo.active": true //< must match array element too
},
{
$push: {
"batchInfo.$.payments": 3000
}
}, {new: true});
See HERE for a working example.
Alternatively you can update several array elements by using arrayFilters
option like so:
const student = await Student.findOneAndUpdate({
_id: new mongoose.Types.ObjectId(req.params.id)
},
{
$push: {
"batchInfo.$[element].payments": 3000
}
},
{
arrayFilters: [
{
"element.active": true
}
]
});
See HERE for a working example.
To match one element from the array based on multiple conditions you can use $elemMatch
like so:
const student = await Student.findOneAndUpdate({
_id: new mongoose.Types.ObjectId(req.params.id),
batchInfo: {
$elemMatch: {
active: true,
batch_id: new mongoose.Types.ObjectId("668353316c57406f16e14420")
}
{
},
{
$push: {
"batchInfo.$.payments": 3000
}
}, {new: true});
See HERE for a working example.