2010-09-04 8 views
17

No sé si solo estoy mirando en los lugares incorrectos o qué, pero ¿el registro activo tiene un método para recuperar un objeto aleatorio?Los rieles seleccionan el registro aleatorio

¿Te gusta?

@user = User.random 

O ... bueno, ya que el método no existe hay alguna increíble "Rieles manera" de hacer esto, yo siempre parecen ser prolijo. Estoy usando mysql también.

Respuesta

3

En Rails 4 I se extendería ActiveRecord::Relation:

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

De esta manera puede utilizar ámbitos:

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

Usaría un alcance con nombre. Solo agrega esto a tu modelo de Usuario.

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

La función aleatoria no es la misma en cada base de datos. SQLite y otros usan RANDOM() pero necesitarás usar RAND() para MySQL.

Si desea ser capaz de tomar más de una fila al azar puede probar esto.

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

Si llama User.random se pondrá por defecto a 1, pero también puede llamar a User.random(3) si quieres más de uno.

+2

Escuché que RAND() es muy lento porque primero busca cada registro y de alguna manera elige uno, pero probablemente estoy equivocado. –

+1

@Blaenk: It * is * muy lento en MySQL. No obstante, no sé sobre la implementación. – Swanand

+1

+1 para hacer un alcance, me gusta eso también. –

39

La mayoría de los ejemplos que he visto que hacen esto terminan contando las filas de la tabla, a continuación, generar un número aleatorio para elegir uno. Esto se debe a que alternativas como RAND() son ineficaces en el sentido de que en realidad obtienen cada fila y les asignan un número aleatorio, o al menos he leído (y creo que son específicas de la base de datos).

se puede añadir un método como el que he encontrado here.

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

Esto hará que sea por lo que cualquier modelo que utiliza tiene un método llamado random que funciona de la manera que he descrito anteriormente: genera un número aleatorio dentro del recuento de las filas de la tabla, a continuación, Obtiene la fila asociada con ese número al azar. Así que, básicamente, sólo se está haciendo FETCH que es lo que probablemente prefiere :)

También puede echar un vistazo a this rails plugin.

+3

Esto es bueno. Me gusta porque además no es ORM específico. Buen trabajo, gracias! –

+0

en Rails 4.1, tuve que usar: 'offset (rand (c)). First' –

2

Si se necesita un registro aleatorio, pero sólo dentro de ciertos criterios que podría utilizar "random_where" de este código:

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 

Por ejemplo:

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

Esta función es muy útil para mostrar productos aleatorios basados ​​en algunos criterios adicionales

+0

Rails 3 y el encadenamiento activo de relaciones se ocupa de este caso. Podrías hacer 'User.random.where ('active = 1')' sin la necesidad de un método global adicional. –

+1

El método "buscar" no devuelve el objeto de relación, devuelve un objeto de modelo o una matriz :), pero! podrías hacer esto 'User.where ('active = 1'). random' y esto funcionará muy bien ... No sé por qué me lo perdí :) –

+0

Estás en lo correcto. Buen ojo. Pensé una cosa y escribí otra. :-) –

7

Encontramos que los desplazamientos corrieron muy lentamente en MySql para una tabla grande. En lugar de utilizar desplazamiento como:

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

...encontramos la siguiente técnica corrió más de 10 veces más rápido (fijo fuera por 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

La última línea es un poco demasiado detallada. Esto hace lo mismo (en Rails 3): 'Model.where (" id> = # {random_id} "). First' –

+3

Si se ha eliminado algún registro, el método no genera resultados uniformemente distribuidos, que es en general, lo que uno esperaría. Imagínese el caso donde existen los ids 1-10, excepto id 5. En ese caso, el modelo con un id de 6 se devolverá cuando el generador de números aleatorios produzca un 5 o un 6 (20% del tiempo), mientras que cada uno de los otros identificadores existentes se selecciona solo el 10% del tiempo. Tal vez no sea un factor decisivo, pero algo de lo que estar al tanto. – encoded

4

Trate de usar el método de matriz sample:

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

Me gusta su respuesta que es un verdadero estilo de rubí – blawzoo

+16

Definitivamente no se escalará. Inflará todos los usuarios en la memoria ... – Nicholas

+0

Debe eliminar (1), ya que esto da como resultado una matriz singleton. El uso de User.all.sample da como resultado un solo usuario – Danny

1

Aquí es la mejor solución para obtener registros al azar de la base de datos. RoR proporciona todo en facilidad de uso.

Para obtener registros aleatorios del uso de DB muestra, a continuación se muestra la descripción de eso con el ejemplo.

Backport of Array # muestra basada en github.com/marcandre/backports/ de Marc-Andre Lafortune Devuelve un elemento aleatorio o n elementos aleatorios de la matriz. Si la matriz está vacía y n es nil, devuelve nil. Si se pasa n y su valor es menor que 0, se genera una excepción ArgumentError. Si el valor de n es igual o mayor que 0, devuelve [].

[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) # => []  

Puede utilizar la condición con según su requisito como el ejemplo siguiente.

User.where (activo: true) .sample (5)

volverá al azar 5 de usuario activo de la mesa usuario

Para obtener más ayuda, por favor visite: http://apidock.com/rails/Array/sample

Cuestiones relacionadas