属性を持つ映画のデータベースがあります。これらの映画のクエリされたバッチをランダムな順序で、ページ付けされたテンプレートに返したいと思います。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が役に立たなくなることを説明しています。rand(n)関数はシードnを取るため、MySQL ユーザーに優れたソリューションを提供します。ただし、Postgres はこれを行いません。ORDERでrandom()を発行する前に 、SELECTでsetseed(n)を宣言する必要があります。
だから私はシードを設定するpostgresの方法を試しました:
@movies = movies.select('setseed(.5)').order('random()').page(params[:page]).per_page(16)
不思議なことに、属性がまったくない Movie オブジェクトが返されました。テンプレートから次のように取り上げられました。
ActiveModel::映画の MissingAttributeError#action
属性がありません: some_movie_attribute
これをデバッグし、スタックが action_controller/metal に到達すると、@movies に次の内容が含まれていました。
[#<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >, #<Movie >]
この 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)
また、ムービーが繰り返されるページも返されました。これは、ページネーションでオブジェクトを何かで順序付けする必要があり、Array#shuffle でシードを設定できないためだと思いました。そこで、ムービー オブジェクトで順序付けされる ID を一時的に格納する独自のランダム化コードを作成してみました。(ずさんなコードを許してください):
# 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 を使用してランダム化された非反復レコードを達成するための唯一の希望のようです。何か案は?
ありがとう!