2012-09-23 2 views
0

Как настроить default_scope в моем приложении для ведения блога, чтобы индекс заказывал записи по алгоритму, определенному в модели?Как определить значения системы репутации в модели

Если бы я использовал HackerNews-like formula для алгоритма ранжирования, как показано ниже, как я могу определить его в моей модели?

total_score = (votes_gained - 1)/(age_in_hours + 2)^1.5 

votes_gained переменная зависит от Active_Record_Reputation_System и записывается следующее в моих просмотров:

votes_gained = @post.reputation_value_for(:votes).to_i 

Наконец, age_in_hours довольно прямо вперед

age_in_hours = (Time.now - @post.created_at)/1.hour 

Как я могу использовать эти цифры, чтобы упорядочить мой индекс сообщений блога? Я пытался выяснить, как правильно определить total_score в модели, чтобы я мог добавить его в область по умолчанию как default_scope order("total_score DESC") или что-то подобное. Прямая замена не сработала, и я не уверен, как «перефразировать» каждую часть формулы.

Как точно определить total_score? Большое спасибо за понимание!

+1

Основной вопрос, который я вижу здесь является то, что, в то время как вы должны дать SQL формулу так, что она может заказать записи, формула содержит вызов функции, которая должна быть оценена с помощью рубина, а именно 'reputation_value_for (: голосов) 'за каждый пост. SQL не может вызвать ruby ​​для оценки функции. Формула может содержать только те термины, которые SQL может определить сам, например значения, хранящиеся в столбцах, или простые математические выражения, содержащие эти значения, или даже текущее время, которое вы можете получить либо путем перехода от рубина, либо с помощью SQL NOW. – cdesrosiers

+0

Спасибо @cdesrosiers. Так будет ли решение создать столбец «total_score» в моей модели Post? Если да, есть ли простой способ сделать это? – umezo

+1

Вы не можете хранить 'total_score', потому что это зависит от постоянно меняющегося параметра:' age_in_hours'. Все, что вам нужно сохранить, - 'vote_gained', которое нужно будет обновлять только тогда, когда кто-то голосует. Есть ли столбец: столбец? – cdesrosiers

ответ

1

Поскольку вы не можете полагаться на активную запись, чтобы перевести формулу в SQL, вы должны написать ее самостоятельно. Единственная потенциальная проблема здесь заключается в том, что это не независимое от базы данных решение.

Поскольку вы используете Postgres, вы можете определить объем, как (я не проверял это еще, так что дайте мне знать, работает ли он):

AGE_IN_HOURS = "(#{Time.now.tv_sec} - EXTRACT (EPOCH FROM posts.created_at))/3600" 
TOTAL_SCORE = "(rs_reputations.value - 1)/((#{AGE_IN_HOURS}) + 2)^1.5" 

default_scope joins("INNER JOIN rs_reputations ON rs_reputations.target_id = posts.id").where("rs_reputations.target_type = 'Post'").order(TOTAL_SCORE) 

EDIT: На самом деле это не будет работать, потому что , в его нынешнем виде Time.now вычисляется один раз (когда модель загружается), но она должна быть пересчитана каждый раз, когда вытягиваются записи. Используйте

default_scope lambda { order_by_score } 

def self.order_by_score 

    age_in_hours = "(#{Time.now.tv_sec} - EXTRACT (EPOCH FROM posts.created_at))/3600" 
    total_score = "(rs_reputations.value - 1)/((#{age_in_hours}) + 2)^1.5" 

    joins("INNER JOIN rs_reputations ON rs_reputations.target_id = posts.id").where("rs_reputations.target_type = 'Post'").order(TOTAL_SCORE) 
end 
+0

Кажется, работает. Благодаря! Однако, похоже, он упорядочен в порядке возрастания. Как я заказываю в другом направлении? Я просто попытался перейти на '.order (" TOTAL_SCORE DESC ")', но это не работает. Большое спасибо за вашу поддержку. – umezo

+0

Не видел ваши изменения. Позвольте мне попробовать это ... – umezo

+0

Вы можете добавить «DESC» в конец строки «total_score»: «(rs_reputations.value - 1)/((# {age_in_hours}) + 2)^1.5 DESC". Вы должны тщательно протестировать это, чтобы убедиться, что это не просто работа для угловых случаев. – cdesrosiers