我想从一个巨大的集合(1亿条记录)中获得一个随机记录。
最快最有效的方法是什么?
数据已经在那里,没有字段可以生成随机数并获得随机行。
我想从一个巨大的集合(1亿条记录)中获得一个随机记录。
最快最有效的方法是什么?
数据已经在那里,没有字段可以生成随机数并获得随机行。
当前回答
有效可靠的方法是:
在每个文档中添加一个名为“random”的字段,并为其分配一个随机值,为该随机字段添加一个索引,如下所示:
让我们假设我们有一个名为“links”的网络链接集合,我们想从它中随机链接:
link = db.links.find().sort({random: 1}).limit(1)[0]
为了确保同一个链接不会第二次弹出,用一个新的随机数更新它的随机场:
db.links.update({random: Math.random()}, link)
其他回答
如果你有一个简单的id键,你可以将所有的id存储在一个数组中,然后随机选择一个id。(Ruby回答):
ids = @coll.find({},fields:{_id:1}).to_a
@coll.find(ids.sample).first
在Mongoose中最好的方法是使用$sample进行聚合调用。 然而,Mongoose并不会将Mongoose文档应用到Aggregation上——尤其是当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
我建议给每个对象添加一个随机的int字段。然后你就可以做
findOne({random_field: {$gte: rand()}})
随机选择一个文档。只要确保你ensureIndex({random_field:1})
下面的方法比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%完美的,但是它已经很好了(如果有必要的话还可以进行改进)
这个配方并不完美——正如其他人所指出的那样,完美的解决方案将是内置功能。 然而,对于许多目的来说,这应该是一个很好的折衷方案。
现在可以使用聚合了。 例子:
db.users.aggregate(
[ { $sample: { size: 3 } } ]
)
去看医生。