skip to Main Content

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);

2

Answers


  1. 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) 
    
    Login or Signup to reply.
  2. 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
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search