2010-09-04 12 views
17

Non so se sto solo cercando nei posti sbagliati qui o cosa, ma il record attivo ha un metodo per recuperare un oggetto casuale?Rails seleziona record casuali

Qualcosa come?

@user = User.random 

Oppure ... bene dato che il metodo non esiste c'è qualche sorprendente "Rails Way" di fare questo, mi sembra sempre di essere a verbose. Sto anche usando mysql.

risposta

3

In Rails 4 I si estenderebbe ActiveRecord::Relation:

class ActiveRecord::Relation 
    def random 
    offset(rand(count)) 
    end 
end 

questo modo è possibile utilizzare gli ambiti:

SomeModel.all.random.first # Return one random record 
SomeModel.some_scope.another_scope.random.first 
3

Vorrei utilizzare un ambito denominato. Basta lanciarlo nel tuo modello Utente.

named_scope :random, :order=>'RAND()', :limit=>1 

La funzione casuale non è la stessa in ogni database però. SQLite e altri usano RANDOM() ma dovrai usare RAND() per MySQL.

Se vuoi essere in grado di afferrare più di una riga casuale puoi provare questo.

named_scope :random, lambda { |*args| { :order=>'RAND()', :limit=>args[0] || 1 } } 

Se si chiama User.random questo verrà impostato a 1, ma si può anche chiamare User.random(3) se si desidera più di uno.

+2

Ho sentito che RAND() è molto lento perché dapprima recupera tutti i record e poi in qualche modo ne sceglie uno, ma probabilmente ho torto. –

+1

@Blaenk: È * molto lento su MySQL. Comunque non so sull'implementazione. – Swanand

+1

+1 per fare uno scope, mi piace anche questo. –

39

La maggior parte degli esempi che ho visto questo finiscono per contare le righe nella tabella, quindi generare un numero casuale per sceglierne uno. Questo perché alternative come RAND() sono inefficienti in quanto in realtà ottengono ogni riga e assegnano loro un numero casuale, o così ho letto (e sono specifico del database, penso).

È possibile aggiungere un metodo come quello che ho trovato here.

module ActiveRecord 
    class Base 
    def self.random 
     if (c = count) != 0 
     find(:first, :offset =>rand(c)) 
     end 
    end 
    end 
end 

Questo farà sì che ogni modello si utilizza ha un metodo chiamato random che funziona nel modo descritto sopra: genera un numero casuale all'interno del conteggio delle righe della tabella, quindi recupera la riga associata quel numero casuale. Quindi, in pratica, stai facendo solo un recupero che è probabilmente quello che preferisci :)

Puoi anche dare un'occhiata a this rails plugin.

+3

Questo è bello. Mi piace perché non è inoltre specifico ORM. Bel lavoro, grazie! –

+0

in Rails 4.1, ho dovuto usare: 'offset (rand (c)). First' –

2

Se si avrebbe bisogno di un record casuale, ma solo entro certi criteri è possibile utilizzare "random_where" da questo codice:

module ActiveRecord 
    class Base 
    def self.random 
     if (c = count) != 0 
     find(:first, :offset =>rand(c)) 
     end 
    end 

    def self.random_where(*params) 
     if (c = where(*params).count) != 0 
     where(*params).find(:first, :offset =>rand(c)) 
     end 
    end 

    end 
end 

Per esempio:

@user = User.random_where("active = 1") 

Questa funzione è molto utile per la visualizzazione prodotti casuali basati su alcuni criteri aggiuntivi

+0

Rails 3 e concatenamento di relazioni attive si prende cura di questo caso. Si potrebbe fare semplicemente 'User.random.where ('active = 1')' senza la necessità di un ulteriore metodo globale. –

+1

Il metodo "find" non restituisce l'oggetto Relation, restituisce sia un oggetto modello che un array :), ma! potresti fare questo 'User.where ('active = 1'). random 'e questo funzionerà bene ... Non so perché l'ho perso :) –

+0

Sei corretto. Buon occhio Ho pensato una cosa e ne ho scritto un'altra. :-) –

7

Abbiamo rilevato che gli offset correvano su molto su MySql per un grande tavolo. Invece di utilizzare offset come:

model.find(:first, :offset =>rand(c)) 

...abbiamo trovato il seguente tecnica ha più di 10 volte più veloce (fisso via da 1):

max_id = Model.maximum("id") 
min_id = Model.minimum("id") 
id_range = max_id - min_id + 1 
random_id = min_id + rand(id_range).to_i 
Model.find(:first, :conditions => "id >= #{random_id}", :limit => 1, :order => "id") 
+1

L'ultima riga è un po 'troppo prolissa. Questo fa la stessa cosa (in Rails 3): 'Model.where (" id> = # {random_id} "). First' –

+3

Se alcuni record sono stati cancellati, il metodo non genera risultati distribuiti uniformemente, che è generalmente ciò che ci si aspetterebbe. Immagina il caso in cui esistono gli id ​​1-10, ad eccezione dell'id 5. In tal caso, il modello con un id di 6 verrebbe restituito quando il generatore di numeri casuali produce un 5 o un 6 (il 20% del tempo), mentre ciascuno degli altri ID esistenti è selezionato solo il 10% delle volte. Forse non è un rompicapo, ma qualcosa di cui essere a conoscenza. – encoded

4

Provare a utilizzare il metodo di Array sample:

@user = User.all.sample(1) 
+0

Mi piace la tua risposta che è un vero stile rubino – blawzoo

+16

Non sarà in scala. Gonfierà tutti gli utenti in memoria ... – Nicholas

+0

È necessario rimuovere (1), in quanto si ottiene un array singleton. L'utilizzo di User.all.sample ha come risultato un solo utente – Danny

1

ecco la soluzione migliore per ottenere record casuale dal database. RoR forniscono tutto in un facile utilizzo.

Per ottenere record casuali da DB utilizzare campione, di seguito è la descrizione per quello con l'esempio.

Backport of Array # campione basato su Marc-Andre Lafortune's github.com/marcandre/backports/ Restituisce un elemento casuale o n elementi casuali dall'array. Se la matrice è vuota e n è nulla, restituisce nil. Se n viene passato e il suo valore è minore di 0, solleva un'eccezione ArgumentError. Se il valore di n è uguale o maggiore di 0 restituisce [].

[1,2,3,4,5,6].sample  # => 4  
[1,2,3,4,5,6].sample(3) # => [2, 4, 5]  
[1,2,3,4,5,6].sample(-3) # => ArgumentError: negative array size  
[].sample  # => nil  
[].sample(3) # => []  

È possibile utilizzare la condizione in base alle proprie esigenze come nell'esempio seguente.

User.where (attivo: true) .Sample (5)

tornerà a caso 5 attiva dell'utente da tavolo Utente

Per maggiori informazioni visitare il sito: http://apidock.com/rails/Array/sample

Problemi correlati