I have an application with 2 models: Student, Book (see below). Whenever a book is added, I would like it to be appended to the books array of every student model. The updateMany function of Mongoose should by my understanding do that, although it never updates the array.
POST function to create a new book object based on given ISBN and append it to every books array of the student model:
if (req.isAuthenticated()) {
if (req.body.ISBN && req.body.dueDate) {
https.get('https://www.googleapis.com/books/v1/volumes?q=isbn:' req.body.ISBN, function (hres) {
var body = "";
hres.on('data', function (chunk) {
body = chunk;
});
hres.on('end', function () {
var resp = JSON.parse(body);
const newBook = new book({
title: resp.items[0].volumeInfo.title,
length: resp.items[0].volumeInfo.pageCount,
author: resp.items[0].authors,
ISBN: req.body.ISBN,
finished: false,
dueDate: req.body.dueDate
});
newBook.save();
student.updateMany({},{$push: {books:newBook}})
});
});
res.redirect('/admin')
}
else {
res.redirect('/newBook');
}
}
else {
res.redirect('/Login')
}
Student model:
const mongoose = require('mongoose');
const bookSchema = require('./book').schema;
const studentSchema = new mongoose.Schema({
name:{
type: String,
required: true
},
books:{
type: [bookSchema]
}
});
module.exports = mongoose.model("student", studentSchema);
Book model:
const mongoose = require('mongoose');
const bookSchema = new mongoose.Schema({
title:{
type: String
},
length:{
type:Number
},
author:{
type:String
},
ISBN:{
type:String
},
finished:{
type:Boolean
},
dueDate:{
type:String
}
});
module.exports = mongoose.model("book", bookSchema);
CodePudding user response:
student.updateMany({},{$push: {books:newBook}})
is the correct syntax but need the await
word to properly works if you are using promises.
If you want to use callBacks you need to add the .exec()
at the end of the operation so you are telling mongoose to run that command.
student.updateMany({},{$push: {books:newBook}}).exec((err,studentsUpdated)=>{
// respond here
})
Using async await (wich i recommend)
try {
const newBook = new book({
title: resp.items[0].volumeInfo.title,
length: resp.items[0].volumeInfo.pageCount,
author: resp.items[0].authors,
ISBN: req.body.ISBN,
finished: false,
dueDate: req.body.dueDate
});
const newBookAdded = await newBook.save();
const studentsUpdated = await student.updateMany({},{$push: {books:newBookAdded}})
// Anwer here, if you console.log(studentsUpdated) you can check if the updateMany operation was correct
} catch (error) {
// manage the error here
}
remmer you need to run to add the async word on the higher function to be able to use await, on you code shoud be here
async function (hres)
CodePudding user response:
As mentioned by Fernando, your existing solution requires await
keyword for proper execution. And to use await
, you have to convert the existing function to async
function. On top of that, it is also encouraged to use async-await
instead of callback chains for better readability.
Remark
Also note that, once you call new book()
, mongoose will create new unique _id
. It means, if you call this function multiple times with the same ISBN, your books
collection and books
array of students
collection will end up containing the same books with same ISBN, but different _id
. So, one possible strategy is to have 2 functions, one which handles new book creation and the other one which handles book update, e.g. updating dueDate
of existing book. Then, we may want to check for existing book in the addNewBook
function prior to adding new book. Thus, function for adding new book could be written as follows
// another import up here...
// we will use axios to replace https
const axios = require("axios");
exports.addNewBook = async (req, res, next) => {
try {
if (!req.isAuthenticated()) {
res.redirect("/newBook");
return;
}
const ISBN = req.body.ISBN;
const dueDate = req.body.dueDate;
if (!ISBN || !dueDate) {
res.redirect("/Login");
return;
}
const existingBook = await book.find({ ISBN }).exec();
// book exists. skipping save() and updateMany()
if (existingBook.length !== 0) {
res.status(400).json({
message: `Failed to add new book. Book with ISBN ${ISBN} exists in database`,
});
return;
}
const bookRes = await axios.get(
"https://www.googleapis.com/books/v1/volumes?q=isbn:" ISBN
);
const bookData = bookRes.data;
const newBook = new book({
title: bookData.items[0].volumeInfo.title,
length: bookData.items[0].volumeInfo.pageCount,
author: bookData.items[0].authors,
ISBN: ISBN,
finished: false,
dueDate: dueDate,
});
const savedBook = await newBook.save();
const updateResult = await student.updateMany({}, { $push: { books: savedBook } });
res.redirect("/admin");
} catch (err) {
// handle error
}
}