来自MongoDB的随机记录


336

我希望从一个巨大的记录(一亿个记录)中获得一个随机记录mongodb

最快,最有效的方法是什么?数据已经存在,并且没有可以生成随机数并获得随机行的字段。

有什么建议么?


2
另请参阅标题为“在mongo中随机订购结果集”的SO问题。考虑随机排序结果集是此问题的一个更通用的版本-功能更强大,更有用。
David J. 2012年

11
这个问题不断出现。可以在功能请求中找到最新信息,以从 MongoDB票务跟踪器中的集合获取随机物品。如果在本地实施,则可能是最有效的选择。(如果您需要该功能,请对其进行投票。)
David J.

这是碎片集合吗?
童迪伦

3
@JohnnyHK在下面给出了正确的答案:db.mycoll.aggregate({$ sample:{size:1}})
Florian

有谁知道这比仅仅取得第一张唱片要慢多少?我正在辩论是否应该随机抽样做某事而不是按顺序做。

Answers:


248

从MongoDB 3.2版本开始,您可以使用$sample聚合管道运算符从集合中获取N个随机文档:

// Get one random document from the mycoll collection.
db.mycoll.aggregate([{ $sample: { size: 1 } }])

如果要从集合的过滤子集中选择随机文档$match,请在管道之前添加一个阶段:

// Get one random document matching {a: 10} from the mycoll collection.
db.mycoll.aggregate([
    { $match: { a: 10 } },
    { $sample: { size: 1 } }
])

如评论中所述,当size大于1时,返回的文档样本中可能有重复项。


12
这是一个好方法,但是请记住,它不能保证样本中没有相同对象的副本。
Matheus Araujo,2016年

10
@MatheusAraujo,无论您想要一张唱片还是好点都没关系
Toby

3
不必过于琐,但问题并没有指定MongoDB版本,因此我认为拥有最新版本是合理的。
dalanmiller '16

2
@Nepoxx有关处理的信息,请参阅文档
JohnnyHK '16

2
@brycejl如果$ sample阶段未选择任何匹配的文档,则将具有致命的缺陷,即不匹配任何内容。
JohnnyHK

115

对所有记录进行计数,生成介于0和该计数之间的随机数,然后执行以下操作:

db.yourCollection.find().limit(-1).skip(yourRandomNumber).next()

139
不幸的是,skip()效率很低,因为它必须扫描那么多文档。另外,如果在获取计数和运行查询之间删除了行,则存在竞争条件。
mstearn 2010年

6
请注意,随机数应介于0和计数之间(不包括在内)。即,如果您有10个项目,则随机数应在0到9之间。否则,光标可能会尝试跳过最后一个项目,并且不会返回任何内容。
马特

4
谢谢,非常适合我的目的。@mstearn,您对效率和竞争条件的评论都是有效的,但是对于无关紧要的集合(不删除记录的集合中的服务器端一次性批处理提取),这远远优于hacky(IMO) Mongo食谱中的解决方案。
Michael Moussa 2012年

4
将限制设置为-1有什么作用?
MonkeyBonkey

@ MonkeyBonkeydocs.mongodb.org/ meta- driver/ latest/ legacy/… “如果numberToReturn为0,则数据库将使用默认的返回大小。如果数字为负,则数据库将返回该数字并关闭光标。 ”
ceejayoz

86

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到索引,然后再次添加即可。


7
这是将随机字段添加到集合中每个文档的简单方法。函数setRandom(){db.topics.find()。forEach(function(obj){obj.random = Math.random(); db.topics.save(obj);}); } db.eval(setRandom);
杰弗里(Geoffrey)

8
这将随机选择一个文档,但是如果您多次执行该操作,则查找将不是独立的。与随机机会所决定的相比,您更有可能连续两次获得同一文档。
缺乏者2012年

12
看起来像是循环散列的错误实现。这比缺乏者说的还要糟糕:即使一次查询也有偏差,因为随机数分布不均。为了正确执行此操作,每个文档需要一组10个随机数。每个文档使用的随机数越多,输出分布就越均匀。
托马斯

4
MongoDB JIRA票证仍然有效:jira.mongodb.org/browse/SERVER-533如果需要此功能,请发表评论并投票。
David J.

1
请注意上述警告的类型。这对于少量文档不能有效地工作。给定两个具有3和63的随机密钥的项目。将首先更频繁地选择文档#63 $gte。在这种情况下,替代解决方案stackoverflow.com/a/9499484/79201会更好。
瑞安·舒马赫

56

您还可以使用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轴为查询添加第二个随机维度。


