5

我有一个具有属性的电影数据库。我想按照随机顺序将这些电影的一批查询返回到带有分页的模板。我正在使用will_paginate。我尝试了以下方法:Rails 3,will_paginate,随机,重复记录,Postgres,setseed失败

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

这个效果很好,除了电影在页面之间重复。此问题的解决方案已发布herehere。这些链接解释说,因为随机()种子每页重置一次,所以没有一致性,并且OFFSET变得无用。它们为MySQL用户提供了很好的解决方案,因为其rand(n)函数需要种子n。 Postgres,但是,不这样做。您必须声明setseed(n)选择发行前随机()

所以,我想Postgres的方式来设置种子:

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

奇怪的是,这回电影,绝对没有属性的对象。下面是从模板中提出:

::加载ActiveModel在MissingAttributeError电影#行动

缺少的属性:some_movie_attribute

我调试此,一旦堆达到action_controller/metal,@movies contains:

[#<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中设置种子。所以我试着写我自己的随机代码,它会临时存储一个要在Movie对象中排序的ID。 (请原谅sl 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) 

这仍然在不同的页面上产生重复的电影。在这一点上,我意识到will_paginate不会在分页被调用之前排序。所以我试过这个:

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

那还是重复记录。 :order - >'rand_id'被忽略,因为:order只在处理ActiveRecord对象而不是数组时处理。

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

谢谢!

+0

种子它选择指定的列在查询中,你指定了none,只有种子。不知道它是否会工作,但试试这个:'.select('*,setseed(1)')' – Matzi 2012-04-28 23:49:48

回答

18

不是Postgres的人,但是......我想尝试

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

至于Array#shuffle没有服用一粒种子,它使用Kernel.rand这样你就可以使用Kernel.srand

+0

谢谢,弗雷德里克,这样做的窍门。 :种子数组##shuffle with Kernel – jkym 2012-04-29 15:40:50

+0

我试图做到这一点,这给了我每次刷新时的相同随机顺序,但是如何为每个访问该站点的用户获得一个新的随机顺序? – funkylaundry 2014-05-12 13:03:26

+2

你想要为每个用户传递不同的种子 – 2014-05-12 20:53:32

1

尝试通过字段的数组select

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

一些结果使用some_movie_attribute,这是不被查询中选择,所以不可用。作为选择字段之一添加它应解决此问题。

+0

非常感谢。我试过'movies.select(['setseed(.5)','*'])。order('random()')。page(params [:page])。per_page(16) '你建议,而是用'*'来调用所有的电影字段(我的模板调用一堆属性),呈现模板(hooray!)。但现在随着种子的设置,记录不会随机排序 - 无论传入什么种子,它们都是按ID排序的。:-( – jkym 2012-04-29 02:49:50