鉴于没有数字索引,从Meteor集合中获取随机文档的最有效算法是什么?
(还有一个问题涉及使用该skip
方法在MongoDB中这样做,但Meteor似乎并不支持这个问题).
我提出的低效方法是选择所有记录并迭代到一个随机数,但随着集合规模的增长,这显然变得昂贵和繁琐.
使用下划线,下面对我有用:
function(){ var random = _.sample(Collection.find().fetch()); return Collection.find({_id: random && random._id}); }
有同样的问题,但我需要从查询结果中获取一个随机元素.我找到了一个解决方案,感谢这个问题提到了fetch(): Meteor:通过_id从集合中查找对象
您可以使用此方法将查询转换为数组.因此,将查询结果转换为数组将是Collection.find().fetch()
.然后,您可以简单地获取此数组的长度并使用它来生成随机数,并选择该数组的元素.
var array = Collection.find().fetch(); var randomIndex = Math.floor( Math.random() * array.length ); var element = array[randomIndex];
注意:这适用于Meteor,而不是普通的MongoDB!对于MongoDB,请参阅其他答案或使用skip()的链接问题.
目前,MongoDB查询语言没有随机运算符(尽管有一个开放的功能请求票证).
更新版本3.2:您现在可以使用$sample
聚合运算符来获取随机样本.
collection.aggregate( [ { $sample: { size: 1 } } ] )
如果你不能或不想使用它,有一些解决方法,但它们并不漂亮.
一种是用于db.collection.count()
获取集合中的文档数量.然后您可以选择n
-th文档db.collection.find().skip(n).limit(1)
.但是当集合很大时,这可能需要一段时间,因为整个集合需要使用游标进行迭代.
另一种方法是rand
在插入每个文档时为每个文档添加一个介于0.0和1.0之间的随机浮点数字段.然后你可以生成另一个随机数,r
并做db.collection.find({rand:{$gt:r}}).sort({rand:1}).limit(1)
下一个更大的随机数.当你在该rand
字段上有索引时,这将非常快.但随机性不会均匀分布,因为那些恰好在它们与其前身之间存在较大差距的文档将被更频繁地选取.此外,当r
碰巧大于集合中的最大值时,将不会返回任何结果.在这种情况下,您应该尝试使用相同的数字,但这次使用rand:{$lte:r}
和sort({rand:-1})
.如果这也没有返回文档,则该集合为空(或者至少没有包含rand
字段的文档).
您可以快速公平地选择随机文档的唯一角落情况是您的收藏不会更改(或者至少不会经常更改).在这种情况下,您可以使用从0开始的连续整数,索引该字段以及find()
介于0和您已知数量的文档之间的随机数对所有文档进行编号.