5

属性を持つ映画のデータベースがあります。これらの映画のクエリされたバッチをランダムな順序で、ページ付けされたテンプレートに返したいと思います。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 を使用してランダム化された非反復レコードを達成するための唯一の希望のようです。何か案は?

ありがとう!

4

2 に答える 2

19

postgresの人ではありませんが...試してみます

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

Array#shuffle種を取らないことに関しては、使用するKernel.randので、次を使用して種をまくことができますKernel.srand

于 2012-04-29T08:04:54.917 に答える
1

フィールドの配列を に渡してみてくださいselect:

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

一部の結果は を使用しsome_movie_attributeていますが、これはクエリで選択されていないため、使用できません。選択フィールドの1つとして追加すると、これが解決されるはずです。

于 2012-04-29T01:37:00.097 に答える