When attempting to update a nested array field within a MongoDB document using Mongoose's findOneAndUpdate method, I'm encountering a perplexing issue where the update doesn't seem to reflect in the database.
Here's a concise summary of the problem:
I have a MongoDB schema called Screen that includes an array field named timeslots. Within each timeslot, there's a nested array called seats, representing available seats in a cinema. I'm trying to update the status field of a specific seat to indicate it has been booked. Despite successfully modifying the status field locally, the update doesn't persist in the database.
Below is a snippet of my code:
router.post('/bookTicket', async (req, res) => {
try {
console.log("Book ticket API is triggered");
// Find the screen details
let getDetails = await Screen.findOne({ title: "DemonSlaye" }).lean()
if (!getDetails) {
return res.status(404).send("Screen not found");
}
console.log("Screen details before update:", getDetails);
// Find the seat status and update it
const foundSeat = getDetails.timeslots[0].seats
.filter(seatTypeIterate => seatTypeIterate.type === 'platinum')
.map(silverSeatRows => silverSeatRows.rows)
.flat()
.find(getrow => getrow.rowname === 'H')
.cols[0]
.seats
.find(getSeatId => getSeatId.seat_id === '2');
if (!foundSeat) {
console.log("Seat not found");
return res.status(404).send("Seat not found");
}
console.log("Seat status before update:", foundSeat);
foundSeat.status = "booked";
foundSeat.bookedBy = "userID";
console.log("Updated seat status:", foundSeat);
const hydratedDetails = Screen.hydrate(getDetails);
console.log("Updating document with ID:", getDetails._id);
// Replace the entire JSON document in the database with the updated one
const updatedDocument=await hydratedDetails.save()
console.log("Document updated successfully:", updatedDocument);
res.send(updatedDocument)
// res.send(hydratedDetails); // Sending the updated document as response
} catch (error) {
console.error("Error occurred:", error);
res.status(500).send("An error occurred while booking the ticket.");
}
});
Schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const screenSchema = new mongoose.Schema({
status: { type: String},
seat_id: { type: String},
bookedBy: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
});
const rowSchema = new mongoose.Schema({
rowname: { type: String, required: true },
cols: [screenSchema] // Array of seats
});
const timeslotSchema = new mongoose.Schema({
seats: [{
type: { type: String, required: true },
rows: [rowSchema], // Array of rows
price: { type: Number, required: true }
}]
});
const movieSchema = new mongoose.Schema({
title: { type: String, required: true },
theatreId: { type: String, required: true },
location: { type: String, required: true },
language: { type: String, required: true },
time: { type: String, required: true },
screenType: { type: String, required: true },
screenName: { type: String, required: true },
timeslots: [timeslotSchema] // Array of timeslots
},
{ strict: false }
);
const Screen = mongoose.model('Screen', screenSchema);
module.exports = Screen;
Output: I attached the DB image , which has no updation of seat-staus: booked (seat_id:2)
I tried out using $set operator it doesnot work
My issue lies in attempting to update the nested array fields within a MongoDB document by modifying the document retrieved using findOne and then saving it back to the database using Mongoose's save method. While this approach may seem intuitive, it can lead to unexpected behavior because Mongoose's save method may not update nested arrays as expected. Instead, you should use Mongoose's findOneAndUpdate method, which is designed to update documents directly in the database and can handle nested arrays more effectively.
The mongoose
Model.hydrate()does not work the way you think.When you do:
and then
It is the same Document. Nothing has changed from when you hydrated it so nothing is saved.
You need to make the changes after you do the hydration. So something like:
So you might as well not use
lean()in the first place.However, I don't think it is going to work for you anyway because
foundSeatis the result offilter()andmap()which return new arrays. It is a new variable and is not part of thegetDetailsobject anymore. When you set new values onfoundSeatthose changes aren't reflected ingetDetails.Your problems stem from not being able to select the correct
timeslots. You are using the arrays index such asgetDetails.timeslots[0].seatsto pick one from the array because you have not modelled your data well. Yourtimelsotsshould be selected by a resource identifier such_id. Then you would be able to perform afindOneAndUpdatequery.