Ruby on Rails — лидер по количеству голосов за месяц

Мне нужен совет по системе голосования в рельсах, которая ежемесячно распознает победителя голосов. У меня есть система, которая работает, но я новичок в рельсах, и я уверен, что есть более эффективные методы. Ниже приведена упрощенная версия моей текущей настройки (код контроллера опущен):

class Charity < ActiveRecord::Base
  has_many :votes
end

class Vote < ActiveRecord::Base
  belongs_to :charity
end

Моя схема выглядит следующим образом:

ActiveRecord::Schema.define(:version => 20130310015627) do
  create_table "charities", :force => true do |t|
    t.string   "name"
    t.text     "description"
    t.date     "last_win"
    t.datetime "created_at",  :null => false
    t.datetime "updated_at",  :null => false
  end 
  create_table "votes", :force => true do |t|
    t.integer  "charity_id"
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end
end

Я буду использовать гем 'whenever' для запуска задания cron, чтобы определить победителя месяца и обновить столбец 'last_win' в таблице благотворительных организаций. В следующем коде я сомневаюсь в своей эффективности:

vote_counts = Vote.count(:group => "charity_id")
most_votes = vote_counts.values.max
winning_ids = vote_counts.map{|k,v| v == most_votes ? k :nil }.compact
charities = Charity.find(winning_ids)
charities.each {|charity| charity.update_attributes(:last_win => Date.today)}

Я уверен, что есть много способов сделать это лучше, и буду признателен за некоторые предложения. Если у вас есть предложения по лучшим способам настройки таблицы голосов / ассоциаций, это тоже будет оценено.

Заранее спасибо, CRS


person Clay    schedule 11.03.2013    source источник
comment
Может ли быть только один победитель? Похоже, ваш код обрабатывает связи.   -  person John Naegle    schedule 11.03.2013
comment
Победителей может быть несколько. Задание cron будет выполняться 1-го числа месяца. Я настрою его так, чтобы учитывались только голоса, поданные в течение предыдущего месяца.   -  person Clay    schedule 11.03.2013


Ответы (2)


Что-то вроде этого:

Если бы был только один победитель, я думаю, это сработало бы.

winner_id = Vote.group(:charity_id).order("count(*) desc").pluck(:charity_id).first
Charity.find(winner)id).update_attribute!(:last_win => Date.today)

Вы можете изменить его для галстуков:

most_votes = Vote.group(:charity_id).order("count(*) desc").count.first[1]
winners = Vote.group(:charity_id).having("count(*) = ?", most_votes).pluck(:charity_id)

Charity.where(:id => winners).update_all(:last_win => Date.today)

Убедитесь, что все правильно проиндексировано в вашей базе данных,

Вероятно, вы можете упростить его еще больше, но SQL станет более сложным.

person John Naegle    schedule 11.03.2013
comment
Спасибо за вашу помощь. И спасибо, что напомнили мне об индексации. - person Clay; 12.03.2013

Последние две строки могут быть такими:

Charity.where(id:winning_ids).update_all(last_win:Date.today)

Что будет переведено в единую команду обновления SQL вместо выдачи команды обновления для каждой победившей благотворительной организации.

Первая часть, где вы определяете победившие благотворительные организации, выглядит нормально, и, поскольку вы запускаете ее как задание cron, вас, вероятно, не волнует, займет ли это несколько минут.

Однако, если вы хотите отображать значения в режиме реального времени, вы можете добавить хук after_create на Vote для обновления счетчика благотворительной организации (возможно, в другой таблице):

class Vote < ActiveRecord::Base
  belongs_to :charity
  after_create :increment_vote_count
  CharityVote.where(year:Time.now.year, month:Time.now.month, 
    charity_id:self.charity_id).first_or_create.increment!(:counter)
end
person Community    schedule 11.03.2013