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

Posted

技术标签:

【中文标题】MongoDB:如何在 100 个集合中找到 10 个随机文档?【英文标题】:MongoDB: how to find 10 random document in a collection of 100? 【发布时间】:2014-09-08 12:18:05 【问题描述】:

MongoDB 是否能够在不进行多次查询的情况下资助大量随机文档?

例如我在加载集合中的所有文档后在 JS 端实现,这很浪费 - 因此只想检查是否可以通过一个 db 查询更好地完成?

我在JS这边走的路:

获取所有数据 制作一个 ID 数组 洗牌 ID 数组(随机顺序) 将数组拼接到所需的文档数 通过按我们在前两次操作后留下的 ID 选择文档来创建文档列表,从整个集合中逐一选择

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

非常感谢任何建议

【问题讨论】:

真的只有 100 份的 10 份文件吗?如果是这样,那么如果当前的解决方案有效,为什么还要优化呢? 嗯,这只是一个例子,我希望集合增长到1000s 【参考方案1】:

很久以前就回答了这个问题,从那时起,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)

不会很好地工作,因为它会使用相同的随机数更新与您的查询匹配的所有产品。最后一种方法效果更好(在检索某些文档时更新它们)

【讨论】:

【参考方案2】:

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

$样本 3.2 版中的新功能。

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

$sample 阶段的语法如下:

$sample: size: &lt;positive integer&gt;

Source: MongoDB Docs

在这种情况下:

db.products.aggregate([$sample: size: 10]);

【讨论】:

请注意,使用此方法可能会在响应中返回重复的文档。小心!【参考方案3】:

这是我最后想到的:

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

    );

);

【讨论】:

【参考方案4】:

skip 不适合我。这是我的结论:

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

获取单个随机结果,匹配条件。

【讨论】:

【参考方案5】:

示例可能不是最好的,因为您不会像那样获得虚拟。 相反,请在后端创建一个对结果进行洗牌的函数。 然后返回打乱后的数组而不是mongodb结果

【讨论】:

以上是关于MongoDB:如何在 100 个集合中找到 10 个随机文档?的主要内容,如果未能解决你的问题,请参考以下文章

带有排序的 MongoDB 范围查询 - 如何加快速度?

如何从 MongoDB 集合中存在的一组文档中找到数组的最大长度?

从 MongoDB 集合中的特定文档开始获取“n”个文档

MongoDB固定集合

如何使用 mongoDb 在单词序列集合中搜索输入单词

在庞大的 mongodb 集合中快速搜索非常稀有的字段