2014-07-17 3 views
7

MongoDB est-il capable de financer un nombre de documents aléatoires sans faire plusieurs requêtes?MongoDB: comment trouver 10 documents aléatoires dans une collection de 100?

par exemple. J'ai implémenté du côté de JS après avoir chargé tout le document dans la collection, ce qui est gaspillage - donc je voulais juste vérifier si cela peut être fait mieux avec une requête db?

Le chemin que je pris sur le côté JS:

  • obtenir toutes les données
  • faire un tableau des ID
  • tableau de lecture aléatoire des ID (ordre aléatoire)
  • de raccordement du tableau à la nombre de document requis
  • créer une liste de documents en les sélectionnant par ID que nous avons laissés après deux opérations précédentes, un par un de l'ensemble de la collection

Deux inconvénients majeurs sont que je charge toutes les données - ou je fais plusieurs requêtes.

Toute suggestion très appréciée

+0

Est-ce vraiment seulement 10 documents de 100? Si oui, alors pourquoi optimiser si la solution actuelle fonctionne? – Prinzhorn

+0

Eh bien, ce n'est qu'un exemple, je m'attends à ce que la collection se développe en 1000s – Iladarsda

Répondre

8

Ce fut il y a répondu depuis longtemps et, depuis lors, MongoDB a beaucoup évolué.

Comme posté dans une autre réponse, MongoDB prend désormais en charge sampling within the Aggregation Framework depuis la version 3.2:

La façon dont vous pouvez faire est:

db.products.aggregate([{$sample: {size: 5}}]); // You want to get 5 docs 

Ou:

db.products.aggregate([ 
    {$match: {category:"Electronic Devices"}}, // filter the results 
    {$sample: {size: 5}} // You want to get 5 docs 
]); 

Cependant, il y a some warnings à propos de l'opérateur d'échantillonnage $:

(comme f nov 6 h 2017, où la plus récente version 3.4) => Si tout cela n'est pas remplie:

  • échantillon $ est la première étape du pipeline
  • N est inférieur à 5% des documents au total dans la collection
  • la collection contient plus de 100 documents

Si l'une des conditions ci-dessus ne sont pas remplies, $ exemple effectue un suivi scan collection par un tri aléatoire pour sélectionner les documents N.

Comme dans le dernier exemple avec le match de $

VIEUX ANSWER

Vous pouvez toujours courir:

db.products.find({category:"Electronic Devices"}).skip(Math.random()*YOUR_COLLECTION_SIZE) 

Mais l'ordre ne sera pas aléatoire et vous besoin de deux requêtes (un compte pour obtenir YOUR_COLLECTION_SIZE) ou d'estimer combien il est gros (il s'agit d'environ 100 enregistrements, environ 1000, environ 10000 ...

Vous pouvez également ajouter un champ à tous les documents avec un nombre aléatoire et interroger par ce numéro. L'inconvénient ici serait que vous obtiendrez les mêmes résultats chaque fois que vous exécutez la même requête. Pour résoudre ce problème, vous pouvez toujours jouer avec la limite et sauter ou même avec le tri. vous pourriez aussi bien mettre à jour ces nombres aléatoires chaque fois que vous récupérez un enregistrement (implique plus de requêtes).

--Je ne sais pas si vous utilisez Mongoose, Mondoid ou directement le pilote Mongo pour une langue spécifique, donc je vais tout écrire sur mongo shell.

Ainsi votre, disons, fiche produit ressemblerait à ceci:

{ 
_id: ObjectId("..."), 
name: "Awesome Product", 
category: "Electronic Devices", 
} 

et je suggère d'utiliser:

{ 
_id: ObjectId("..."), 
name: "Awesome Product", 
category: "Electronic Devices", 
_random_sample: Math.random() 
} 

Ensuite, vous pouvez faire:

db.products.find({category:"Electronic Devices",_random_sample:{$gte:Math.random()}}) 

Ensuite, vous pouvez exécuter périodiquement afin de mettre à jour périodiquement le champ _random_sample du document:

var your_query = {} //it would impact in your performance if there are a lot of records 
your_query = {category: "Electronic Devices"} //Update 
//upsert = false, multi = true 
db.products.update(your_query,{$set:{_random_sample::Math.random()}},false,true) 

ou tout simplement à chaque fois que vous récupérez des enregistrements que vous pouvez mettre à jour tous ou quelques-uns (selon le nombre de dossiers que vous avez récupéré)

for(var i = 0; i < records.length; i++){ 
    var query = {_id: records[i]._id}; 
    //upsert = false, multi = false 
    db.products.update(query,{$set:{_random_sample::Math.random()}},false,false); 
} 

EDIT

Be conscient que

db.products.update(your_query,{$set:{_random_sample::Math.random()}},false,true) 

ne fonctionnera pas très bien car il mettra à jour chaque pro conduits qui correspond à votre requête avec le même nombre aléatoire. La dernière approche fonctionne mieux (mise à jour des documents que vous les récupérer)

2

Voici ce que je suis venu à la fin:

var numberOfItems = 10; 


// GET LIST OF ALL ID's 
SchemaNameHere.find({}, { '_id': 1 }, function(err, data) { 

    if (err) res.send(err); 

    // shuffle array, as per here https://github.com/coolaj86/knuth-shuffle 
    var arr = shuffle(data.slice(0)); 

    // get only the first numberOfItems of the shuffled array 
    arr.splice(numberOfItems, arr.length - numberOfItems); 

    // new array to store all items 
    var return_arr = []; 

    // use async each, as per here http://justinklemm.com/node-js-async-tutorial/ 
    async.each(arr, function(item, callback) { 

     // get items 1 by 1 and add to the return_arr 
     SchemaNameHere.findById(item._id, function(err, data) { 

      if (err) res.send(err); 
      return_arr.push(data); 

      // go to the next one item, or to the next function if done 
      callback(); 

     }); 

    }, function(err) { 

     // run this when looped through all items in arr 
     res.json(return_arr); 

    }); 

}); 
23

Depuis 3.2, il est un moyen plus facile d'obtenir un échantillon aléatoire de documents à partir d'une collection:

$ échantillon Nouveauté de la version 3.2.

Sélectionne aléatoirement le nombre de documents spécifié à partir de son entrée.

L'étape de l'échantillon de $ a la syntaxe suivante:

{ $sample: { size: <positive integer> } }

Source: MongoDB Docs

Dans ce cas:

db.products.aggregate([{$sample: {size: 10}}]); 
+0

cela devrait être la réponse acceptée – nxmohamad

0

saut n'a pas fonctionné pour moi.Voici ce que j'ai fini avec:

var randomDoc = db.getCollection("collectionName").aggregate([ { 
    $match : { 
// criteria to filter matches 
    } 
}, { 
    $sample : { 
     size : 1 
    } 
} ]).result[0]; 

obtient un résultat aléatoire unique, correspondant aux critères.

Questions connexes