8
我喜欢这个答案,这是我见过的最高效的答案,不需要一堆服务器端的麻烦。
Tony Million

4
这也偏向于附近几乎没有点的文档。
Thomas

6
没错,还有其他问题:文档的随机键高度相关,因此,如果选择多个文档,则可以高度预测哪些文档将作为一组返回。另外,不太可能选择接近边界(0和1)的文档。后者可以通过使用球形地理映射解决,该球形几何映射在边缘环绕。但是,您应该将此答案视为菜谱食谱的改进版本,而不是理想的随机选择机制。对于大多数目的来说,它是足够随机的。
Nico de Poel 2012年

@NicodePoel,我喜欢您的回答以及您的评论!我有几个问题供您参考:1-您如何知道不太可能选择接近边界0和1的点,是基于某种数学基础吗?2-您能否详细说明球形几何映射,如何改善随机选择,以及如何在MongoDB中进行呢?……感激!
securecurve

丰富您的想法。最后,我有一个很棒的代码,对CPU和RAM非常友好!谢谢
Qais Bsharat

21

以下食谱比mongo cookbook解决方案要慢一些(在每个文档上添加一个随机密钥),但是返回的是分布更均匀的随机文档。与skip( random )解决方案相比,它的分布不均,但是在删除文档的情况下,它更快,更安全。

function draw(collection, query) {
    // query: mongodb query object (optional)
    var query = query || { };
    query['random'] = { $lte: Math.random() };
    var cur = collection.find(query).sort({ rand: -1 });
    if (! cur.hasNext()) {
        delete query.random;
        cur = collection.find(query).sort({ rand: -1 });
    }
    var doc = cur.next();
    doc.random = Math.random();
    collection.update({ _id: doc._id }, doc);
    return doc;
}

它还需要您在文档中添加一个随机的“随机”字段,因此创建它们时请不要忘记添加此字段:您可能需要初始化集合,如Geoffrey所示

function addRandom(collection) { 
    collection.find().forEach(function (obj) {
        obj.random = Math.random();
        collection.save(obj);
    }); 
} 
db.eval(addRandom, db.things);

基准结果

此方法比skip()(ceejayoz)方法快得多,并且比Michael报告的“ cookbook”方法生成更均匀的随机文档:

对于具有1,000,000个元素的集合:

  • 这种方法在我的机器上花费不到一毫秒

  • skip()方法平均需要180毫秒

该Cookbook方法将导致大量文档永远不会被拾取,因为它们的随机数不利于它们。

  • 该方法将随着时间的推移均匀地选择所有元素。

  • 在我的基准测试中,它仅比Cookbook方法慢30%。

  • 随机性不是100%完美,但非常好(如有必要,可以改善)

这个食谱并不完美-完美的解决方案将是其他人所指出的内置功能。
但是,对于许多目的,这应该是一个很好的折衷方案。


10

这是使用的默认ObjectId_id以及一些数学和逻辑的方法。

// Get the "min" and "max" timestamp values from the _id in the collection and the 
// diff between.
// 4-bytes from a hex string is 8 characters

var min = parseInt(db.collection.find()
        .sort({ "_id": 1 }).limit(1).toArray()[0]._id.str.substr(0,8),16)*1000,
    max = parseInt(db.collection.find()
        .sort({ "_id": -1 })limit(1).toArray()[0]._id.str.substr(0,8),16)*1000,
    diff = max - min;

// Get a random value from diff and divide/multiply be 1000 for The "_id" precision:
var random = Math.floor(Math.floor(Math.random(diff)*diff)/1000)*1000;

// Use "random" in the range and pad the hex string to a valid ObjectId
var _id = new ObjectId(((min + random)/1000).toString(16) + "0000000000000000")

// Then query for the single document:
var randomDoc = db.collection.find({ "_id": { "$gte": _id } })
   .sort({ "_id": 1 }).limit(1).toArray()[0];

这是外壳表示中的通用逻辑,并且易于适应。

所以要点:

  • 在集合中找到最小和最大主键值

  • 生成一个随机数,介于这些文档的时间戳之间。

  • 将随机数加到最小值,然后找到大于或等于该值的第一个文档。

这将使用“十六进制”中时间戳值中的“填充”来形成有效值,ObjectId因为这正是我们要寻找的。使用整数作为_id值本质上比较简单,但要点相同。


我有3亿条线的集合。这是唯一可行的解​​决方案,而且速度足够快。
Nikos,

8

在使用pymongo的Python中:

import random

def get_random_doc():
    count = collection.count()
    return collection.find()[random.randrange(count)]

