Сортировка по виртуальному атрибуту в Rails 3 - PullRequest
3 голосов
/ 14 марта 2011

ФОН: У меня есть набор сообщений, за которые можно проголосовать.Я хочу отсортировать сообщения в соответствии с их "оценкой голосов", которая определяется по следующему уравнению:

((@ 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) }

ЦЕЛЬ: Этот метод сильно сказывается на времени загрузки моих приложений.Есть ли лучший, более эффективный способ выполнить такую ​​сортировку?

Ответы [ 2 ]

14 голосов
/ 14 марта 2011

Если вы используете 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

P.S .: Эквивалент SQLLite3:

strftime('%s','now') - strftime('%s',posts.created_at)
0 голосов
/ 14 марта 2011
  1. Не следует использовать sort!, если вы собираетесь присваивать одну и ту же переменную (в данном случае это неправильно), вам следует изменить сортировку на:

    @posts.sort!{|a, b| vote_score(b) <=> vote_score(a) }
    
  2. Похоже, что вы подсчитываете голоса за Post каждый раз, когда вы звоните другому Post, который сильно влияет на базу данных и, возможно, источником потерь во время загрузки, вы можете использовать counter_cache для считать каждый раз, когда голосование сделано и сохранить его в таблице сообщений. Это сделает так, что вы только сделаете один запрос БД для загрузки из таблицы сообщений.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...