4

I am working on a table planner application where guests can be assigned to tables. The table model has the following Schema:

const mongoose = require('mongoose');

mongoose.Promise = global.Promise;

const tableSchema = new mongoose.Schema({
  name: {
    type: String,
    required: 'Please provide the name of the table',
    trim: true,
  },
  capacity: {
    type: Number,
    required: 'Please provide the capacity of the table',
  },
  guests: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Guest',
  }],
});

module.exports = mongoose.model('Table', tableSchema);

Guests can be dragged and dropped in the App (using React DND) to "Table" React components. Upon being dropped on a table, an Axios POST request is made to a Node.js method to update the Database and add the guest's Object ID to an array within the Table model:

exports.updateTableGuests = async (req, res) => {
  console.log(req.body.guestId);
  await Table.findOneAndUpdate(
    { name: req.body.tablename },
    { $push: { guests: req.body.guestId } },
    { safe: true, upsert: true },
    (err) => {
      if (err) {
        console.log(err);
      } else {
      // do stuff
      }
    },
  );
  res.send('back');
};

This is working as expected, except that with each dropped guest, the Table model's guests array is updated with the same guest Object ID twice? Does anyone know why this would be?

I have tried logging the req.body.guestID to ensure that it is a single value and also to check that this function is not being called twice. But neither of those tests brought unexpected results. I therefore suspect something is wrong with my findOneAndUpdate query?

1
  • Do you have an example of the Table JSON object once the problem has occurred? I want to see the JSON representative of "ObjectIDs to array twice" to hopefully get an idea of how mongoose is behaving. Commented May 15, 2018 at 16:53

2 Answers 2

6

Don't use $push operator here, you need to use $addToSet operator instead...

The $push operator can update the array with same value many times where as The $addToSet operator adds a value to an array unless the value is already present.

exports.updateTableGuests = async (req, res) => {
  console.log(req.body.guestId);
  await Table.findOneAndUpdate(
    { name: req.body.tablename },
    { $addToSet : { guests: req.body.guestId } },
    { safe: true, upsert: true },
    (err) => {
      if (err) {
        console.log(err);
      } else {
      // do stuff
      }
    },
  );
  res.send('back');
};
Sign up to request clarification or add additional context in comments.

1 Comment

This did the trick. Thanks so much. It makes sense coz you can only do an insert into a new record, whereas this is an update to an existing record.
2

I am not sure if addToSet is the best solution because the query being executed twice.

If you used a callback and a promise simultaneously, it would make the query executes twice.

So choosing one of them would make it works fine.

Like below:

async updateField({ fieldName, shop_id, item }) {
    return Shop.findByIdAndUpdate(
      shop_id,
      { $push: { menuItems: item } },
      { upsert: true, new: true }
    );
  }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.