我想从一个巨大的集合(1亿条记录)中获得一个随机记录。
最快最有效的方法是什么?
数据已经在那里,没有字段可以生成随机数并获得随机行。
我想从一个巨大的集合(1亿条记录)中获得一个随机记录。
最快最有效的方法是什么?
数据已经在那里,没有字段可以生成随机数并获得随机行。
当前回答
有效可靠的方法是:
在每个文档中添加一个名为“random”的字段,并为其分配一个随机值,为该随机字段添加一个索引,如下所示:
让我们假设我们有一个名为“links”的网络链接集合,我们想从它中随机链接:
link = db.links.find().sort({random: 1}).limit(1)[0]
为了确保同一个链接不会第二次弹出,用一个新的随机数更新它的随机场:
db.links.update({random: Math.random()}, link)
其他回答
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附加到它,然后再次添加它。
使用Python (pymongo),聚合函数也可以工作。
collection.aggregate([{'$sample': {'size': sample_size }}])
这种方法比对随机数(例如collection.find([random_int]))运行查询要快得多。对于大型收藏来说尤其如此。
我对php的解决方案:
/**
* Get random docs from Mongo
* @param $collection
* @param $where
* @param $fields
* @param $limit
* @author happy-code
* @url happy-code.com
*/
private function _mongodb_get_random (MongoCollection $collection, $where = array(), $fields = array(), $limit = false) {
// Total docs
$count = $collection->find($where, $fields)->count();
if (!$limit) {
// Get all docs
$limit = $count;
}
$data = array();
for( $i = 0; $i < $limit; $i++ ) {
// Skip documents
$skip = rand(0, ($count-1) );
if ($skip !== 0) {
$doc = $collection->find($where, $fields)->skip($skip)->limit(1)->getNext();
} else {
$doc = $collection->find($where, $fields)->limit(1)->getNext();
}
if (is_array($doc)) {
// Catch document
$data[ $doc['_id']->{'$id'} ] = $doc;
// Ignore current document when making the next iteration
$where['_id']['$nin'][] = $doc['_id'];
}
// Every iteration catch document and decrease in the total number of document
$count--;
}
return $data;
}
现在可以使用聚合了。 例子:
db.users.aggregate(
[ { $sample: { size: 3 } } ]
)
去看医生。
我最简单的解决办法是……
db.coll.find()
.limit(1)
.skip(Math.floor(Math.random() * 500))
.next()
你至少有500件收藏品