2013-06-28 11 views
27

User.find(:all, :order => "RANDOM()", :limit => 10) è stato il modo in cui l'ho fatto in Rails 3.Qual è il 'Rails 4 Way' di trovare un certo numero di record casuali?

User.all(:order => "RANDOM()", :limit => 10) è come pensavo Rails 4 farei, ma questo è ancora mi dà un avvertimento Deprecation:

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`). 

risposta

67

Vorrete utilizzare invece i metodi order e limit. Puoi liberarti di all.

per PostgreSQL e SQLite:

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

O per MySQL:

User.order("RAND()").limit(10) 
+0

Brillante, grazie! – justindao

+12

Solo una breve nota, ho dovuto usare: 'User.order (" RAND() "). Limit (10)' come sto usando un database MySQL. – backwardm

+3

non funziona. Mi dà lo stesso set di record. – Major

22

Come la funzione random potrebbe cambiare per diversi database, mi sento di raccomandare di utilizzare il codice seguente:

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

Naturalmente, questo è utile solo se stai cercando un solo record.

Se si desidera ottenere più di uno, si potrebbe fare qualcosa di simile:

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

Il - 10 è quello di assicurare che si ottiene 10 record nel caso in cui rand restituisce un numero maggiore di conteggio - 10.

Ricorda che riceverai sempre 10 record consecutivi.

+3

+1 Non solo l'approccio 'offset' è più versatile della risposta accettata, ma è anche più veloce quando ci sono molti record. Questo perché 'ORDER BY RANDOM() LIMIT $ 1' ** ordinerà l'intera tabella **, quindi scegli i primi elementi _n_. L'operazione di ordinamento è probabilmente 'O (n * log n)' e l'ordinamento di una tabella grande non solo legge quella tabella ma implica anche la lettura e la scrittura di file temporanei. – Dennis

+0

Questo ha funzionato per me, mentre 'User.order (" RANDOM() "). Limit (10)' no; ha sempre restituito lo stesso record. Sto utilizzando un database ** Postgresql ** e eseguo ** Rails 5 **. – sambecker

+0

Questa dovrebbe essere la risposta accettata – Tallboy

12

Penso che la soluzione migliore sia davvero ordinata in modo casuale nel database. Ma se è necessario evitare una funzione casuale specifica dal database, è possibile utilizzare l'approccio pluck e shuffle.

Per un record:

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

Per più di un record:

User.where(id: User.pluck(:id).sample(10)) 
+0

Bello. Ancora più breve: 'User.find (User.ids.shuffle.first)' – Arta

1

per MySQL questo ha funzionato per me:

User.order("RAND()").limit(10) 
-4

Ecco una soluzione rapida .. attualmente in uso con oltre 1,5 milioni di record e prestazioni decenti. La soluzione migliore sarebbe quella di mettere in cache uno o più set di record casuali e quindi aggiornarli con un worker in background all'intervallo desiderato.

Creato random_records_helper.rb di 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 

nel controller:

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

Questo è molto più veloce rispetto al metodo .order("RANDOM()").limit(10) - sono andato da un tempo di caricamento 13 sec fino a 500 ms.

+0

Ovviamente, questo funziona solo se i tuoi ID sono numeri interi autoincrementanti, iniziano con 1 e non ci sono buchi nella sequenza. –

+1

Devo votare a causa degli stessi sentimenti di @SergioTulentsev. Se hai mai distrutto qualche record, questo metodo non è affidabile, poiché potresti chiedere 10 record e potenzialmente non ottenere 10 record. La velocità è buona, ma la velocità senza risultati corretti non ha valore, mentre i risultati corretti (anche se sono più lenti di quanto vorresti) hanno ancora valore. – jeffdill2

4

Vorrei suggerire rendendo questo un ambito come si può quindi concatenare esso:

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

User.random.limit(10) 
User.active.random.limit(10) 
4

Pur non essendo la soluzione più veloce, mi piace la brevità di:

User.ids.sample(10)

I .ids metodo rendimenti un array di User ID e .sample(10) preleva 10 valori casuali da questo array.

+0

Oppure, se vuoi solo i Modelli stessi, 'User.all.sample (10)' li otterrà. – Okomikeruko

Problemi correlati