我想从一个巨大的集合(1亿条记录)中获得一个随机记录。

最快最有效的方法是什么?

数据已经在那里,没有字段可以生成随机数并获得随机行。


当前回答

现在可以使用聚合了。 例子:

db.users.aggregate(
   [ { $sample: { size: 3 } } ]
)

去看医生。

其他回答

我最简单的解决办法是……

db.coll.find()
    .limit(1)
    .skip(Math.floor(Math.random() * 500))
    .next()

你至少有500件收藏品

您可以选择一个随机时间戳,然后搜索随后创建的第一个对象。 它将只扫描单个文档,尽管它不一定会给您一个统一的分布。

var randRec = function() {
    // replace with your collection
    var coll = db.collection
    // get unixtime of first and last record
    var min = coll.find().sort({_id: 1}).limit(1)[0]._id.getTimestamp() - 0;
    var max = coll.find().sort({_id: -1}).limit(1)[0]._id.getTimestamp() - 0;

    // allow to pass additional query params
    return function(query) {
        if (typeof query === 'undefined') query = {}
        var randTime = Math.round(Math.random() * (max - min)) + min;
        var hexSeconds = Math.floor(randTime / 1000).toString(16);
        var id = ObjectId(hexSeconds + "0000000000000000");
        query._id = {$gte: id}
        return coll.find(query).limit(1)
    };
}();

您可以选择随机_id并返回相应的对象:

 db.collection.count( function(err, count){
        db.collection.distinct( "_id" , function( err, result) {
            if (err)
                res.send(err)
            var randomId = result[Math.floor(Math.random() * (count-1))]
            db.collection.findOne( { _id: randomId } , function( err, result) {
                if (err)
                    res.send(err)
                console.log(result)
            })
        })
    })

在这里,你不需要花空间存储随机数字的集合。

您还可以使用MongoDB的地理空间索引功能来选择与随机数“最近”的文档。

首先,在集合上启用地理空间索引:

db.docs.ensureIndex( { random_point: '2d' } )

用x轴上的随机点创建一堆文档:

for ( i = 0; i < 10; ++i ) {
    db.docs.insert( { key: i, random_point: [Math.random(), 0] } );
}

然后你可以像这样从集合中随机获得一个文档:

db.docs.findOne( { random_point : { $near : [Math.random(), 0] } } )

或者你可以检索几个文档最近的随机点:

db.docs.find( { random_point : { $near : [Math.random(), 0] } } ).limit( 4 )

这只需要一个查询,没有空检查,加上代码干净,简单和灵活。您甚至可以使用地理点的y轴为查询添加第二个随机性维度。

MongoDB 3.2更新

3.2在聚合管道中引入了$sample。

还有一篇很好的博客文章是关于如何将其付诸实践的。

对于旧版本(以前的答案)

这实际上是一个功能请求:http://jira.mongodb.org/browse/SERVER-533,但它是在“不会修复”下提交的。

烹饪书中有一个非常好的方法,可以从集合中随机选择一个文档:http://cookbook.mongodb.org/patterns/random-attribute/

套用这个方法,你可以给你的文档分配随机数:

db.docs.save( { key : 1, ..., random : Math.random() } )

然后随机选择一个文档:

rand = Math.random()
result = db.docs.findOne( { key : 2, random : { $gte : rand } } )
if ( result == null ) {
  result = db.docs.findOne( { key : 2, random : { $lte : rand } } )
}

同时使用$gte和$lte进行查询,以找到与rand最接近的随机数的文档。

当然你要在随机场上建立索引

db.docs.ensureIndex( { key : 1, random :1 } )

如果您已经在查询一个索引,只需删除它,将random: 1附加到它,然后再次添加它。