2016-05-24 1 views
1

Ma collection a des dossiers comme:MongoDB vérification des dossiers en double pour les insertions en vrac

{ "_id":"1", "field1":"foo","field2":"xyz", "field3":"something" ...} 
{ "_id":"2", "field1":"bar","field2":"xyz", "field3":"something" ...} 
{ "_id":"3", "field1":"foo","field2":"abc", "field3":"something" ...} 
{ "_id":"4", "field1":"bar","field2":"lmn", "field3":"something" ...} 

Avant d'insérer un nouveau disque, je dois vérifier si un enregistrement avec la même valeur champ1 et champ2 existe déjà. Et puis rejeter la demande, si elle existe déjà. Je peux réussir à faire ceci si j'insérais un disque à la fois. Comment est-ce que je manipule ceci si im faisant un encart en vrac (c'est-à-dire quand je m insère le tableau des documents)?

Je tableau de [field1, field2] combinaisons que je dois rechercher EX:

querryArray=[ { "field1":"foo","field2":"xyz"}, 
       { "field1":"bar","field2":"lmn"}] 

Résultat attendu:

result=[ { "_id":"1", "field1":"foo","field2":"xyz", "field3":"something" ...}, 
      { "_id":"4", "field1":"bar","field2":"lmn", "field3":"something" ...}] 
+0

une façon de le faire est de créer un index unique sur {filed1: 1, field2: 1} qui assure que le field1 et filed2 sont uniques en insérant les docs – Astro

Répondre

3

Créer une unique compound index sur les deux champs

db.collection.createIndex({ "field1": 1, "field2": 1 }, { "unique": true }) 

utilisation le insertMany() méthode pour effectuer l'insertion en bloc, mais définissez l'option ordered sur false car cela garantit que toutes les opérations d'écriture sont tentées, même en cas d'erreurs. les opérations commandées arrêtent après une erreur, alors que les opérations désordonnées continuent de traiter les opérations d'écriture restant dans la file d'attente:

var queryArray = [ 
    { "field1": "foo", "field2": "xyz" }, 
    { "field1": "bar", "field2": "lmn" } 
]; 
try { db.collection.insertMany(queryArray, { "ordered": false }); } 
catch (e) { print (e); } 

Ceci affichera un document

{ 
    "acknowledged" : true, 
    "insertedIds" : [ 
     ObjectId("57443e6fa58e5654f3a6c5ae"), 
     ObjectId("57443e6fa58e5654f3a6c5af") 
    ] 
} 

Le document qui en résulte montre les champs reconnus comme vrai si l'opération s'est déroulée avec un problème d'écriture ou une erreur si l'inquiétude d'écriture a été désactivée et un tableau de _id pour chaque document inséré avec succès. Parce que les documents dans queryArray n'incluaient pas _id, mongod crée et ajoute le champ _id pour chaque document et lui assigne une valeur ObjectId unique. Et puisque vous avez imposé l'unicité sur les deux champs field1 et field2, ce qui précède montre la tentative d'écriture car l'opération n'était pas ordonnée et a donc continué à traiter toutes les opérations d'écriture restantes.


Supposons que vous aviez supprimé l'option ordonnée (par défaut, il est défini sur true), alors vous obtenir la sortie suivante de l'opération:

var queryArray = [ 
    { "field1": "foo", "field2": "xyz" }, 
    { "field1": "bar", "field2": "lmn" } 
]; 
try { db.collection.insertMany(queryArray); } 
catch (e) { print (e); } 

Sortie de la console:

{ 
    "name" : "BulkWriteError", 
    "message" : "write error at item 0 in bulk operation", 
    "ok" : undefined, 
    "nInserted" : 0, 
    "nUpserted" : 0, 
    "nMatched" : 0, 
    "nModified" : 0, 
    "nRemoved" : 0, 
    "getUpsertedIds" : function() { 
     return bulkResult.upserted; 
    }, 
    "getUpsertedIdAt" : function (index) { 
     return bulkResult.upserted[index]; 
    }, 
    "getRawResponse" : function() { 
     return bulkResult; 
    }, 
    "hasWriteErrors" : function() { 
     return bulkResult.writeErrors.length > 0; 
    }, 
    "getWriteErrorCount" : function() { 
     return bulkResult.writeErrors.length; 
    }, 
    "getWriteErrorAt" : function (index) { 
     if(index < bulkResult.writeErrors.length) { 
     return bulkResult.writeErrors[index]; 
     } 
     return null; 
    }, 
    "getWriteErrors" : function() { 
     return bulkResult.writeErrors; 
    }, 
    "hasWriteConcernError" : function() { 
     return bulkResult.writeConcernErrors.length > 0; 
    }, 
    "getWriteConcernError" : function() { 
     if(bulkResult.writeConcernErrors.length == 0) { 
     return null; 
     } else if(bulkResult.writeConcernErrors.length == 1) { 
     // Return the error 
     return bulkResult.writeConcernErrors[0]; 
     } else { 

     // Combine the errors 
     var errmsg = ""; 
     for(var i = 0; i < bulkResult.writeConcernErrors.length; i++) { 
      var err = bulkResult.writeConcernErrors[i]; 
      errmsg = errmsg + err.errmsg; 
      // TODO: Something better 
      if (i != bulkResult.writeConcernErrors.length - 1) { 
      errmsg = errmsg + " and "; 
      } 
     } 

     return new WriteConcernError({ errmsg : errmsg, code : WRITE_CONCERN_FAILED }); 
     } 
    }, 
    "tojson" : function (indent, nolint) { 
     return tojson(bulkResult, indent, nolint); 
    }, 
    "toString" : function() { 
     return "BulkWriteError(" + this.tojson() + ")"; 
    }, 
    "shellPrint" : function() { 
     return this.toString(); 
    }, 
    "hasErrors" : function() { 
     return this.hasWriteErrors() || this.hasWriteConcernError(); 
    }, 
    "toSingleResult" : function() { 
     if(singleBatchType == null) throw Error(
      "Cannot output single WriteResult from multiple batch result"); 
     return new WriteResult(bulkResult, singleBatchType, writeConcern); 
    }, 
    "stack" : "BulkWriteError({\n\t\"writeErrors\" : [\n\t\t{\n\t\t\t\"index\" : 0,\n\t\t\t\"code\" : 11000,\n\t\t\t\"errmsg\" : \"E11000 duplicate key error index: test.collection.$field1_1_field2_1 dup key: { : \\\"foo\\\", : \\\"xyz\\\" }\",\n\t\t\t\"op\" : {\n\t\t\t\t\"_id\" : ObjectId(\"574441aea58e5654f3a6c5b6\"),\n\t\t\t\t\"field1\" : \"foo\",\n\t\t\t\t\"field2\" : \"xyz\"\n\t\t\t}\n\t\t}\n\t],\n\t\"writeConcernErrors\" : [ ],\n\t\"nInserted\" : 0,\n\t\"nUpserted\" : 0,\n\t\"nMatched\" : 0,\n\t\"nModified\" : 0,\n\t\"nRemoved\" : 0,\n\t\"upserted\" : [ ]\n})\[email protected]/mongo/shell/bulk_api.js:372:44\nBulkWriteResult/[email protected]/mongo/shell/bulk_api.js:335:16\nBulk/[email protected]/mongo/shell/bulk_api.js:1162:1\[email protected]/mongo/shell/crud_api.js:279:5\[email protected](shell):1:7\n", 
    "toResult" : function() { 
     return new BulkWriteResult(bulkResult, singleBatchType, writeConcern); 
    } 
} 

en mettant l'accent sur l'erreur d'écriture retourné:

"E11000 duplicate key error index: test.collection.$field1_1_field2_1 dup key: { : \\\"foo\\\", : \\\"xyz\\\" }\" 

En dehors de la méthode insertMany(), vous pouvez également essayer les Bulk() méthodes de l'API où, en particulier, vous devez appeler la méthode initializeUnorderedBulkOp() faire un insert en vrac non ordonnée après la création de l'indice composé unique.

Prenons l'exemple suivant pour le cas ci-dessus:

db.collection('collectionName', function(err, collection) { 
    var bulk = collection.initializeUnorderedBulkOp(); 
    counter = 0; 

    querryArray.forEach(function (doc){ 
     bulk.insert(doc); 
     counter++; 

     if (counter % 1000 == 0) { 
      bulk.execute(function(err, result) { 
       // you could do something with results, check for duplicate errors 
       bulk = collection.initializeUnorderedBulkOp(); // re-initialise 
      }); 
     } 
    }); 

    // Clean-up remaining operations in the queue 
    if (counter % 1000 != 0) {  
     bulk.execute(function(err, result) { 
      // you could do something with results, check for duplicate errors 
      console.log(result); 
     }); 
    } 
}); 
+0

, Merci pour l'explication détaillée. Cette solution fonctionne parfaitement bien. Je pense que c'est une approche très soignée et propre. J'utilise mongo-skin pour mes interactions db, et on dirait qu'ils ne supportent pas la fonction insertMany. Malheureusement, je dois trouver un autre moyen de contourner cela maintenant. –

+0

@KavyaMugali J'ai ajouté une solution de contournement qui utilise l'API 'Bulk()' que mongoskin prend en charge car il suffit de l'appeler en utilisant l'objet de collection sous-jacent de mongoskin – chridam

+1

Votre solution fait à nouveau la magie. Merci beaucoup :) –

