MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

MongoDB:come trovare 10 documenti casuali in una raccolta di 100?

Questa risposta è stata data molto tempo fa e, da allora, MongoDB si è notevolmente evoluto.

Come pubblicato in un'altra risposta, MongoDB ora supporta il campionamento all'interno di Aggregation Framework dalla versione 3.2:

Il modo in cui potresti farlo è:

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

Oppure:

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

Tuttavia, ci sono alcuni avvisi sull'operatore $sample:

(al 6 novembre 2017, dove l'ultima versione è la 3.4) => Se una di queste condizioni non viene soddisfatta:

  • $sample è la prima fase della pipeline
  • N è meno del 5% del totale dei documenti nella collezione
  • La collezione contiene più di 100 documenti

Se una delle condizioni di cui sopra NON è soddisfatta, $sample esegue una scansione della raccolta seguita da un ordinamento casuale per selezionare N documenti.

Come nell'ultimo esempio con $match

VECCHIA RISPOSTA

Puoi sempre correre:

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

Ma l'ordine non sarà casuale e avrai bisogno di due query (un conteggio per ottenere YOUR_COLLECTION_SIZE) o stimare quanto è grande (sono circa 100 record, circa 1000, circa 10000...)

Puoi anche aggiungere un campo a tutti i documenti con un numero casuale e interrogare in base a quel numero. Lo svantaggio qui sarebbe che otterrai gli stessi risultati ogni volta che esegui la stessa query. Per risolvere questo problema puoi sempre giocare con limit e skip o anche con sort. potresti anche aggiornare quei numeri casuali ogni volta che recuperi un record (implica più query).

--Non so se stai usando Mongoose, Mondoid o direttamente Mongo Driver per una lingua specifica, quindi scriverò tutto su mongo shell.

Quindi, diciamo, il tuo record di prodotto sarebbe simile a questo:

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

e suggerirei di usare:

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

Allora potresti fare:

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

quindi, potresti eseguire periodicamente in modo da aggiornare periodicamente il campo _random_sample del documento:

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)

o solo ogni volta che recuperi alcuni record puoi aggiornarli tutti o solo alcuni (a seconda di quanti record hai recuperato)

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);
}

MODIFICA

Tieni presente che

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

non funzionerà molto bene poiché aggiornerà tutti i prodotti che corrispondono alla tua richiesta con lo stesso numero casuale. L'ultimo approccio funziona meglio (aggiornando alcuni documenti man mano che li recuperi)