codingnighter2000
codingnighter2000

Reputation: 529

can not update a document via 'findById' and save() with promise

I'm building a cinema app. using Node.js and mongodb via mongoose.

When a user makes an order, i need to update the 'Show' model and add the latest seats that was ordered to the 'showTakenSeats' object. note that i need to add the seats to the 'showTakenSeats' and not replace all the object.

The problem is that when i try to update and save the show - everything seems ok. but in the end it does not save it successfully in the DB.

Here is the code:

const express = require('express');
const router = express.Router();

const Order = require('../models/order.js');
const Show = require('../models/show.js');

router.post("/", (req, res) => {
    if (!req.body) {
        return res.sendStatus(400);
    }
    let order = new Order(req.body);
    order.save().then(newOrder => {
        console.log("Order saved successfully");
        Show.findById(req.body._ShowId).then(updateShow => {
            console.log('we got the show');
            console.log(newOrder.ticketsPositions);
            console.log("updateShow before any changes");
            console.log(updateShow);
            newOrder.ticketsPositions.forEach(element => {
                updateShow.showTakenSeats[element[0] + "-" + element[1]] = newOrder._id;
            });
            console.log("updateShow after adding new seats");
            console.log(updateShow);
            updateShow.save().then((updateShowSaved) => {

                console.log('updated the last order seats at the Show seats map')
                console.log(updateShowSaved);
                //res.json(updateShowSaved);
            }, err => {
                console.log(err);
                //res.send(err);
            });
            res.json(updateShow);
        }, err => {
            console.log(err);
        });
        res.json(newOrder);
    }, err => {
        res.send(err);
    });
});


module.exports = router;

console.log:

Order saved successfully
we got the show
[[6,0],[6,1],[6,2]]
updateShow before any changes
{ _id: 5b5203bfcbb3a311c0911b8f,
  _MovieId: 5b45faa4a53b0c05f8959262,
  _TheaterId: 5b3dfeb217bc8c23f0e7ec3f,
  date: '2018-07-22',
  time: '22:00',
  dateTime: 2018-07-22T19:00:00.000Z,
  showTakenSeats:
   { '0-14': 5b52186a7feaac1028ec592f,
     '5-0': 5b530e6dc7d3ed280427145a,
     '5-1': 5b530e6dc7d3ed280427145a,
     '5-2': 5b530e6dc7d3ed280427145a },
  __v: 0,
  movieInfo: null,
  theaterInfo: null,
  id: '5b5203bfcbb3a311c0911b8f' }
updateShow after adding new seats
{ _id: 5b5203bfcbb3a311c0911b8f,
  _MovieId: 5b45faa4a53b0c05f8959262,
  _TheaterId: 5b3dfeb217bc8c23f0e7ec3f,
  date: '2018-07-22',
  time: '22:00',
  dateTime: 2018-07-22T19:00:00.000Z,
  showTakenSeats:
   { '0-14': 5b52186a7feaac1028ec592f,
     '5-0': 5b530e6dc7d3ed280427145a,
     '5-1': 5b530e6dc7d3ed280427145a,
     '5-2': 5b530e6dc7d3ed280427145a,
     '6-0': 5b53735ef7ce3d2cd4bbfee7,
     '6-1': 5b53735ef7ce3d2cd4bbfee7,
     '6-2': 5b53735ef7ce3d2cd4bbfee7 },
  __v: 0,
  movieInfo: null,
  theaterInfo: null,
  id: '5b5203bfcbb3a311c0911b8f' }
(node:11476) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Can't set headers after they are sent.
(node:11476) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
updated the last order seats at the Show seats map
{ _id: 5b5203bfcbb3a311c0911b8f,
  _MovieId: 5b45faa4a53b0c05f8959262,
  _TheaterId: 5b3dfeb217bc8c23f0e7ec3f,
  date: '2018-07-22',
  time: '22:00',
  dateTime: 2018-07-22T19:00:00.000Z,
  showTakenSeats:
   { '0-14': 5b52186a7feaac1028ec592f,
     '5-0': 5b530e6dc7d3ed280427145a,
     '5-1': 5b530e6dc7d3ed280427145a,
     '5-2': 5b530e6dc7d3ed280427145a,
     '6-0': 5b53735ef7ce3d2cd4bbfee7,
     '6-1': 5b53735ef7ce3d2cd4bbfee7,
     '6-2': 5b53735ef7ce3d2cd4bbfee7 },
  __v: 0,
  movieInfo: null,
  theaterInfo: null,
  id: '5b5203bfcbb3a311c0911b8f' }

When i check the document in the db after i run the code, The new seats are not being saved in the db.

db after "saving": enter image description here

Anybody knows what the problem?

Thanks

Upvotes: 1

Views: 794

Answers (2)

chridam
chridam

Reputation: 103355

Would consider breaking down the operation into manageable chunks. In this case you'd want to update the showTakenSeats field with the ticket positions data from the new order.

Firstly, using async await with your express route you need to save the order and get the created order document. Create a document with the new taken seats and then update the show document using findByIdAndUpdate method.

The following example describes the above:

const express = require('express');
const router = express.Router();

const Order = require('../models/order.js');
const Show = require('../models/show.js');

router.post('/', async (req, res, next) => {
    try {
        /* create a new Order */
        const order = new Order(req.body);
        const newOrder = await order.save();

        /* create a document to use in the update with the following data structure:
            {
                'showTakenSeats.6-0': 5b53735ef7ce3d2cd4bbfee7,
                'showTakenSeats.6-1': 5b53735ef7ce3d2cd4bbfee7,
                'showTakenSeats.6-2': 5b53735ef7ce3d2cd4bbfee7 
            }

            Use the native reduce method on the array to create this 
        */
        const updatedSeats = newOrder.ticketPositions.reduce((acc, position) => {
            acc[`showTakenSeats.${position.join('-')}`] = newOrder._id;
            return acc;
        }, {});

        /* update the show document's embedded showTakenSeats 
           with the new properties from above 
        */
        const updateShow = await Show.findByIdAndUpdate(req.body._ShowId,
            { '$set': updatedSeats },
            { 'new': true }
        );

        res.json(updateShow);

    } catch (e) {
        /* this will eventually be handled by your error handling middleware */
        next(e);
    }
});

Upvotes: 2

Matthias Herrmann
Matthias Herrmann

Reputation: 2790

It looks like you think that the function executes in a sequential order, but when you call .then on a promise like updateShow.save().then the execution of the function doesn't wait and continues! This causes the error message below where you try to set the headers after they already have been sent:

> (node:11476) UnhandledPromiseRejectionWarning: Unhandled promise
> rejection (rejection id: 1): Error: Can't set headers after they are
> sent. (node:11476) [DEP0018] DeprecationWarning: Unhandled promise
> rejections are deprecated. In the future, promise rejections that are
> not handled will terminate the Node.js process with a non-zero exit
> code.

You can make your function async and await function calls to finish work before continuing.

Example:

router.post("/", async (req, res) => {
    let order = new Order(req.body);
    await order.save(); // if this fails an exception is thrown
    console.log('order is saved') // statement is executed after order is saved
}

More on async and await: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

This is possibly not the only bug in your code and therefore this will not answer your question but this text is too long for a comment.

Upvotes: 1

Related Questions