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?
Vous avez ici deux choix réels selon la façon dont vous voulez gérer les choses:
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.
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
})
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 .
Utilisez setOnInsert
db.collection('collection').updateOne(
{ _id: data._id },
{ $setOnInsert: { ...data } },
{ upsert: true },
)