我想要来自 mongoDB 集合的单个随机文档。现在我的 mongoDB 集合包含超过 10 亿个集合。如何从该集合中获取单个随机文档?
5 回答
我从未在 Python 中使用过 MongoDB,但是对于您的问题有一个通用的解决方案。这是一个用于获取单个随机文档的 MongoDB shell 脚本:
N = db.collection.count(condition)
db.collection.find(condition).limit(1).skip(Math.floor(Math.random()*N))
condition
这是一个 MongoDB 查询。如果要查询整个集合,请使用query = null
.
这是一个通用解决方案,因此它适用于任何 MongoDB 驱动程序。
更新
我运行了一个基准测试来测试几个实现。首先,我创建了包含 5567249 个带有索引随机字段的文档的测试集合rnd
。
我选择了三种方法相互比较:
第一种方法:
db.collection.find().limit(1).skip(Math.floor(Math.random()*N))
第二种方法:
db.collection.find({rnd: {$gte: Math.random()}}).sort({rnd:1}).limit(1)
第三种方法:
db.collection.findOne({rnd: {$gte: Math.random()}})
我将每种方法运行了 10 次,得到了它的平均计算时间:
method 1: 882.1 msec
method 2: 1.2 msec
method 3: 0.6 msec
这个基准表明我的解决方案不是最快的。
但是第三种解决方案也不是一个好的解决方案,因为它找到了数据库中的第一个元素(按自然顺序排序)rnd > random()
。因此,它的输出并不是真正随机的。
我认为第二种方法是经常使用的最佳方法。但它有一个缺陷:它需要更改整个数据库并确保附加索引。
为您的集合添加一个名为的附加列random
,并使其中的值介于 0 到 1 之间。您可以通过 为每条记录分配 0 到 1 之间的随机浮点到此列中[random.random() for _ in range(0, 10)]
。
然后:-
import random
collection = mongodb["collection_name"]
rand = random.random() # rand will be a floating point between 0 to 1.
random_record = collection.find_one({ 'random' => { '$gte' => rand } })
MongoDB 将在适当的时候有它的本地实现。在此处提交功能 - https://jira.mongodb.org/browse/SERVER-533
在撰写本文时尚未实施。
由于MongoDB 3.2
,它可以使用aggregate
带有$sample
运算符的函数来完成,如docs中所述。它超级快。以下代码将从集合中随机选择 20 个文档。
db.collection.aggregate( [ { $sample: {size: 20} } ] )
如果您需要选择具有特定标准的随机文档,您可以将其与$match
操作员一起使用
db.collection.aggregate([
{ $sample: {size: 20} },
{ $match:{"yourField": value} }
])
当心订单!如果我在我的小型数据库中搜索大约 100k 个文档,上面的这个命令需要 15 毫秒,而当你切换顺序时,它是 1750 毫秒(慢了 100 倍以上)。原因当然很明显。此外,通过这个顺序,您可以获得那些随机 20 个文档的子集......
以高效的方式?至少可以说,如果不更改您的数据,这很难。
想象一下,您尝试从 1b 文档中获取 1,000,000 的 rand()。那会很慢,非常慢。这是因为 MongoDB 在跳过时没有有效利用索引。
正如@Calvin 所说,MongoDB 有一个获取随机文档的功能请求,但尚未实现。
如果您要定期执行此操作,atm 执行此操作的最高效方法是在您的记录中添加一个自动递增的 id:http ://www.mongodb.org/display/DOCS/How+to+Make+an+ Auto+Incrementing+Field并使用它来rand()
打开。
编辑
澄清; 使用自动递增 id 时,您最初需要执行一个查询(除非您以另一种方式跟踪它)以获得该字段的最高值。您可以查询计数器集合或集合本身并反向排序 ( sort({field:-1})
) 并limit(1)
获得 的最大值rand()
。
您还需要考虑数据的变化,这意味着您实际上想要$gte
那个随机位置。
我的想法可以在这里得到更多解释:php mongodb find nth entry in collection
如果您的对象上有 int id,您可以执行类似的操作
findOne({id: {$gte: rand()}})