ChatGPT解决这个技术问题 Extra ChatGPT

来自 MongoDB 的随机记录

我希望从庞大的集合(1 亿条记录)中获取随机记录。

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

数据已经存在,并且没有我可以在其中生成随机数并获得随机行的字段。

另请参阅此SO question titled "Ordering a result set randomly in mongo"。考虑随机排序结果集是这个问题的一个更通用的版本——更强大、更有用。
这个问题不断出现。最新信息可能在 MongoDB 票证跟踪器的 feature request to get random items from a collection 中找到。如果本机实施,它可能是最有效的选择。 (如果你想要这个功能,去投票吧。)
这是一个分片集合吗?
有谁知道这比只记录第一条记录要慢多少?我正在争论是否值得随机抽样做某事而不是按顺序做。
实际上与答案相反 $sample 可能不是最快的解决方案。因为 mongo 在使用 $sample 时可能会根据情况进行随机排序的集合扫描。请参阅:参考:docs.mongodb.com/manual/reference/operator/aggregation/sample也许做计数结果集和做一些随机跳过会做得更好。

J
JohnnyHK

从 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 时,返回的文档样本中可能存在重复项。


这是一个好方法,但请记住,它不能保证样本中没有相同对象的副本。
@MatheusAraujo,如果您想要一张唱片,这无关紧要,但无论如何都是好点
不要迂腐,但问题没有指定 MongoDB 版本,所以我认为拥有最新版本是合理的。
@Nepoxx 有关所涉及的处理,请参阅 the docs
@brycejl 如果 $sample 阶段没有选择任何匹配的文档,那将具有不匹配任何内容的致命缺陷。
a
abraham

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

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

不幸的是,skip() 效率很低,因为它必须扫描那么多文档。此外,如果在获取计数和运行查询之间删除行,则存在竞争条件。
请注意,随机数应介于 0 和计数(不包括)之间。即,如果您有 10 项,则随机数应介于 0 和 9 之间。否则光标可能会尝试跳过最后一项,并且不会返回任何内容。
谢谢,非常适合我的目的。 @mstearn,您对效率和竞争条件的评论都是有效的,但是对于两者都不重要的集合(在不删除记录的集合中一次性服务器端批量提取),这远远优于hacky(IMO) Mongo Cookbook 中的解决方案。
将限制设置为 -1 有什么作用?
@MonkeyBonkey docs.mongodb.org/meta-driver/latest/legacy/… "如果 numberToReturn 为 0,则数据库将使用默认返回大小。如果数字为负数,则数据库将返回该数字并关闭游标。"
G
Giacomo1968

MongoDB 3.2 更新

3.2 将 $sample 引入聚合管道。

还有一个很好的blog post将其付诸实践。

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

这实际上是一个功能请求: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 附加到它,然后再次添加它。


这是一种将随机字段添加到集合中每个文档的简单方法。函数 setRandom() { db.topics.find().forEach(函数 (obj) {obj.random = Math.random();db.topics.save(obj);}); } db.eval(setRandom);
这会随机选择一个文档,但如果您多次执行此操作,则查找不是独立的。与随机机会相比,您更有可能连续两次获得相同的文档。
看起来像是循环散列的糟糕实现。它甚至比缺乏者说的更糟糕:即使是一次查找也是有偏差的,因为随机数不是均匀分布的。要正确执行此操作,您需要一组,例如,每个文档 10 个随机数。每个文档使用的随机数越多,输出分布就越均匀。
MongoDB JIRA 票仍然有效:jira.mongodb.org/browse/SERVER-533 如果您想要该功能,请发表评论并投票。
请注意所提到的警告类型。这不适用于少量文档。给定两个随机键为 3 和 63 的项目。将更频繁地选择文档 #63,其中 $gte 是第一个。在这种情况下,替代解决方案 stackoverflow.com/a/9499484/79201 会更好。
N
Nico de Poel

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


