What's the 'Rails 4 Way' of finding so

2019-01-21 18:58发布

User.find(:all, :order => "RANDOM()", :limit => 10) was the way I did it in Rails 3.

User.all(:order => "RANDOM()", :limit => 10) is how I thought Rails 4 would do it, but this is still giving me a Deprecation warning:

DEPRECATION WARNING: Relation#all is deprecated. If you want to eager-load a relation, you can call #load (e.g. `Post.where(published: true).load`). If you want to get an array of records from a relation, you can call #to_a (e.g. `Post.where(published: true).to_a`).

9条回答
女痞
2楼-- · 2019-01-21 19:28

While not the fastest solution, I like the brevity of:

User.ids.sample(10)

The .ids method yields an array of User IDs and .sample(10) picks 10 random values from this array.

查看更多
smile是对你的礼貌
3楼-- · 2019-01-21 19:32

I think the best solution is really ordering randomly in database. But if you need to avoid specific random function from database, you can use pluck and shuffle approach.

For one record:

User.find(User.pluck(:id).shuffle.first)

For more than one record:

User.where(id: User.pluck(:id).sample(10))
查看更多
Melony?
4楼-- · 2019-01-21 19:32

Here's a quick solution.. currently using it with over 1.5 million records and getting decent performance. The best solution would be to cache one or more random record sets, and then refresh them with a background worker at a desired interval.

Created random_records_helper.rb file:

module RandomRecordsHelper

 def random_user_ids(n)
    user_ids = []
    user_count = User.count
    n.times{user_ids << rand(1..user_count)}
    return user_ids
 end

in the controller:

@users = User.where(id: random_user_ids(10))

This is much quicker than the .order("RANDOM()").limit(10) method - I went from a 13 sec load time down to 500ms.

查看更多
疯言疯语
5楼-- · 2019-01-21 19:36

I would suggest making this a scope as you can then chain it:

class User < ActiveRecord::Base
  scope :random, -> { order(Arel::Nodes::NamedFunction.new('RANDOM', [])) }
end 

User.random.limit(10)
User.active.random.limit(10)
查看更多
▲ chillily
6楼-- · 2019-01-21 19:43

As the random function could change for different databases, I would recommend to use the following code:

User.offset(rand(User.count)).first

Of course, this is useful only if you're looking for only one record.

If you wanna get more that one, you could do something like:

User.offset(rand(User.count) - 10).limit(10)

The - 10 is to assure you get 10 records in case rand returns a number greater than count - 10.

Keep in mind you'll always get 10 consecutive records.

查看更多
闹够了就滚
7楼-- · 2019-01-21 19:45

You'll want to use the order and limit methods instead. You can get rid of the all.

For PostgreSQL and SQLite:

User.order("RANDOM()").limit(10)

Or for MySQL:

User.order("RAND()").limit(10)
查看更多
登录 后发表回答