2015-11-18 3 views
1

Il existe un certain nombre de questions et de réponses concernant l'ordre aléatoire des résultats ou l'obtention aléatoire d'un seul enregistrement. Les réponses recommandent d'ajouter un champ aléatoire, de créer un index sur ce champ, puis de faire un tirage au sort. Il ressemble à:Comment ajouter en vrac un champ aléatoire à chaque enregistrement dans MongoDB

db.myindex.find().forEach(function(doc) { 
    db.myindex.update({_id: doc._id}, {$set: {rand: Math.random()}}) 
}) 

Cela fonctionne très bien, mais cela prend plusieurs heures (beaucoup, beaucoup de données). Cela semble limité par le verrouillage d'écriture, ce qui est logique puisque la mise à jour est en cours pour chaque enregistrement. Comment est-ce que je fais cela en vrac? J'ai essayé:

var bulk = db.myindex.initializeUnorderedBulkOp(); 
bulk.find({}).update({ $set: { rand: Math.random() } }); 
bulk.execute(); 

Mais il définit le champ rand à la même valeur pour chaque enregistrement! Comment puis-je réparer ça? En passant, la raison pour laquelle je dois faire ceci est parce que je reçois un énorme fichier bson de quelqu'un d'autre et j'ai besoin de l'importer fréquemment, donc je ne peux pas attendre plusieurs heures pour le mettre à jour.

+0

peut-être attendez-vous un peu à la version 3.2, alors vous pouvez utiliser l'opérateur '$ sample': https://docs.mongodb.org/manual/release-notes/3.2-reference/ – nickmilon

Répondre

1

Introduire une boucle avec les opérations en vrac envoyés au serveur une fois par 1000 documents, ou autant de modifications que vous pouvez adapter sous la limite de BSON 64MB:

var bulk = db.myindex.initializeOrderedBulkOp(); 
var counter = 0; 

db.myindex.find().forEach(function(doc) { 

    bulk.find({ "_id": doc._id }).updateOne({ 
     "$set": { "rand": Math.random() } 
    }); 

    counter++; 

    if (counter % 1000 == 0) { 
     bulk.execute(); 
     bulk = db.myindex.initializeOrderedBulkOp(); 
    } 

}); 

if (counter % 1000 != 0){ 
    bulk.execute(); 
} 
0

Si la collecte est des données purement statiques, et vous obtenez un fichier BSON de quelqu'un d'autre, il pourrait être plus rapide de diffuser le fichier BSON à travers un filtre pour générer un nouveau fichier BSON que vous pouvez ensuite importer en utilisant mongoimport.

En voici une que j'ai écrite en utilisant nodeJS qui peut traiter un fichier BSON à environ 1 Go/min.

var bson = require('bson'); 
var BSON = new bson.BSONPure.BSON(); 
var BSONStream = require('bson-stream'); 
var fs = require('fs'); 
var sb = require('stream-buffers'); 
var rs = fs.createReadStream('tweets.bson'); 
var ws = fs.createWriteStream('tweets_random.bson',{flags:'a'}); 

var writeBuffer = new sb.WritableStreamBuffer({ 
    initialSize: (1024*1024), 
    incrementAmount: (10*1024) 
});  
rs.pipe(new BSONStream()).on('data',function(obj) { 
    obj.rand = Math.random(); 
    writeBuffer.write(BSON.serialize(obj)); 
    if(writeBuffer.size()>(1024*1024)) { 
     var size = writeBuffer.size(); 
     ws.write(writeBuffer.getContents(),function() { 
      console.log("Wrote",size,"bytes"); 
      console.log("Buffer has:",writeBuffer.size(),"bytes left"); 
     }); 
    } 
}); 

Il est possible que cela accélère si vous modifiez les paramètres de taille/incrément de la mémoire tampon.

Cela suppose bien sûr que vous ayez le luxe de réimporter vos données.