Mongoid随机文件

让我们说我有一个用户集合。 有没有办法使用mongoid在集合中找到n个随机用户,它不会返回同一个用户两次? 现在让我们假设用户集合如下所示:

class User include Mongoid::Document field :name end 

简单吧?

谢谢

最佳解决方案将取决于集合的预期大小。

对于微小的集合,只需获取所有这些和.shuffle.slice!

对于小尺寸的n,你可以逃避这样的事情:

 result = (0..User.count-1).sort_by{rand}.slice(0, n).collect! do |i| User.skip(i).first end 

对于大尺寸的n,我建议创建一个“随机”列进行排序。 详情请见: http//cookbook.mongodb.org/patterns/random-attribute/ https://github.com/mongodb/cookbook/blob/master/content/patterns/random-attribute.txt

如果您只想要一个文档,并且不想定义新的条件方法,则可以这样做:

 random_model = Model.skip(rand(Model.count)).first 

如果要根据某些条件找到随机模型:

 criteria = Model.scoped_whatever.where(conditions) # query example random_model = criteria.skip(rand(criteria.count)).first 

MongoDB 3.2带来$sample ( 链接到doc )

编辑 :最近的Mongoid实现了$ sample,所以你可以调用YourCollection.all.sample(5)

以前版本的mongoid

Mongoid在Mongoid 6之前不支持sample ,因此您必须使用Mongo驱动程序运行此聚合查询:

 samples = User.collection.aggregate([ { '$sample': { size: 3 } } ]) # call samples.to_a if you want to get the objects in memory 

你能做些什么呢

我相信function现在应该很快成为Mongoid,但与此同时

 module Utility module_function def sample(model, count) ids = model.collection.aggregate([ { '$sample': { size: count } }, # Sample from the collection { '$project': { _id: 1} } # Keep only ID fields ]).to_a.map(&:values).flatten # Some Ruby magic model.find(ids) end end Utility.sample(User, 50) 

如果你真的想要简单,你可以使用它:

 class Mongoid::Criteria def random(n = 1) indexes = (0..self.count-1).sort_by{rand}.slice(0,n).collect! if n == 1 return self.skip(indexes.first).first else return indexes.map{ |index| self.skip(index).first } end end end module Mongoid module Finders def random(n = 1) criteria.random(n) end end end 

您只需调用User.random(5)即可获得5个随机用户。 它也适用于过滤,因此如果您只想要注册用户,您可以执行User.where(:registered => true).random(5)

对于大型集合,这需要一段时间,因此我建议使用替代方法,您可以随机分配计数(例如:25 000到30 000)并随机化该范围。

你可以这样做

  1. 生成随机偏移量,进一步满足挑选下n个元素(不超过限制)
  2. 假设计数为10,n为5
  3. 要做到这一点,检查给定的n是否小于总数
  4. 如果没有将偏移量设置为0,则转到步骤8
  5. 如果是,则从总计数中减去n,您将获得数字5
  6. 用它来查找一个随机数,数字肯定会从0到5(假设2)
  7. 使用随机数2作为偏移量
  8. 现在你可以通过简单地传递这个偏移量和n(5)作为限制来获取随机的5个用户。
  9. 现在你从3到7获得用户

 >> cnt = User.count => 10 >> n = 5 => 5 >> offset = 0 => 0 >> if n> offset = rand(cnt-n) >> end >> 2 >> User.skip(offset).limit(n) 

你可以把它放在一个方法中

 def get_random_users(n) offset = 0 cnt = User.count if n < cnt offset = rand(cnt-n) end User.skip(offset).limit(n) end 

并称之为

 rand_users = get_random_users(5) 

希望这可以帮助

刚遇到这样的问题。 试着

 Model.all.sample 

它对我有用

由于我想保留一个标准,我这样做:

 scope :random, ->{ random_field_for_ordering = fields.keys.sample random_direction_to_order = %w(asc desc).sample order_by([[random_field_for_ordering, random_direction_to_order]]) } 

来自@moox的方法非常有趣,但我怀疑monkeypatching整个Mongoid在这里是一个好主意。 因此,我的方法只是编写一个关注Randomizable ,可以包含在每个模型中使用此function。 这适用于app/models/concerns/randomizeable.rb

 module Randomizable extend ActiveSupport::Concern module ClassMethods def random(n = 1) indexes = (0..count - 1).sort_by { rand }.slice(0, n).collect! return skip(indexes.first).first if n == 1 indexes.map { |index| skip(index).first } end end end 

然后您的User模型将如下所示:

 class User include Mongoid::Document include Randomizable field :name end 

和测试……

 require 'spec_helper' class RandomizableCollection include Mongoid::Document include Randomizable field :name end describe RandomizableCollection do before do RandomizableCollection.create name: 'Hans Bratwurst' RandomizableCollection.create name: 'Werner Salami' RandomizableCollection.create name: 'Susi Wienerli' end it 'returns a random document' do srand(2) expect(RandomizableCollection.random(1).name).to eq 'Werner Salami' end it 'returns an array of random documents' do srand(1) expect(RandomizableCollection.random(2).map &:name).to eq ['Susi Wienerli', 'Hans Bratwurst'] end end 

我认为最好关注随机化返回的结果集,所以我试过:

 Model.all.to_a.shuffle 

希望这可以帮助。