我喜欢这个答案,它是我见过的最有效的答案,不需要一堆乱七八糟的服务器端。
这也偏向于在其附近恰好有几个点的文档。
确实如此,还有其他问题:文档在其随机键上高度相关,因此如果您选择多个文档,则可以高度预测哪些文档将作为一个组返回。此外,接近边界(0 和 1)的文档不太可能被选中。后者可以通过使用环绕在边缘的球形地理映射来解决。但是,您应该将此答案视为食谱食谱的改进版本,而不是完美的随机选择机制。对于大多数目的来说,它是随机的。
@NicodePoel,我喜欢你的回答和评论!我有几个问题要问你:1-你怎么知道接近边界 0 和 1 的点不太可能被选择,这是基于一些数学基础吗? 2- 你能详细说明球形地理映射吗?它将如何更好地随机选择,以及如何在 MongoDB 中做到这一点? ...赞赏!
Apprichate 你的想法。最后,我有一个很棒的代码,它对 CPU 和 RAM 非常友好!谢谢
c
colllin

以下配方比 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);

基准测试结果

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

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

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

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

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

此方法将随着时间的推移均匀地选取所有元素。

在我的基准测试中,它只比食谱方法慢 30%。

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

这个配方并不完美 - 完美的解决方案将是其他人指出的内置功能。然而,对于许多目的来说,它应该是一个很好的折衷方案。


B
Blakes Seven

这是一种使用 _id 的默认 ObjectId 值以及一些数学和逻辑的方法。

// 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];

这是 shell 表示的一般逻辑,并且很容易适应。

所以在点:

查找集合中的最小和最大主键值

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

将随机数添加到最小值并找到大于或等于该值的第一个文档。

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


我收集了 300 000 000 行。这是唯一有效的解决方案,而且速度足够快。
d
dbam

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

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

See the doc


注意:$sample 可能会多次获取同一个文档
J
Jabba

在 Python 中使用 pymongo:

import random

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

值得注意的是,在内部,这将使用跳过和限制,就像许多其他答案一样。
你的答案是正确的。但是,请将 count() 替换为 estimated_document_count(),因为 count() 在 Mongdo v4.2 中已弃用。
D
Daniel

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

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

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


d
dm.

如果没有数据可以关闭,那就很难了。 _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);

任何想法在 PHP 中会是什么样子?或者至少你在上面使用了什么语言?是 Python 吗?
M
Martin Nowak

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

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 skip() 或 count()
c
code_turist

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

a
anonymous255

为了获得确定数量的没有重复的随机文档:

首先获取所有 id 获取文档的大小 循环获取随机索引并跳过重复的 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) }); });


t
torbenl

我建议使用 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 也应该可以在分片数据库上使用。

如果您想从数据库中准确地选择 m 个文档中的 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)是要检索的文档数。

这种方法可能会给分片数据库带来一些问题。


进行完整的集合扫描以返回 1 个元素……这一定是效率最低的技术。
诀窍是,它是返回任意数量的随机元素的通用解决方案 - 在这种情况下,当获得 > 2 个随机元素时,它会比其他解决方案更快。
V
Vijay13

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

在这里,您不需要花费空间来存储集合中的随机数。


A
Anup Panwar

以下聚合操作从集合中随机选择 3 个文档:

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

https://docs.mongodb.com/manual/reference/operator/aggregation/sample/


P
Polv

MongoDB 现在有 $rand

要选择 n 个不重复的项目,请先用 { $addFields: { _f: { $rand: {} } } } 聚合,然后用 $sort_f$limit n 聚合。


有什么例子吗?
o
om-nom-nom

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

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

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


如果你的集合中的第一条记录有一个比较高的random_field值,那它不会几乎一直被返回吗?
thehaitus 是正确的,它会的——它不适合任何目的
这个解决方案是完全错误的,添加一个随机数(假设在 0 到 2^32-1 之间)并不能保证任何良好的分布,使用 $gte 会使情况变得更糟,因为你的随机选择不会接近为一个伪随机数。我建议永远不要使用这个概念。
p
paegun

当我遇到类似的解决方案时,我回溯并发现业务请求实际上是为了创建某种形式的库存轮换。在这种情况下,有更好的选择,它们有来自 Solr 等搜索引擎的答案,而不是 MongoDB 等数据存储。

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

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

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

我发现这种技术效率更高,但更重要的是更有效地创造了使用该软件解决方案的相关、有价值的体验。


