MongoDB:如何在100个集合中找到10个随机文档?

Ila*_*sda 12 javascript random mongodb mlab restangular

MongoDB是否能够在不进行多次查询的情况下为随机文档提供资金?

例如,我在将所有文档加载到集合中后在JS端实现,这很浪费 - 因此只是想检查一次db查询是否可以做得更好?

我在JS方面采取的路径:

  • 获取所有数据
  • 制作一个ID数组
  • 随机数组ID(随机顺序)
  • 将数组拼接到所需的文档数
  • 创建一个文档列表,方法是按照我们在前两个操作之后离开的ID选择它们,从整个集合中逐个进行

两个主要缺点是我正在加载所有数据 - 或者我进行了多次查询.

任何建议都非常感谢

Dan*_*ick 27

从3.2开始,有一种更简单的方法可以从集合中获取随机的文档样本:

$ sample 3.2版本中的新功能.

从输入中随机选择指定数量的文档.

$ sample阶段具有以下语法:

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

资料来源:MongoDB文件

在这种情况下:

db.products.aggregate([{$sample: {size: 10}}]);
Run Code Online (Sandbox Code Playgroud)


Enr*_*eyo 20

这是很久以前的答案,从那时起,MongoDB已经有了很大的发展.

正如另一个答案所述,MongoDB现在支持从版本3.2开始在聚合框架内进行采样:

你可以这样做的方式是:

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

要么:

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

但是,有一些关于$ sample运算符的警告:

(截至2017年11月6日,最新版本为3.4)=>如果不满足以下任何条件:

  • $ sample是管道的第一阶段
  • N小于集合中文档总数的5%
  • 该集合包含100多个文档

如果不满足上述任何条件,$ sample将执行集合扫描,然后进行随机排序以选择N个文档.

就像在上一个与$ match的例子中一样

老答复

你总是可以运行:

db.products.find({category:"Electronic Devices"}).skip(Math.random()*YOUR_COLLECTION_SIZE)
Run Code Online (Sandbox Code Playgroud)

但订单不是随机的,你需要两个查询(一个计数来获得YOUR_COLLECTION_SIZE)或估计它有多大(它是大约100条记录,大约1000条,大约10000条...)

您还可以使用随机数向所有文档添加字段,并按该编号查询.这里的缺点是每次运行相同的查询时都会得到相同的结果.要解决此问题,您始终可以使用限制和跳过,甚至可以使用排序.您每次获取记录时都可以更新这些随机数(意味着更多查询).

- 我不知道您是否使用Mongoose,Mondoid或直接使用Mongo驱动程序获取任何特定语言,所以我会写关于mongo shell的所有信息.

因此,您的产品记录将如下所示:

{
 _id: ObjectId("..."),
 name: "Awesome Product",
 category: "Electronic Devices",
}
Run Code Online (Sandbox Code Playgroud)

我建议使用:

{
 _id: ObjectId("..."),
 name: "Awesome Product",
 category: "Electronic Devices",
 _random_sample: Math.random()
}
Run Code Online (Sandbox Code Playgroud)

然后你可以这样做:

db.products.find({category:"Electronic Devices",_random_sample:{$gte:Math.random()}})
Run Code Online (Sandbox Code Playgroud)

然后,您可以定期运行,以便定期更新文档的_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)
Run Code Online (Sandbox Code Playgroud)

或者只要你检索一些记录,就可以更新所有记录或只更新一些(取决于你检索的记录数)

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);
}
Run Code Online (Sandbox Code Playgroud)

编辑

意识到

db.products.update(your_query,{$set:{_random_sample::Math.random()}},false,true)
Run Code Online (Sandbox Code Playgroud)

将无法正常工作,因为它将使用相同的随机数更新与您的查询匹配的每个产品.最后一种方法效果更好(在检索时更新一些文档)