【发布时间】:2012-04-28 21:29:09
【问题描述】:
我有一个带有属性的电影数据库。我想以随机顺序将查询的一批电影返回到带有分页的模板。我正在使用 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 对象完全没有属性。以下是从模板中提出的:
电影中的 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 >]
电影对象的数量 (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)
这也返回了包含重复电影的页面。我认为这是因为分页需要按 something 对对象进行排序,而您不能在 Array#shuffle 中设置种子。因此,我尝试编写自己的随机化代码,该代码将临时存储要在 Movie 对象中排序的 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 实现随机非重复记录的唯一希望。有什么想法吗?
谢谢!
【问题讨论】:
-
select 指定了查询中的列,而您没有指定,只指定了种子。不确定它是否会起作用,但试试这个:
.select('*, setseed(1)')
标签: ruby-on-rails ruby-on-rails-3 postgresql random will-paginate