M
Mantas Karanauskas

没有一种解决方案对我很有效。特别是当有很多间隙并且设置很小时。这对我来说非常有效(在 php 中):

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

您指定了语言,但没有指定您正在使用的库?
仅供参考,如果在第一行和第三行之间删除了文档,则此处存在竞争条件。 find + skip 也很糟糕,您返回所有文档只是为了选择一个:S。
find() 应该只返回一个游标,因此它不会返回所有实际文档。但是,是的,这种妥协在我的测试中损失了 x 1000000 倍的性能;)
f
feskr

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

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

首先,我创建一个包含 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
        ]
    ];

T
TG___

Mongoose 中最好的方法是使用 $sample 进行聚合调用。但是,Mongoose 不会将 Mongoose 文档应用于聚合 - 特别是如果要应用 populate() 则更是如此。

从数据库中获取“精益”数组:

/*
Sample model should be init first
const Sample = mongoose …
*/

const samples = await Sample.aggregate([
  { $match: {} },
  { $sample: { size: 33 } },
]).exec();
console.log(samples); //a lean Array

获取一组 mongoose 文档:

const samples = (
  await Sample.aggregate([
    { $match: {} },
    { $sample: { size: 27 } },
    { $project: { _id: 1 } },
  ]).exec()
).map(v => v._id);

const mongooseSamples = await Sample.find({ _id: { $in: samples } });

console.log(mongooseSamples); //an Array of mongoose documents

如何只带某些字段而不带整个记录?
... { $project: { keyYes: 1, keyNo: 0 } } ...
M
Mr. Demetrius Michael

如果您有一个简单的 id 键,则可以将所有 id 存储在一个数组中,然后选择一个随机 id。 (红宝石回答):

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

d
doublehelix

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

我已经用 50,000 个文档(过滤器将其减少到大约 30,000 个)测试了这种方法,它在具有 16GB 内存和 SATA3 硬盘的 Intel i3 上执行大约 400ms...

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 听起来很长,而且确实如此,如果您有 5000 万条记录而不是 50000 万条记录,这可能会增加开销,使其在多用户情况下变得无法使用。

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

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


C
Community

这很好用,速度很快,适用于多个文档并且不需要填充 rand 字段,该字段最终会自行填充:

使用查找和刷新将索引添加到集合中的 .rand 字段,例如:

// 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)
  }
})

附言。 How to find random records in mongodb 个问题被标记为与该问题重复。不同之处在于,这个问题明确询问单个记录,而另一个明确询问获取随机文档s


D
Dennis Kiprotich

对我来说,我想以随机顺序获得相同的记录,所以我创建了一个用于排序的空数组,然后生成 1 到 7 之间的随机数(我有 7 个字段)。所以每次我得到不同的值时,我都会分配一个不同的随机排序。这是“外行”,但对我有用。

//generate random number
const randomval = some random value;
//declare sort array and initialize to empty

const sort = [];

//write a conditional if else to get to decide which sort to use

if(randomval == 1)
{


sort.push(...['createdAt',1]);

}

else if(randomval == 2)

{
   sort.push(...['_id',1]);
}

....
else if(randomval == n)
{
   sort.push(...['n',1]);
}

Z
Zack Xu

如果您使用的是文档到对象包装器 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 亿个文档的集合,所以对于这种情况,这将是一个非常糟糕的解决方案!
r
rabie jegham

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

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

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


t
trainwreck

有效和可靠的工作是这样的:

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

假设我们有一个名为“links”的网络链接集合,我们希望从中获得一个随机链接:

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

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

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

当您可以选择不同的随机密钥时,为什么要更新数据库?
您可能没有要从中随机选择的键列表。
所以你每次都必须对整个集合进行排序?那么得到大随机数的倒霉记录呢?他们永远不会被选中。
您必须这样做,因为其他解决方案,尤其是 MongoDB 书中建议的解决方案,不起作用。如果第一次查找失败,第二次查找总是返回具有最小随机值的项。如果您按降序排列随机索引,则第一个查询始终返回具有最大随机数的项目。
在每个文档中添加一个字段?我认为这是不可取的。