5

Tengo una base de datos de películas con atributos. Me gustaría devolver un lote consultado de esas películas en orden aleatorio a una plantilla con paginación. Estoy usando will_paginate. Intenté lo siguiente:Rails 3, will_paginate, random, repeating records, Postgres, setseed failure

## MoviesController 

movies = Movie.get_movies(query_string) # a method in Movie model that takes in 
              # a query_string and fetches movies 
              # with user-set params 

@movies = movies.order('random()').page(params[:page]).per_page(16) 

Esto funciona bien, excepto que las películas se repiten de página a página. Se han publicado soluciones a este problema here y here. Esos enlaces explican que, dado que random() semilla se restablece de página a página, no hay consistencia y OFFSET se vuelve inútil. Ofrecen excelentes soluciones para los usuarios de MySQL, ya que su función rand (n) toma una semilla n. Postgres, sin embargo, no hace esto. Usted tiene que declarar setSeed (n) enSELECT antes de emitir aleatorio() en ORDEN.

así que he intentado el camino postgres para establecer la semilla:

@movies = movies.select('setseed(.5)').order('random()').page(params[:page]).per_page(16) 

Curiosamente, que devuelve objetos de película sin ningún tipo de atributos. El siguiente fue levantado de la plantilla:

ActiveModel :: MissingAttributeError en las películas de acción #

falta de atributos: some_movie_attribute

I depurado esto y, una vez que la pila alcanza action_controller/metal, @movies contenido:

[#<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >]

Ese número de objetos de película (18) corresponde a la cantidad de películas devueltas de la consulta.

También probé el siguiente para ver si setSeed (n) era el problema eliminando el método de orden aleatorio:

@movies = movies.select('setseed(.5)').page(params[:page]).per_page(16) 

Esto devuelve el mismo no de atributo objetos de película como layed anteriormente. Entonces parece que setseed (n) es realmente el problema.

he intentado un par de soluciones, como:

# MoviesController 

movies = Movie.get_movies(query_string) 
shuf_movs = movies.shuffle ## effectively turns shuf_movs into an array 

@movies = shuf_movs.paginate(:page => params[:page], :per_page => 16) 

Eso también volvió páginas con las películas que se repiten. Pensé que esto era porque la paginación necesitaba que los objetos se ordenaran por algo, y no se pueden establecer las semillas en Array # shuffle. Así que traté de escribir mi propio código de aleatorización que almacenaría temporalmente un id para que lo ordenaran en los objetos de la película. (Disculpe el código descuidado):

# Movie model 

attr_accessor :rand_id 

# MoviesController 

movies = get_movies(query_string) 
movies_count = movies.count 
r = Random.new 
nums = [] 
rand_movs = [] 
id = 1 
while nums.count != movies_count 
    num = r.rand(0..movies_count - 1) 
    if !(nums.include?(num)) 
    movie = movies[num] 
    movie.rand_id = id 
    rand_movs << movie 
    nums  << num 
    id += 1 
    end 
end 

@movies = rand_movs.sort_by { |a| a.rand_id }.paginate(:page => params[:page], :per_page => 16) 

Que todavía produjo películas repetidas en diferentes páginas.En este punto me doy cuenta de que will_paginate no acepta lo que ha ordenado antes de llamar al paginate. Así que probé esto:

@movies = rand_movs.paginate(:order => 'rand_id', :page => params[:page], :per_page => 16) 

Que aún repite registros. : orden -> 'rand_id' se ignora porque : orden solo importa cuando se trata de objetos ActiveRecord, no de matrices.

Así que setseed (n) parece ser mi única esperanza para lograr registros aleatorios no repetitivos usando will_paginate. ¿Algunas ideas?

Gracias!

+0

El SELECT especifica las columnas en la consulta, y no especificaste ninguno, solo la semilla. No estoy seguro si funcionará, pero intente esto: '.select ('*, setseed (1)')' – Matzi

Respuesta

18

No es una persona postgres pero ... me gustaría probar

Movie.connection.execute "select setseed(0.5)" 
Movie.where(...).order('random()').page(params[:page]).per_page(15) 

Con respecto a Array#shuffle que no toman una semilla, utiliza Kernel.rand para que pueda sembrar usando Kernel.srand

+0

Gracias, Frederick. Esto fue suficiente. Además, gracias por el aviso : seeding Array # shuffle with Kernel. – jkym

+0

Estoy tratando de lograr lo mismo. Esto me da el mismo orden aleatorio en cada recarga, pero ¿cómo obtengo un nuevo orden aleatorio para cada usuario que visita el sitio? – funkylaundry

+2

Te gustaría pasar una semilla diferente para cada usuario –

1

tratan de pasar una serie de campos para select:

@movies = movies.select(['setseed(.5)', 'some_movie_attribute']).order('random()').page(params[:page]).per_page(16) 

Algunos resultado está utilizando some_movie_attribute, que no está siendo seleccionada por la consulta, por lo que no está disponible. Agregarlo como uno de los campos de selección debería resolverlo.

+0

Muchas gracias. Intenté 'movies.select (['setseed (.5)', '*']) order ('random()'). Page (params [: page]). Per_page (16) ' Pasando una matriz como sugirió, pero más bien con '*' para llamar a todos los campos de Película (mi plantilla llama a un montón de atributos), renderiza la plantilla (¡hurra!). Pero ahora, con el conjunto de semillas, los registros no ordenan aleatoriamente: están ordenados por identificación, sin importar qué semilla se transmita. :-( – jkym