5
值得一提的是,内部将使用跳过和限制,就像其他许多答案一样。
JohnnyHK 2015年

您的回答是正确的。但是,请更换count()estimated_document_count()count()在Mongdo V4.2已经过时了。
user3848207


6

如果没有数据可以取消,这将非常困难。_id字段是什么?他们是mongodb对象ID的吗?如果是这样,您可以获得最高和最低值:

lowest = db.coll.find().sort({_id:1}).limit(1).next()._id;
highest = db.coll.find().sort({_id:-1}).limit(1).next()._id;

那么,如果您假设ID是均匀分布的(但不是,但至少是一个开始):

unsigned long long L = first_8_bytes_of(lowest)
unsigned long long H = first_8_bytes_of(highest)

V = (H - L) * random_from_0_to_1();
N = L + V;
oid = N concat random_4_bytes();

randomobj = db.coll.find({_id:{$gte:oid}}).limit(1);

1
有什么想法在PHP中会是什么样子?或至少您在上面使用了哪种语言?是Python吗?
Marcin

6

使用Python(pymongo),聚合函数也可以使用。

collection.aggregate([{'$sample': {'size': sample_size }}])

这种方法比运行查询随机数(例如collection.find([random_int])的速度快得多。对于大型集合,尤其如此。


5

您可以选择随机时间戳记,然后搜索之后创建的第一个对象。尽管它不一定能为您提供统一的分发,但它只会扫描单个文档。

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

可以很容易地使随机日期偏斜以说明超线性数据库的增长。
马丁·诺瓦克

这是一个非常大的集合最好的方法,它工作在其他解决方案中使用O(1),unline跳过()或计数()在这里
MARMOR

4

我在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;
}

3

为了获得确定数量的无重复文档,请执行以下操作:

  1. 首先获得所有ID
  2. 获取文件大小
  3. 循环获取随机索引并跳过重复

    number_of_docs=7
    db.collection('preguntas').find({},{_id:1}).toArray(function(err, arr) {
    count=arr.length
    idsram=[]
    rans=[]
    while(number_of_docs!=0){
        var R = Math.floor(Math.random() * count);
        if (rans.indexOf(R) > -1) {
         continue
          } else {           
                   ans.push(R)
                   idsram.push(arr[R]._id)
                   number_of_docs--
                    }
        }
    db.collection('preguntas').find({}).toArray(function(err1, doc1) {
                    if (err1) { console.log(err1); return;  }
                   res.send(doc1)
                });
            });

2

我建议使用map / reduce,在其中使用map函数仅在随机值高于给定概率时才发出。

function mapf() {
    if(Math.random() <= probability) {
    emit(1, this);
    }
}

function reducef(key,values) {
    return {"documents": values};
}

res = db.questions.mapReduce(mapf, reducef, {"out": {"inline": 1}, "scope": { "probability": 0.5}});
printjson(res.results);

上述reducef函数之所以有效,是因为map函数仅发出一个键('1')。

调用mapRreduce(...)时,在“范围”中定义“概率”的值

像这样使用mapReduce也可以在分片数据库上使用。

如果要从数据库中准确选择n个文档,则可以这样操作:

function mapf() {
    if(countSubset == 0) return;
    var prob = countSubset / countTotal;
    if(Math.random() <= prob) {
        emit(1, {"documents": [this]}); 
        countSubset--;
    }
    countTotal--;
}

function reducef(key,values) {
    var newArray = new Array();
for(var i=0; i < values.length; i++) {
    newArray = newArray.concat(values[i].documents);
}

return {"documents": newArray};
}

res = db.questions.mapReduce(mapf, reducef, {"out": {"inline": 1}, "scope": {"countTotal": 4, "countSubset": 2}})
printjson(res.results);

其中“ countTotal”(m)是数据库中的文档数,“ countSubset”(n)是要检索的文档数。

这种方法可能会对分片数据库产生一些问题。


4
进行完整的集合扫描以返回1个元素...这必须是效率最低的技术。
Thomas

1
诀窍是,这是返回任意数量的随机元素的通用解决方案-在这种情况下,当获得> 2个随机元素时,它会比其他解决方案更快。
torbenl 2014年

2

您可以选择随机的_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)
            })
        })
    })

在这里,您不需要花时间在集合中存储随机数。


1

我建议向每个对象添加一个随机的int字段。然后你可以做一个

findOne({random_field: {$gte: rand()}}) 

选择一个随机文档。只要确保您确保Index({random_field:1})


2
如果您集合中的第一条记录具有较高的random_field值,那么几乎不会一直返回它吗?
thehiatus

