Search code examples
node.jsdatabasemongodbmongoosemongoose-schema

Mongoose $push keeps adding two entries


Here are my user and product schemas:

const productSchema = new Schema({
  //... 
  addedBy: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "users"
  }
});

const userSchema = new Schema({   
  //...
  addedItems: [{
    type: mongoose.Schema.ObjectId,
    ref: "products"
  }]
});

mongoose.model("products", productSchema);
mongoose.model("users", userSchema);

In my Node back end route I do this query:

User.findOneAndUpdate(
  { _id: req.body.id },
  { $push: { addedItems: newProduct._id } },
  { upsert: true, new: true },
  function(err, doc) {
    console.log(err, doc);
  }
);

The console.log prints out this:

{
    //...
    addedItems: [ 5ab0223118599214f4dd7803 ]
}

Everything looks good. I go to actually look at the data using the front-end website for my mongo db; I'm using mlab.com, and this is what shows:

{
//...
"addedItems": [
        {
            "$oid": "5ab0223118599214f4dd7803"
        },
        {
            "$oid": "5ab0223118599214f4dd7803"
        }
    ]
}

Question: What the heck happened? Why does it add an additional entry into addedItems ?! Even though my console.log only showed one.

Note:

I tested to see if the backend route was being called more than once. It is not.

It seems to be a problem with $push because if I just have { addedItems: newProduct._id } then only one entry goes in, but it overwrites the entire array.

Edit:

Made a test project to produce the same results: https://github.com/philliprognerud/test-mcve-stackoverflow

Can anyone figure out what's going on?


Solution

  • The problem is caused by your mixed used of promises (via async/await) and callbacks with the findOneAndUpdate call which ends up executing the command twice.

    To fix the problem:

    const updatedUser = await User.findOneAndUpdate(
      { id: userID },
      { $push: { addedItems: newProduct.id } },
      { upsert: true, new: true }
    );
    
    console.log(updatedUser);
    

    Future readers note that the use of await isn't shown here in the question, but is in the MCVE.