让我们说我有一个用户集合。有没有办法使用mongoid在集合中找到n个随机用户,它不会返回同一个用户两次?现在让我们假设用户集合如下所示:
class User
include Mongoid::Document
field :name
end
简单吧?
由于
答案 0 :(得分:16)
如果您只想要一个文档,并且不想定义新的条件方法,那么您可以这样做:
random_model = Model.skip(rand(Model.count)).first
如果您想根据某些标准找到随机模型:
criteria = Model.scoped_whatever.where(conditions) # query example
random_model = criteria.skip(rand(criteria.count)).first
答案 1 :(得分:13)
最佳解决方案将取决于预期的集合大小。
对于小型集合,只需获取所有这些和.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
答案 2 :(得分:5)
MongoDB 3.2通过$sample
(link to 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
您可以用
做什么我相信功能现在应该很快就会进入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)
答案 3 :(得分:3)
如果你真的想要简单,你可以改用它:
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)并随机化该范围。
答案 4 :(得分:2)
您可以通过
执行此操作码
>> cnt = User.count
=> 10
>> n = 5
=> 5
>> offset = 0
=> 0
>> if n<cnt
>> 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)
希望这会有所帮助
答案 5 :(得分:0)
由于我想保留一个标准,我这样做:
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]])
}
答案 6 :(得分:0)
刚遇到这样的问题。试图
Model.all.sample
它对我有用
答案 7 :(得分:0)
来自@moox的方法非常有趣,但我怀疑monkeypatching整个Mongoid在这里是一个好主意。所以我的方法就是编写一个问题Randomizable
,它可以包含在您使用此功能的每个模型中。这转到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
答案 8 :(得分:-2)
我认为最好关注随机化返回的结果集,所以我尝试了:
Model.all.to_a.shuffle
希望这有帮助。