我有一个带属性的电影数据库。我想以随机顺序将查询过的一批电影返回到带分页的模板。我正在使用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)
这很好用,除了电影在页面之间重复。已发布此问题的解决方案here和here。这些链接解释了,因为 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)
奇怪的是,它返回的Movie对象完全没有属性。以下内容来自模板:
Movie#action
中的ActiveModel :: MissingAttributeError缺少属性:some_movie_attribute
我调试了这个,一旦堆栈到达action_controller / metal,@ 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。 (请原谅邋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)
仍然会在不同的页面中制作重复的电影。在这一点上,我意识到在调用 paginate 之前,will_paginate不会接受你排序的内容。所以我尝试了这个:
@movies = rand_movs.paginate(:order => 'rand_id', :page => params[:page], :per_page => 16)
仍然重复记录。 :订单 - &gt; 'rand_id'会被忽略,因为:order 仅在您处理ActiveRecord对象而不是数组时很重要。
所以 setseed(n)似乎是我使用will_paginate实现随机非重复记录的唯一希望。有什么想法吗?
谢谢!
答案 0 :(得分: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
答案 1 :(得分:1)
尝试将一组字段传递给select
:
@movies = movies.select(['setseed(.5)', 'some_movie_attribute']).order('random()').page(params[:page]).per_page(16)
某些结果是使用some_movie_attribute
,而您的查询未选择该{{1}},因此无法使用。将其添加为选择字段之一应解决此问题。