很久以前就回答了这个问题,从那时起,MongoDB 有了很大的发展。
正如在另一个答案中发布的那样,MongoDB 现在支持 sampling within the Aggregation Framework,因为版本 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 操作符有some warnings:
(截至 2017 年 11 月 6 日,最新版本为 3.4)=> 如果不满足任何条件:
- $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 条......)
您还可以使用随机数向所有文档添加一个字段并按该数字进行查询。这里的缺点是每次运行相同的查询时都会得到相同的结果。要解决此问题,您始终可以使用限制和跳过甚至排序。您也可以在每次获取记录时更新这些随机数(意味着更多查询)。
--我不知道你是在使用Mongoose、Mondoid还是直接使用Mongo Driver来实现任何特定语言,所以我会写关于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)
不会很好地工作,因为它会使用相同的随机数更新与您的查询匹配的所有产品。最后一种方法效果更好(在检索某些文档时更新它们)