如何使用python从mongoDB中的10亿个文档中获取随机单个文档?

时间:2012-11-23 07:22:40

标签: python mongodb pymongo

我想要来自mongoDB集合的单个随机文档。现在我的mongoDB集合包含超过10亿个集合。如何从该集合中获取单个随机文档?

5 个答案:

答案 0 :(得分:21)

我从未使用过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驱动程序。


更新

我运行了一个基准来测试几个实现。首先,我使用带有索引随机字段rnd的5567249文档创建了测试集合。

我选择了三种方法进行比较:

第一种方法:

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()。所以,它的输出并不是真正随机的。

我认为第二种方法是经常使用的最佳方法。但它有一个缺陷:它需要改变整个数据库并确保额外的索引。

答案 1 :(得分:6)

将另一个名为random的列添加到您的集合中,并使其中的值介于0到1之间。您可以通过{{1}为每个记录为此列分配0到1之间的随机浮点数}。

然后: -

[random.random() for _ in range(0, 10)]

MongoDB将在适当的时候实现其原生实现。此处提交的功能 - https://jira.mongodb.org/browse/SERVER-533

撰写本文时尚未实施。

答案 2 :(得分:6)

MongoDB 3.2开始,可以使用aggregate运算符与$sample运算符完成,如docs中所述。它超级快。以下代码将从集合中随机选择20个文档。

db.collection.aggregate( [ { $sample: {size: 20} } ] )

如果您需要选择具有特定条件的随机文档,则可以将其与$match操作符一起使用

db.collection.aggregate([ 
    { $sample: {size: 20} }, 
    { $match:{"yourField": value} } 
  ])

小心订单!如果我在我的小数据库中搜索大约100k文档,上面的命令需要15ms,而当你切换顺序时,它是1750ms(慢了100多倍)。原因很明显。此外,通过此订单,您可以获得这些随机20个文档的子集...

答案 3 :(得分:2)

以高效的方式?至少可以说,在不改变数据的情况下很难。

想象一下,你尝试从1b文件中获得1,000,000兰特()。那将是缓慢的,非常缓慢的。这是因为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

答案 4 :(得分:1)

如果您的对象上有int id,则可以执行类似

的操作
findOne({id: {$gte: rand()}})