Помогите оптимизировать запрос ActiveRecord (система голосования) - PullRequest
2 голосов
/ 28 сентября 2010

У меня есть система голосования с двумя моделями: Item (id, name) и Vote (id, item_id, user_id).

Вот код, который у меня есть:

class Item < ActiveRecord::Base
  has_many :votes

  def self.most_popular
    items = Item.all #where can I optimize here?
    items.sort {|x,y| x.votes.length <=> y.votes.length}.first #so I don't need to do anything here?
  end
end

В этом есть несколько проблем, в основном из-за того, что я извлекаю все записи Предметов, ТОГДА использую Ruby для вычисления популярности. Я почти уверен, что есть простое решение для этого, но я не могу понять, что именно.

Я бы предпочел собрать записи и выполнить вычисления в начальном запросе. Таким образом, я могу добавить простой :limit => 1 (или LIMIT 1) к запросу.

Будет полезна любая помощь - либо переписать во всех ActiveRecord, либо даже в сыром SQl. Последнее фактически дало бы мне более ясную картину характера запроса, который я хочу выполнить.

Ответы [ 4 ]

3 голосов
/ 28 сентября 2010

Сгруппируйте голоса по номеру предмета, упорядочите их по количеству и затем возьмите предмет первого. В рельсах 3 код для этого:

Vote.group(:item_id).order("count(*) DESC").first.item

В рельсах 2 это должно работать:

Vote.all(:order => "count(*) DESC", :group => :item_id).first.item
1 голос
/ 28 сентября 2010

sepp2k имеет правильную идею. В случае, если вы не используете Rails 3, эквивалент:

Vote.first(:group => :item_id, :order => "count(*) DESC", :include => :item).item
0 голосов
/ 28 сентября 2010

Один из простых способов справиться с этим - добавить поле «Количество голосов» в «Элемент» и обновлять его при каждом голосовании.Рельсы делали это автоматически для вас, но не уверены, что это все еще имеет место в 2.x и 3.0.В любом случае вам достаточно легко сделать это, используя шаблон Observer, или просто вставив "after_save" в модель голосования.

Тогда ваш запрос очень прост, просто добавив VOTE_COUNT DESC"Заказать к вашему запросу.

0 голосов
/ 28 сентября 2010

Возможно, есть лучший способ сделать это в ruby, но в SQL (по крайней мере, в mysql) вы можете попробовать что-то вроде этого, чтобы получить рейтинг 10 лучших:

SELECT i.id, i.name, COUNT( v.id ) AS total_votes
FROM Item i
LEFT JOIN Vote v ON ( i.id = v.item_id ) 
GROUP BY i.id
ORDER BY total_votes DESC 
LIMIT 10
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...