Я создаю систему управления студентами. Я использую nodejs, expressjs, ejs и mongoose. Вот код модели Student:
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
В массиве batchInfo
должна быть информация о другой партии.
У меня есть такой студенческий документ:
{
"_id": "6683591adb3c21133fd99e12",
"name": "John",
"batchInfo": [
{
"batch_id": "66834a6d4b221111587def7b",
"studentID": "252105",
"payments": [2000, 1000]
"active": false
},
{
"batch_id": "668353316c57406f16e14449",
"studentID": "252301",
"payments": [1000]
"active": true,
}
]
}
Я фильтрую этот документ по массиву batchInfo
, имеющему "active": true
. Я получил этот документ:
{
"_id": "6683591adb3c21133fd99e12",
"name": "John",
"batchInfo": [
{
"batch_id": "668353316c57406f16e14449",
"studentID": "252301",
"active": true,
"payments": [1000]
}
]
}
Чтобы получить этот документ, я применил этот экспресс-код:
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)
}
})
Теперь я хочу вставить элемент в массив payments
. Итак, я попробовал этот код:
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)
}
})
Попробовав этот код, я получил такую ошибку:
{
"ok": 0,
"code": 40324,
"codeName": "Location40324"
}
Как решить эту проблему?
Существует несколько различных подходов к обновлению элементов массива путем добавления нового значения в массив.
Если вы знаете, что нашли только один элемент массива, вы можете просто использовать позиционный оператор $push
для обновления первого соответствующего элемента массива в методе $
следующим образом:
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});
Рабочий пример смотрите ЗДЕСЬ.
Альтернативно вы можете обновить несколько элементов массива, используя опцию findOneAndUpdate()
, например:
const student = await Student.findOneAndUpdate({
_id: new mongoose.Types.ObjectId(req.params.id)
},
{
$push: {
"batchInfo.$[element].payments": 3000
}
},
{
arrayFilters: [
{
"element.active": true
}
]
});
Рабочий пример смотрите ЗДЕСЬ.
Чтобы сопоставить один элемент массива на основе нескольких условий, вы можете использовать arrayFilters
следующим образом:
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});
Рабочий пример смотрите ЗДЕСЬ.
если я хочу использовать первый метод, добавление условия типа {"batchInfo.batch_id": ObjectId("66834a6d4b221111587def7b"), "batchInfo.active": true}
не поможет. Поскольку элемент batchInfo
, имеющий batch_id
, имеет active: false
, он все равно обновляется. Как решить эту проблему?
Чтобы сопоставить более одного условия в элементе массива, вы можете использовать $elemMatch
, чтобы гарантировать выполнение обоих условий. ВОТ пример.
@jQuenny Большое спасибо. Вы такой полезный человек.
Возможно, вы хотите сделать обновление, подобное этому примеру? Стандартной практикой является использование
arrayFilters
для выбора определенных элементов массива для обновления. Если вам нужно, я могу привести пример мангуста.