Rails 3,will_paginate,随机,重复记录,Postgres,setseed失败

jky*_*kym 5 random postgresql ruby-on-rails will-paginate ruby-on-rails-3

我有一个带属性的电影数据库.我想以随机顺序将查询过的一批电影返回到带分页的模板.我正在使用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)
Run Code Online (Sandbox Code Playgroud)

这很好用,除了电影在页面之间重复.此问题的解决方案已在此处此处发布.这些链接解释了这一点,因为random()种子在页面之间重置,所以没有一致性,并且OFFSET变得无用.它们为MySQL用户提供了很好的解决方案,因为它的rand(n)函数需要种子n.然而,Postgres并没有这样做.在ORDER中发出random()之前,必须 在SELECT中声明setseed(n).

所以我尝试了postgres方式来设置种子:

@movies = movies.select('setseed(.5)').order('random()').page(params[:page]).per_page(16)
Run Code Online (Sandbox Code Playgroud)

奇怪的是,返回的Movie对象完全没有属性.从模板中提出以下内容:

Movies#action中的ActiveModel :: MissingAttributeError

缺少属性:some_movie_attribute

我调试了这个,一旦堆栈到达action_controller/metal,@ mobies包含:

[#<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)
Run Code Online (Sandbox Code Playgroud)

这返回了与上面列出的相同的非属性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)
Run Code Online (Sandbox Code Playgroud)

这也返回了重复电影的页面.我认为这是因为分页需要按照某些东西排序对象,并且你不能在Array #shuffle中设置种子.所以我尝试编写自己的随机化代码,暂时存储要在Movie对象中排序的id.(请原谅邋code的代码):

# 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)
Run Code Online (Sandbox Code Playgroud)

这仍然在不同的页面中产生重复的电影.在这一点上,我意识到在调用paginate之前,will_paginate不会接受你已经排序的内容.所以我尝试了这个:

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

这仍然重复记录.:order - >'rand_id'被忽略,因为:order仅在你处理ActiveRecord对象而不是数组时很重要.

所以setseed(n)似乎是我使用will_paginate实现随机非重复记录的唯一希望.有任何想法吗?

谢谢!

Fre*_*ung 18

不是一个postgres人,但......我试试

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

关于Array#shuffle不采取种子,它使用,Kernel.rand所以你可以使用种子Kernel.srand

  • 您想为每个用户传递不同的种子 (2认同)