I'm trying to create update functionality for nested documents in a MongoDB app, but coming from my update function I get "Cannot read properties of undefined (reading 'updateOne')" in the browser. That example is for updateOne, but it happens for every mongoose method I've tried.
The function:
function update(req, res) {
Employee.labs.updateOne({_id: req.params.id}, {
reactPh: req.body.reactPh,
pH: req.body.pH,
temperature: req.body.temperature,
dissOx: req.body.dissOx,
ammonia: req.body.ammonia,
date: req.body.date,
})
Employee.labs.save(function(err) {
res.redirect('/labs')
})
}
"Employee" is the name of the schema/model, and the "labs" schema is embedded into it.
I've been using mongoose methods to create, index, show, delete these same subdocs with no such issues.
Here is the form action with one of the fields:
<form action="/labs/<%= lab._id %>?_method=PUT" method="POST">
<label>Reactive phosphorous:
<input type ="number" step="0.01" name="reactPh" value="<%= lab.reactPh %>">
</label><br>
Any help is much appreciated!
Employee schema (one employee object to many lab objects):
var mongoose = require('mongoose');
var labSchema = new mongoose.Schema({
reactPh: Number,
pH: Number,
temperature: Number,
dissOx: Number,
ammonia: Number,
date: Date,
})
var employeeSchema = new mongoose.Schema({
name: String,
email: String,
labs: [labSchema],
googleId: String
})
module.exports = mongoose.model('Employee', employeeSchema);
UPDATE: Using Luiz's code and a couple small changes in referencing, it now works. Here it is:
function update (req, res) {
Employee.updateOne({ _id: req.body.employeeId, 'labs._id': req.params.id },
{ $set: {
'labs.$.reactPh': req.body.reactPh,
'labs.$.pH': req.body.pH,
'labs.$.temperature': req.body.temperature,
'labs.$.dissOx': req.body.dissOx,
'labs.$.ammonia': req.body.ammonia,
'labs.$.date': req.body.date,
}}, (err) => {
res.redirect('/labs');
})
}
The mongoose methods, like updateOne, have to be called from the model object itself, the object embedded selection is within the method, like this:
function update(req, res) {
Employee.updateOne({_id: req.params.id},
{ $set: {
'labs.reactPh': req.body.reactPh,
'labs.pH': req.body.pH,
'labs.temperature': req.body.temperature,
'labs.dissOx': req.body.dissOx,
'labs.ammonia': req.body.ammonia,
'labs.date': req.body.date,
}
}, (err) => {
res.redirect('/labs')
})
}
Also, updateOne will do execute the whole updating job, so you don't need to use the save
method.
EDIT
Before seeing the schema, I thought that labs was an object, not an array.
To do this update, you'll need to match the object inside the array, I will consider that you have the _id
(mongoose generates one automatically) of the labs object that you are trying to update. This is not the only way to match it, you can do it by another of the attributes in the object.
To indicate that you are updating an element of the array, you'll also need to add a .$.
in the $set
stage.
The answer is:
function update(req, res) {
Employee.updateOne({_id: req.params.id, 'labs._id': req.params.labsId },
{ $set: {
'labs.$.reactPh': req.body.reactPh,
'labs.$.pH': req.body.pH,
'labs.$.temperature': req.body.temperature,
'labs.$.dissOx': req.body.dissOx,
'labs.$.ammonia': req.body.ammonia,
'labs.$.date': req.body.date,
}
}, (err) => {
res.redirect('/labs')
})
}