我想从一个巨大的集合(1亿条记录)中获得一个随机记录。
最快最有效的方法是什么?
数据已经在那里,没有字段可以生成随机数并获得随机行。
我想从一个巨大的集合(1亿条记录)中获得一个随机记录。
最快最有效的方法是什么?
数据已经在那里,没有字段可以生成随机数并获得随机行。
当前回答
现在可以使用聚合了。 例子:
db.users.aggregate(
[ { $sample: { size: 3 } } ]
)
去看医生。
其他回答
下面的方法比mongo烹饪书解决方案稍慢(在每个文档上添加一个随机键),但是返回分布更均匀的随机文档。与跳过(随机)解决方案相比,它的分布稍微不那么均匀,但在删除文档时要快得多,而且更安全。
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;
}
它还要求您添加一个随机的“random”字段到您的文档中,所以不要忘记在创建它们时添加这个:您可能需要初始化您的集合,如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个元素的集合:
这种方法在我的机器上花费的时间不到1毫秒 skip()方法平均花费180毫秒
cookbook方法将导致大量文档永远不会被选中,因为它们的随机数对它们不利。
该方法将在一段时间内均匀地挑选所有元素。 在我的基准测试中,它只比食谱方法慢了30%。 随机性并不是100%完美的,但是它已经很好了(如果有必要的话还可以进行改进)
这个配方并不完美——正如其他人所指出的那样,完美的解决方案将是内置功能。 然而,对于许多目的来说,这应该是一个很好的折衷方案。
我建议给每个对象添加一个随机的int字段。然后你就可以做
findOne({random_field: {$gte: rand()}})
随机选择一个文档。只要确保你ensureIndex({random_field:1})
使用Map/Reduce,您当然可以获得一个随机记录,只是不一定非常有效,这取决于您最终使用的过滤集合的大小。
我已经用5万个文档测试了这个方法(过滤器将其减少到大约3万个),它在Intel i3、16GB ram和SATA3 HDD上执行大约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。在我的例子中,我测试了5万个可能的文档中的大约3万个。
Reduce函数只是在数组中从0到项数(-1)之间选择一个随机整数,然后从数组中返回该_id。
400ms听起来是一段很长的时间,而且确实如此,如果您有5000万条记录而不是5万条记录,这可能会增加开销,以至于在多用户情况下无法使用。
MongoDB在核心中包含这个功能有一个悬而未决的问题…https://jira.mongodb.org/browse/SERVER-533
如果将这种“随机”选择构建到索引查找中,而不是将id收集到一个数组中然后选择一个,这将非常有帮助。(去投票吧!)
MongoDB现在有$rand
要选择n个非重复项,请使用{$addFields: {_f: {$rand:{}}}}进行聚合,然后按_f进行$sort和$limit n。
如果您使用的是mongoid(文档到对象的包装器),您可以执行以下操作 Ruby。(假设你的模型是User)
User.all.to_a[rand(User.count)]
在我的。irbrc,我有
def rando klass
klass.all.to_a[rand(klass.count)]
end
所以在rails控制台,我可以做,例如,
rando User
rando Article
从任何集合中随机获取文件。