Rails 3, will_paginate, random, Repeating записи, Postgres, setseed failure

У меня есть база фильмов с атрибутами. Я хотел бы вернуть запрошенную партию этих фильмов в случайном порядке в шаблон с нумерацией страниц. Я использую will_paginate. Я попробовал следующее:

## 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)

Это работает хорошо, за исключением того, что фильмы повторяются от страницы к странице. Решения этой проблемы опубликованы здесьи здесь. Эти ссылки объясняют, что из-за того, что начальное число random()сбрасывается со страницы на страницу, согласованности нет, и OFFSETстановится бесполезным. Они предлагают отличные решения для пользователей MySQL, поскольку их функция rand(n)принимает начальное значение n. Однако Postgres этого не делает. Вы должны объявить setseed(n)в SELECTперед выдачей random()в ORDER.

Итак, я попробовал установить начальное число с помощью postgres:

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

Любопытно, что возвращались объекты Movie без каких-либо атрибутов.Из шаблона было получено следующее:

ActiveModel::MissingAttributeError in Movies#action

отсутствует атрибут: some_movie_attribute

Я отладил это, и как только стек достиг action_controller/metal, @movies содержал:

[#, #, #, #, #, #, #, #, #, #, #, #, #, #, #, #]

Это количество объектов Movie (18) соответствует количество фильмов, возвращаемых по запросу.

Я также попробовал следующее, чтобы увидеть, является ли setseed(n)проблемой, удалив метод случайного порядка:

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

Это вернуло те же объекты Movie без атрибутов, что и изложенные выше. Таким образом, похоже, что setseed(n)действительно является проблемой.

Я пробовал несколько обходных путей, например:

# 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)

Это также возвращало страницы с повторяющимися фильмами. Я думал, это потому, что для нумерации страниц нужно, чтобы объекты были упорядочены something, и вы не можете установить семена в Array#shuffle. Поэтому я попытался написать свой собственный код рандомизации, который временно сохранял бы идентификатор для упорядочения в объектах Movie. (Извините за неаккуратный код):

# 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)

Это по-прежнему производило повторяющиеся фильмы на разных страницах. В этот момент я понимаю, что will_paginate не принимает то, что вы отсортировали до вызова paginate. Итак, я попробовал это:

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

Это все еще повторяет записи. :order -> 'rand_id'игнорируется, потому что :orderимеет значение только тогда, когда вы имеете дело с объектами ActiveRecord, а не с массивами.

Так что setseed(n)кажется моей единственной надеждой на получение рандомизированных неповторяющихся записей с помощью will_paginate. Любые идеи?

Спасибо!

5
задан Community 23 May 2017 в 12:01
поделиться