MongoDB:как найти 10 случайных документов в коллекции из 100?
способен ли MongoDB финансировать количество случайных документов без создания нескольких запросов?
например, я реализовал на стороне JS после загрузки всего документа в коллекции, что является расточительным-следовательно, просто хотел проверить, можно ли это сделать лучше с одним запросом db?
путь, который я взял на стороне JS:
- получить все сведения
- сделать массив идентификаторов
- shuffle массив идентификаторов (случайный порядок)
- соедините массив с требуемым количеством документов
- создайте список документов, выбрав их по ID, который мы оставили после двух предыдущих операций, одну за другой из всей коллекции
два основных недостатка в том, что я загружаю все данные - или я делаю несколько запросов.
любое предложение очень ценится
4 ответов
как опубликовано в другом ответе, MongoDB теперь поддерживает выборка в рамках агрегирования начиная с версии 3.2:
способ, которым вы могли бы это сделать:
db.products.aggregate([{$sample: {size: 5}}]); // You want to get 5 docs
или:
db.products.aggregate([
{$match: {category:"Electronic Devices"}}, // filter the results
{$sample: {size: 5}} // You want to get 5 docs
]);
, есть предупреждения об операторе $ sample:
(по состоянию на 6 ноября 2017 года, где последняя версия 3.4) => If ничего из этого не выполнено:
- $sample является первой стадией трубопровода
- N составляет менее 5% от общего количества документов в коллекции
- коллекция содержит более 100 документов
если какое-либо из вышеперечисленных условий не выполняется, $sample выполняет сканирование коллекции с последующей случайной сортировкой для выбора N документов.
как в последнем примере с $ match
ОТВЕТ
вы всегда можете запустить:
db.products.find({category:"Electronic Devices"}).skip(Math.random()*YOUR_COLLECTION_SIZE)
но заказ не будет случайным, и вам понадобятся два запроса (один счетчик, чтобы получить YOUR_COLLECTION_SIZE) или оценить, насколько он велик (это около 100 записей, около 1000, около 10000...)
вы также можете добавить поле ко всем документам со случайным числом и запросить по этому номеру. Недостатком здесь будет то, что вы получите те же результаты каждый раз, когда вы запустите тот же вопрос. Чтобы исправить это, вы всегда можете играть с limit и пропустить или даже с сортировкой. вы также можете обновлять эти случайные числа каждый раз, когда вы получаете запись (подразумевает больше запросов).
--Я не знаю, используете ли вы Mongoose, Mondoid или непосредственно драйвер Mongo для любого конкретного языка, поэтому я напишу все о Mongo shell.
таким образом, скажем, запись продукта будет выглядеть так:
{
_id: ObjectId("..."),
name: "Awesome Product",
category: "Electronic Devices",
}
и я бы предложил использовать:
{
_id: ObjectId("..."),
name: "Awesome Product",
category: "Electronic Devices",
_random_sample: Math.random()
}
затем вы могли бы сделать:
db.products.find({category:"Electronic Devices",_random_sample:{$gte:Math.random()}})
затем вы можете периодически запускать, чтобы периодически обновлять поле _random_sample документа:
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)
или просто всякий раз, когда вы получаете некоторые записи, вы можете обновить их все или только несколько (в зависимости от того, сколько записей вы получили)
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);
}
редактировать
имейте в виду, что
db.products.update(your_query,{$set:{_random_sample::Math.random()}},false,true)
не будет работать очень хорошо, так как он будет обновлять каждый продукты, которые соответствуют вашему запросу с тот же случайных чисел. Последний подход работает лучше (обновление некоторых документов по мере их извлечения)
С 3.2 есть более простой способ получить случайную выборку документов из коллекции:
$образец Новое в версии 3.2.
случайным образом выбирает определенное количество документов на входе.
этап $ sample имеет следующий синтаксис:
{ $sample: { size: <positive integer> } }
в этом случае:
db.products.aggregate([{$sample: {size: 10}}]);
вот что я придумал в конце:
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);
});
});
скип не сработал для меня. Вот что я получил в итоге:
var randomDoc = db.getCollection("collectionName").aggregate([ {
$match : {
// criteria to filter matches
}
}, {
$sample : {
size : 1
}
} ]).result[0];
получает один случайный результат, соответствующий критериям.