user984621
user984621

Reputation: 48483

Rails - how to fetch random records from an object?

I am doing something like this:

data = Model.where('something="something"')
random_data = data.rand(100..200)

returns:

NoMethodError (private method `rand' called for #<User::ActiveRecord_Relation:0x007fbab27d7ea8>):

Once I get this random data, I need to iterate through that data, like this:

random_data.each do |rd|
  ...

I know there's a way to fetch random data in MySQL, but I need to pick the random data like 400 times, so I think to load data once from database and 400 times to pick random number is more efficient than to run the query 400 times on MySQL.

But - how to get rid of that error?

NoMethodError (private method `rand' called for #<User::ActiveRecord_Relation:0x007fbab27d7ea8>):

Thank you in advance

Upvotes: 0

Views: 380

Answers (4)

Ahmad Al-kheat
Ahmad Al-kheat

Reputation: 1795

Use data.sample(rand(100..200)) for more info why rand is not working, read here https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4555

Upvotes: 0

spickermann
spickermann

Reputation: 106972

I would add the following scope to the model (depends on the database you are using):

# to model/model.rb
# 'RANDOM' works with postgresql and sqlite, whereas mysql uses 'RAND'
scope :random, -> { order('RAND()') } 

Then the following query would load a random number (in the range of 200-400) of objects in one query:

Model.random.limit(rand(200...400))

If you really want to do that in Rails and not in the database, then load all records and use sample:

Model.all.sample(rand(200..400))

But that to be slower (depending on the number of entries in the database), because Rails would load all records from the database and instantiate them what might take loads of memory.

Upvotes: 2

Arup Rakshit
Arup Rakshit

Reputation: 118289

Another way, which is not DB specific is :

def self.random_record
  self.where('something = ? and id = ?', "something", rand(self.count))
end

The only things here is - 2 queries are being performed. self.count is doing one query - SELECT COUNT(*) FROM models and the other is your actual query to get a random record.

Well, now suppose you want n random records. Then write it like :

def self.random_records n
  records = self.count
  rand_ids = Array.new(n) { rand(records) }
  self.where('something = ? and id IN (?)', 
             "something", rand_ids )
end

Upvotes: 0

Stanley
Stanley

Reputation: 56

It really depends how much effort you want to put into optimizing this, because there's more than one solution. Here's 2 options..

Something simple is to use ORDER BY RAND() LIMIT 400 to randomly select 400 items.

Alternatively, just select everything under the moon and then use Ruby to randomly pick 400 out of the total result set, ex:

data = Model.where(something: 'something').all # all is necessary to exec query
400.times do
  data.sample # returns a random model
end

I wouldn't recommend the second method, but it should work.

Upvotes: 0

Related Questions