2011-03-13 37 views
3

背景:我有一組可以投票的帖子。我想根據由以下等式確定的「投票分數」對帖子進行分類:通過Rails 3中的虛擬屬性排序

((@ post.votes.count)/((Time.now - @ post.created_at)** 1 ))

我目前定義投得分爲這樣:

def vote_score(x) 
    ((x.votes.count)/((Time.now - x.created_at) ** 1)) 
    end 

而且它們的排序爲這樣:

@posts = @posts.sort! { |a,b| vote_score((b) <=> vote_score((a) } 

目的:此方法T對我的應用加載時間造成了巨大的損失。有沒有更好,更有效的方法來完成這種排序?

+0

有沒有理由讓你的等式提升到1的冪次?這似乎沒有必要。 – nzifnab 2011-03-13 21:49:53

+0

你還有兩個額外的'('在你發佈的最後一個代碼中, – Dogbert 2011-03-13 21:55:36

+0

你是對的,我一直在玩弄價值觀,在發佈權力的時候碰巧是1 - 沒有特別的理由。括號 – neon 2011-03-13 21:57:47

回答

14

如果你正在使用MySQL,你可以使用查詢做整個事情:

SELECT posts.id, 
     (COUNT(votes.id)/(TIME_TO_SEC(NOW()) - TIME_TO_SEC(posts.created_at))) as score 
FROM  posts INNER JOIN votes ON votes.post_id = posts.id 
GROUP BY posts.id 
ORDER BY score DESC 

或者:

class Post 
    scope :with_score, select('posts.*') 
    .select('(COUNT(votes.id)/(TIME_TO_SEC(NOW()) - TIME_TO_SEC(posts.created_at))) as score') 
    .joins(:votes) 
    .group('posts.id') 
    .order('score DESC') 
end 

這會使你的整個查詢:

@posts = Post.with_score.all 

P.S:然後,您可以修改Post類以使用分數的SQL版本(如果存在)。您也可以在實例中緩存的得分功能,這樣你就不必每次你問一個職位的得分時間重新計算的話:

class Post 
    def score 
    @score ||= self[:score] || (votes.count/(Time.now.utc - x.created_at.utc) 
    end 
end 

PS:本SQLLite3相當於是:

strftime('%s','now') - strftime('%s',posts.created_at) 
+1

+1表示寫入SQL比使用內置的Ruby函數更快,更好,更智能:) – sethvargo 2011-03-13 21:56:48

+0

在嘗試此操作時出現此錯誤:「SQLite3 :: SQLException:no such function:TIME_TO_SEC: SELECT posts。*,...「 – neon 2011-03-13 22:21:41

+0

在sqllite3中,您希望使用strftime('%s','now'),其中'now'表示當前時間,或者可以替換爲您想要計算秒數的時間,而不是MySQL的TIME_TO_SEC函數。 – 2011-03-13 22:49:32

0
  1. 你不應該使用sort!如果你要分配給同一個變量(它是錯誤在這種情況下),你應該更改排序到:

    @posts.sort!{|a, b| vote_score(b) <=> vote_score(a) } 
    
  2. 它看起來像您每次打電話給另一個發佈數據庫的帖子時,都會計算Post的投票數,這可能是您的加載時間的收費來源,您可以使用counter_cache來計算每次進行投票並將其存入帖子表。這將使它,所以你只做一個數據庫查詢加載從張貼表。

http://guides.rubyonrails.org/association_basics.html