Rails 3,will_paginate,随机,重复记录,Postgres,setseed失败 [英] Rails 3, will_paginate, random, repeating records, Postgres, setseed failure

查看:103
本文介绍了Rails 3,will_paginate,随机,重复记录,Postgres,setseed失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含属性的电影数据库。我想将一批查询过的电影按随机顺序返回到带有分页的模板中。我正在使用will_paginate。我尝试了以下操作:

I have a database of movies with attributes. I'd like to return a queried batch of those movies in random order to a template with pagination. I'm using will_paginate. I tried the following:

## 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 变得无用。它们为MySQL用户提供了很好的解决方案,因为它的 rand(n)函数需要种子 n 。但是,Postgres不会这样做。您必须先在 SELECT 中声明 setseed(n),然后再在 ORDER 中发布 random()

This works nicely, except movies are repeated from page to page. Solutions to this problem have been posted here and here. Those links explain that, because the random() seed is reset from page to page, there is no consistency and OFFSET is rendered useless. They offer great solutions for MySQL users, as its rand(n) function takes a seed n. Postgres, however, doesn't do this. You have to declare setseed(n) in SELECT before issuing random() in ORDER.

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

So I tried the postgres way to set the seed:

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

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

Curiously, that returned Movie objects with absolutely no attributes. The following was raised from the template:


ActiveModel :: MissingAttributeError in Movies#action

ActiveModel::MissingAttributeError in Movies#action


缺少属性:some_movie_attribute

missing attribute: some_movie_attribute


我对此进行了调试,堆栈到达action_controller / metal后,包含@movies:

I debugged this and, once the stack reached action_controller/metal, @movies contained:

[#< Movie> ;、#< Movie>,#< ;电影>,#<电影>,#<电影>,#<电影>,#<电影>,#<电影>,#<电影>,#<电影>,#<电影>,#<电影>,#<电影>,#<电影>,#<电影>,#<电影>]

电影对象的数量(18)对应于从查询返回的电影数量。

That number of Movie objects (18) corresponds to the number of movies returned from the query.

我还尝试了以下操作,通过删除随机顺序方法来查看 setseed(n)是否是问题所在:

I also tried the following to see if setseed(n) was the problem by removing the random order method:

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

这将返回与上面列出的相同的非属性Movie对象。因此看来 setseed(n)确实是问题所在。

This returned the same non-attribute Movie objects as layed out above. So it appears that setseed(n) is indeed the issue.

我尝试了几种解决方法,例如:

I tried a couple of workarounds, like:

# 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。 (请原谅代码):

That also returned pages with repeating movies. I thought this was because pagination needed the objects to be ordered by something, and you can't set seeds in Array#shuffle. So I tried writing my own randomization code that would temporarily store an id to be ordered by in the Movie objects. (Please excuse the sloppy 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不会接受您调用 paginate 之前的排序。因此,我尝试了以下操作:

That still produced repeating movies in different pages. At this point I realize will_paginate doesn't take in what you've sorted by before paginate is called. So I tried this:

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

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

That still repeats records. :order -> 'rand_id' is ignored because :order only matters when you're dealing with ActiveRecord objects, not arrays.

因此 setseed(n)似乎是我使用Will_paginate实现随机非重复记录的唯一希望。有想法吗?

So setseed(n) appears to be my only hope for achieving randomized non-repeating records using will_paginate. Any ideas?

谢谢!

推荐答案

不是Postgres人,但...我会尝试

Not a postgres person but ... I'd try

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

关于 Array#shuffle 不获取种子的情况,它使用 Kernel.rand ,您可以使用 Kernel.srand

With regards to Array#shuffle not taking a seed, it uses Kernel.rand so you can seed it using Kernel.srand

这篇关于Rails 3,will_paginate,随机,重复记录,Postgres,setseed失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