2
thehaitus是正确的,它将-不适合任何目的
Heptic

7
这个解决方案是完全错误的,添加一个随机数(假设在0到2 ^ 32-1之间)不能保证任何良好的分布,并且使用$ gte会使情况更糟,因为您的随机选择不会很接近伪随机数。我建议永远不要使用这个概念。
Maximiliano Rios 2013年

1

当我遇到类似的解决方案时,我回溯并发现业务请求实际上是为了对要显示的库存进行某种形式的轮换。在这种情况下,有更好的选择,可以从诸如Solr之类的搜索引擎获得答案,而不能从诸如MongoDB之类的数据存储中获得答案。

简而言之,由于要求“智能旋转”内容,我们应该做的是代替个人q得分修饰符,而不是对所有文档使用随机数。为了自己实现这一点,假设用户数量很少,您可以为每个用户存储一个文档,该文档具有productId,展示次数,点击次数,上次查看日期以及企业发现对计算aq分数有意义的任何其他因素修饰符。检索要显示的集合时,通常您从数据存储中请求的文档要比最终用户请求的要多,然后应用q得分修饰符,获取最终用户请求的记录数,然后将结果页面随机化设置,因此只需对应用程序层(在内存中)中的文档进行排序。

如果用户范围太大,则可以将用户分类为行为组,然后按行为组而不是用户进行索引。

如果产品范围足够小,则可以为每个用户创建一个索引。

我发现该技术效率更高,但更重要的是,在创建使用软件解决方案的相关有价值的经验方面,效率更高。


1

没有一种解决方案对我来说效果很好。尤其是当间隙很多且设置很小时。这对我来说很好(在php中):

$count = $collection->count($search);
$skip = mt_rand(0, $count - 1);
$result = $collection->find($search)->skip($skip)->limit(1)->getNext();

您指定语言,但未指定正在使用的库?
本杰明

仅供参考,如果在第一行和第三行之间删除了文档,则会出现争用情况。另外find+ skip很不好,您返回所有文档只是为了选择一个:S。
Martin Konecny 2014年


1

我的PHP / MongoDB按RANDOM解决方案排序/排序。希望这对任何人有帮助。

注意:我的MongoDB集合中有数字ID,用于引用MySQL数据库记录。

首先,我创建一个包含10个随机生成数字的数组

    $randomNumbers = [];
    for($i = 0; $i < 10; $i++){
        $randomNumbers[] = rand(0,1000);
    }

在我的聚合中,我将$ addField管道运算符与$ arrayElemAt和$ mod(模数)结合使用。模运算符会给我一个0到9之间的数字,然后我用它从具有随机生成数字的数组中选择一个数字。

    $aggregate[] = [
        '$addFields' => [
            'random_sort' => [ '$arrayElemAt' => [ $randomNumbers, [ '$mod' => [ '$my_numeric_mysql_id', 10 ] ] ] ],
        ],
    ];

之后,您可以使用排序管道。

    $aggregate[] = [
        '$sort' => [
            'random_sort' => 1
        ]
    ];

0

如果您有一个简单的ID密钥,则可以将所有ID存储在一个数组中,然后选择一个随机ID。(Ruby答案):

ids = @coll.find({},fields:{_id:1}).to_a
@coll.find(ids.sample).first

0

使用Map / Reduce,您当然可以得到一个随机记录,但不一定非常有效,这取决于最终使用的结果过滤后的集合的大小。

我已经用50,000个文档测试了此方法(过滤器将其减少到大约30,000个),并且在具有16GB内存和SATA3 HDD的Intel i3上,它可以在大约400毫秒内执行...

db.toc_content.mapReduce(
    /* map function */
    function() { emit( 1, this._id ); },

    /* reduce function */
    function(k,v) {
        var r = Math.floor((Math.random()*v.length));
        return v[r];
    },

    /* options */
    {
        out: { inline: 1 },
        /* Filter the collection to "A"ctive documents */
        query: { status: "A" }
    }
);

Map函数只是创建一个与查询匹配的所有文档的ID的数组。就我而言,我用50,000个可能的文档中的大约30,000个进行了测试。

Reduce函数仅选择一个介于0和数组中项数(-1)之间的随机整数,然后从数组中返回该_id

400ms听起来很长一段时间,实际上,如果您有五千万条记录而不是五万条记录,这可能会将开销增加到在多用户情况下变得无法使用的程度。

MongoDB在核心中包含此功能存在一个未解决的问题... https://jira.mongodb.org/browse/SERVER-533

如果将此“随机”选择内置到索引查找中,而不是将id收集到一个数组中然后选择一个,那么这将非常有用。(去投票吧!)