0

En passant par https://docs.mongodb.com/manual/reference/method/Bulk.find.update/#Bulk.find.update, semble que nous pouvons utiliser les méthodes d'exploitation en vrac. Cependant, notez que vous devez toujours appeler bulk.find(). Upsert(). UpdateOne(), pour chaque document de votre tableau de requêtes, car vous souhaitez rechercher et mettre à jour le document correspondant à partir de votre queryArray. Néanmoins, l'utilisation de bulk peut être préférable car vous pouvez exécuter la requête en une fois, après l'avoir préparée, au lieu d'exécuter chaque requête une par une.

par exemple. en utilisant shell MongoDB

> //verifying db is empty 
> db.items.find() 
> 
> //creating my bulk update function 
> var myUpdate = function (rec) { this.find(rec).upsert().updateOne({$set: rec}); return null; } 
> 
> //initializing docs array with 4 documents 
> var docs = 
... [ 
...  { "_id":"1", "field1":"foo","field2":"xyz", "field3":"something31"}, 
...  { "_id":"2", "field1":"bar","field2":"xyz", "field3":"something32"}, 
...  { "_id":"3", "field1":"foo","field2":"abc", "field3":"something33"}, 
...  { "_id":"4", "field1":"bar","field2":"lmn", "field3":"something34", "field4": "something44" } 
... ] 
> 
> //initializing the bulk operation object 
> var bulk = db.items.initializeUnorderedBulkOp(); 
> 
> //checking current state of bulk object 
> bulk 
{ "nInsertOps" : 0, "nUpdateOps" : 0, "nRemoveOps" : 0, "nBatches" : 0 } 
> 
> //maping each doc in the docs array to my update function 
> docs.map(myUpdate, bulk); 
[ null, null, null, null ] 
> 
> //checking current state of bulk object 
> bulk 
{ "nInsertOps" : 0, "nUpdateOps" : 4, "nRemoveOps" : 0, "nBatches" : 1 } 
> 
> 
> //now actually bulk updating the db 
> bulk.execute(); 
BulkWriteResult({ 
     "writeErrors" : [ ], 
     "writeConcernErrors" : [ ], 
     "nInserted" : 0, 
     "nUpserted" : 4, 
     "nMatched" : 0, 
     "nModified" : 0, 
     "nRemoved" : 0, 
     "upserted" : [ 
       { 
         "index" : 0, 
         "_id" : "1" 
       }, 
       { 
         "index" : 1, 
         "_id" : "2" 
       }, 
       { 
         "index" : 2, 
         "_id" : "3" 
       }, 
       { 
         "index" : 3, 
         "_id" : "4" 
       } 
     ] 
}) 
> 
> 
> //checking for newly insert docs 
> db.items.find(); 
{ "_id" : "1", "field1" : "foo", "field2" : "xyz", "field3" : "something31" } 
{ "_id" : "2", "field1" : "bar", "field2" : "xyz", "field3" : "something32" } 
{ "_id" : "3", "field1" : "foo", "field2" : "abc", "field3" : "something33" } 
{ "_id" : "4", "field1" : "bar", "field2" : "lmn", "field3" : "something34", "field4" : "something44" } 
> 
> 
> //now preparing to upsert new docs (might be existing docs, per your example) 
> var newDocs = 
... [ 
...  { "field1":"foo","field2":"xyz"}, 
...  { "field1":"bar","field2":"lmn"} 
... ] 
> 
> 
> //initializing the bulk object 
> var bulk = db.items.initializeUnorderedBulkOp(); 
> 
> //mapping the myUpdate function to each new document in the newDocs array 
> newDocs.map(myUpdate, bulk); 
[ null, null ] 
> 
> 
> //now actually bulk updating the db 
> bulk.execute(); 
BulkWriteResult({ 
     "writeErrors" : [ ], 
     "writeConcernErrors" : [ ], 
     "nInserted" : 0, 
     "nUpserted" : 0, 
     "nMatched" : 2, 
     "nModified" : 0, 
     "nRemoved" : 0, 
     "upserted" : [ ] 
}) 
> 
> //notice how the nMatched = 2, and nModified = 0 
> 
> //verifying that nothing changed in the db 
> db.items.find({$or: newDocs}) 
{ "_id" : "1", "field1" : "foo", "field2" : "xyz", "field3" : "something31" } 
{ "_id" : "4", "field1" : "bar", "field2" : "lmn", "field3" : "something34", "field4" : "something44" } 
> 
>