web-dev-qa-db-fra.com

MongoDB- Insérer s'il n'existe pas, sinon sauter

Est-il possible d'insérer dans Mongo avec condition;

//Pseudo code

Bulk Insert Item :

If Key exists
    Skip, don't throw error
If key does not exist
    Add item

Si je fais des insertions simples, cela pourrait renvoyer une erreur ou insérer dans la collection, mais est-il possible dans bulk?

21
Basit Anwer

Vous avez ici deux choix réels selon la façon dont vous voulez gérer les choses:

  1. Utilisez la fonctionnalité psert de MongoDB pour essentiellement "rechercher" si les données clés existent. Sinon, vous transmettez uniquement les données à $setOnInsert et cela ne touchera à rien d'autre.

  2. Utilisez les opérations "non commandées" en bloc. L'ensemble du lot de mises à jour continuera même si une erreur est renvoyée, mais le ou les rapports d'erreur ne sont que cela, et tout ce qui n'est pas une erreur sera validé.

Exemple entier:

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var testSchema = new Schema({
  "_id": Number,
  "name": String
},{ "_id": false });

var Test = mongoose.model('Test',testSchema,'test');

mongoose.connect('mongodb://localhost/test');

var data = [
  { "_id": 1, "name": "One" },
  { "_id": 1, "name": "Another" },
  { "_id": 2, "name": "Two" }
];

async.series(
  [
    // Start fresh
    function(callback) {
      Test.remove({},callback);
    },

    // Ordered will fail on error. Upserts never fail!
    function(callback) {
      var bulk = Test.collection.initializeOrderedBulkOp();
      data.forEach(function(item) {
        bulk.find({ "_id": item._id }).upsert().updateOne({
          "$setOnInsert": { "name": item.name }
        });
      });
      bulk.execute(callback);
    },

    // All as expected
    function(callback) {
      Test.find().exec(function(err,docs) {
        console.log(docs)
        callback(err);
      });
    },


    // Start again
    function(callback) {
      Test.remove({},callback);
    },

    // Unordered will just continue on error and record an error
    function(callback) {
      var bulk = Test.collection.initializeUnorderedBulkOp();
      data.forEach(function(item) {
        bulk.insert(item);
      });
      bulk.execute(function(err,result) {
        callback(); // so what! Could not care about errors
      });
    },


    // Still processed the whole batch
    function(callback) {
      Test.find().exec(function(err,docs) {
        console.log(docs)
        callback(err);
      });
    }
  ],
  function(err) {
    if (err) throw err;
    mongoose.disconnect();
  }
);

Notez que "l'action modifiée" dans les pilotes actuels est que la réponse de résultat sur .execute() renverra un objet d'erreur à lancer, où les versions précédentes ne le faisaient pas avec les opérations "non ordonnées".

Il est donc impératif que votre code ne repose jamais sur le err renvoyé seul, et vous devriez plutôt insérer le result retourné à la place pour la classification complète des erreurs.

Néanmoins, lorsqu'il n'est pas ordonné, le lot continue jusqu'à la fin, quel que soit le nombre d'erreurs. Les choses qui ne sont pas une erreur seront commises normalement.

Cela revient vraiment à "la séquence est-elle importante". Si tel est le cas, vous avez besoin d'opérations "ordonnées" et vous ne pouvez éviter les clés en double qu'en utilisant des "upserts". Sinon, utilisez "non ordonné", mais soyez conscient des retours d'erreur et de ce qu'ils signifient réellement.

De plus, lorsque vous utilisez .collection Pour obtenir l'objet de collection sous-jacent à partir du pilote de base afin d'activer les opérations "Bulk", assurez-vous toujours que la méthode "some" mongoose a toujours été appelée en premier.

Sans cela, il n'y a pas de connexion garantie à la base de données avec les méthodes de pilote natives car elle est gérée pour les méthodes mangouste, donc l'opération échouera en raison de l'absence de connexion.

L'alternative au "tir" d'une méthode mangouste consiste d'abord à encapsuler la logique de votre application dans un écouteur d'événements pour la connexion:

mongoose.connection.on("open",function(err) {
    // app logic in here
})
24
Blakes Seven

Comme cela a déjà été dit, "insérer s'il n'existe pas déjà" peut être obtenu en utilisant la commande update avec l'option upsert définie sur true. Voici comment procéder avec le pilote 3.x node.js:

let ops = [];
ops.Push({ updateOne: { filter: {key:"value1"}, update: {} }, { upsert:true } });
ops.Push({ updateOne: { filter: {key:"value2"}, update: { $set:{/*...*/} } }, { upsert:true } });
ops.Push({ updateOne: { filter: {key:"value3"}, update: { { $setOnInsert:{/*...*/} } } }, { upsert:true } });
// < add more ops here >
await db.collection("my-collection").bulkWrite(ops, {ordered:false});

Si le filter ne renvoie aucun résultat, un nouveau document sera créé en utilisant les conditions du filtre et le $set mises à jour (le cas échéant). Si tu utilises $setOnInsert, les mises à jour ne sont appliquées qu'aux nouveaux documents.

Publier cet exemple car il aurait été pratique pour ma situation. Plus d'informations dans les documents pour db.collection.bulkWrite .

6
user993683

Utilisez setOnInsert

db.collection('collection').updateOne(
     { _id: data._id },
    { $setOnInsert: { ...data } },
    { upsert: true },
  )
0
Mbanda