0

这样效果很好,速度很快,可以处理多个文档,并且不需要填充rand字段,字段最终将自己填充:

  1. 将索引添加到集合中的.rand字段
  2. 使用查找和刷新,例如:
// Install packages:
//   npm install mongodb async
// Add index in mongo:
//   db.ensureIndex('mycollection', { rand: 1 })

var mongodb = require('mongodb')
var async = require('async')

// Find n random documents by using "rand" field.
function findAndRefreshRand (collection, n, fields, done) {
  var result = []
  var rand = Math.random()

  // Append documents to the result based on criteria and options, if options.limit is 0 skip the call.
  var appender = function (criteria, options, done) {
    return function (done) {
      if (options.limit > 0) {
        collection.find(criteria, fields, options).toArray(
          function (err, docs) {
            if (!err && Array.isArray(docs)) {
              Array.prototype.push.apply(result, docs)
            }
            done(err)
          }
        )
      } else {
        async.nextTick(done)
      }
    }
  }

  async.series([

    // Fetch docs with unitialized .rand.
    // NOTE: You can comment out this step if all docs have initialized .rand = Math.random()
    appender({ rand: { $exists: false } }, { limit: n - result.length }),

    // Fetch on one side of random number.
    appender({ rand: { $gte: rand } }, { sort: { rand: 1 }, limit: n - result.length }),

    // Continue fetch on the other side.
    appender({ rand: { $lt: rand } }, { sort: { rand: -1 }, limit: n - result.length }),

    // Refresh fetched docs, if any.
    function (done) {
      if (result.length > 0) {
        var batch = collection.initializeUnorderedBulkOp({ w: 0 })
        for (var i = 0; i < result.length; ++i) {
          batch.find({ _id: result[i]._id }).updateOne({ rand: Math.random() })
        }
        batch.execute(done)
      } else {
        async.nextTick(done)
      }
    }

  ], function (err) {
    done(err, result)
  })
}

// Example usage
mongodb.MongoClient.connect('mongodb://localhost:27017/core-development', function (err, db) {
  if (!err) {
    findAndRefreshRand(db.collection('profiles'), 1024, { _id: true, rand: true }, function (err, result) {
      if (!err) {
        console.log(result)
      } else {
        console.error(err)
      }
      db.close()
    })
  } else {
    console.error(err)
  }
})

ps。如何在mongodb问题中查找随机记录被标记为该问题的重复项。所不同的是,这个问题是问关于明确单个记录作为另一个明确有关获取随机文件小号


-2

如果使用的是文档到对象包装器mongoid,则可以在Ruby中执行以下操作。(假设您的模型是用户)

User.all.to_a[rand(User.count)]

在我的.irbrc中,我有

def rando klass
    klass.all.to_a[rand(klass.count)]
end

因此在Rails控制台中,我可以执行例如

rando User
rando Article

从任何集合中随机获取文档。


1
这是非常低效的,因为它将整个集合读入一个数组,然后选择一个记录。
JohnnyHK

好的,也许效率不高,但是一定很方便。如果您的数据量不太大,请尝试此操作
Zack Xu

3
可以,但是最初的问题是要收集1亿个文档,因此对于这种情况而言,这将是一个非常糟糕的解决方案!
JohnnyHK 2013年

-2

您还可以在执行查询后使用shuffle-array

var shuffle = require('shuffle-array');

Accounts.find(qry,function(err,results_array){newIndexArr = shuffle(results_array);


-7

有效而可靠地工作的是:

向每个文档添加一个名为“ random”的字段,并为其分配一个随机值,为该随机字段添加一个索引,然后按以下步骤操作:

假设我们有一个称为“ links”的Web链接集合,并且我们想要一个随机链接:

link = db.links.find().sort({random: 1}).limit(1)[0]

为确保同一链接不会再次弹出,请使用新的随机数更新其随机字段:

db.links.update({random: Math.random()}, link)

2
为什么只能选择其他随机密钥来更新数据库?
杰森S

您可能没有要从中随机选择的键的列表。
麦克,

因此,您每次都必须对整个集合进行排序?那些拥有大量随机数的不幸记录呢?他们将永远不会被选中。
Fantius 2012年

1
您必须执行此操作,因为其他解决方案(特别是MongoDB书中建议的解决方案)不起作用。如果第一个查找失败,则第二个查找始终返回具有最小随机值的项目。如果您按随机降序索引,则第一个查询始终返回具有最大随机数的项目。
失事

在每个文档中添加一个字段?我认为这是不明智的。
CS_noob
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.