8 votes

Mongoose $push continue d'ajouter deux entrées

Voici mes user y product des schémas :

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

Dans ma route dorsale Node, je fais cette requête :

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

En console.log imprime ceci :

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

Tout semble bon. Je vais regarder les données en utilisant le site Web frontal de mon BDM. mlab.com et c'est ce qui ressort :

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

Question : Que s'est-il passé ? Pourquoi ajoute-t-il une entrée supplémentaire dans addedItems ? ! Même si mon console.log n'en montre qu'une.

Nota:

J'ai testé pour voir si la route backend était appelée plus d'une fois. Ce n'est pas le cas.

Il semble que ce soit un problème avec $push parce que si j'ai juste { addedItems: newProduct._id } alors une seule entrée entre, mais elle écrase le tableau entier.

Edit :

J'ai fait un projet de test pour produire les mêmes résultats : https://github.com/philliprognerud/test-mcve-stackoverflow

Quelqu'un peut-il comprendre ce qui se passe ?

10voto

JohnnyHK Points 61191

Le problème est dû à l'utilisation mixte de promesses (via async/await) et de callbacks avec l'attribut findOneAndUpdate qui finit par exécuter deux fois la commande.

Pour régler le problème :

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

console.log(updatedUser);

Les futurs lecteurs noteront que l'utilisation de await n'est pas indiqué ici dans la question, mais est dans le MCVE.

0voto

Ben P Points 41

Je suis confronté à un problème similaire. Je viens d'atterrir sur cette page. Je trouve que la réponse précédente n'est pas très descriptive. Je poste donc celle-ci :

export const updateUserHandler = async (req, res) => {
    const request = req.body;    
 await  User.findOneAndUpdate(                  //<== remove await 
  { _id: request.id },
  { $push: { addedItems: newProduct._id } },
  { upsert: true, new: true },
  (findErr, findRes) => {
        if (findErr) {
          res.status(500).send({
            message: 'Failed: to update user',
            IsSuccess: false,
            result: findErr
          });
        } else {
          res.status(200).send({
            message: 'Success:  to update user',
            IsSuccess: true,
            result: findRes
          });

        }
      }
);
  }

Ici, il y a deux appels asynchrones : l'un est le asynchrone et l'autre est attendre . De ce fait, il y a deux entrées dans le document. Il suffit de supprimer await de attendre User.findOneAndUpdate. Cela fonctionnera parfaitement. Merci !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X