Требуется рефакторинг условия поиска / оптимизация по полиморфной ассоциации

#ruby-on-rails #optimization #polymorphic-associations

#ruby-on-rails #оптимизация #полиморфные ассоциации

Вопрос:

Я создаю метод рекомендаций для пользователей в моем проекте. Пользователи генерируют записи интересов всякий раз, когда они просматривают, создают, комментируют или взаимодействуют с объектами (взвешенные в зависимости от действия).

Я написал метод поиска, который учитывает интересы пользователя, а затем находит пользователей, которые также заинтересованы в этих элементах. Однако это ужасно неэффективно, поскольку выполняется столько вызовов к БД, сколько интересует пользователя (до 50).

Вот сокращенная версия того, что происходит:

 #User.rb
...
has_many :interests, :as => :interestable, :dependent => :destroy

def recommendations
  recommendations = []

  Interest.for(self).limit(50).each do |item|
    recommendations << Interest.other_fans_of(item)
  end

  user_ids = recommendations.flatten.map(amp;:user_id).uniq
end
...

#interest.rb

...

belongs_to :user
belongs_to :interestable, :polymorphic => true

named_scope :for, lambda { |user| { :conditions => { :user_id => user.id } } }
named_scope :limit, lambda { |num| { :limit => num } }
named_scope :other_fans_of, lambda { |interest| { :conditions => { :interestable_type => interest.interestable_type, :interestable_id => interest.interestable_id } } }
default_scope :order => "weight DESC"

...
  

Есть ли какие-нибудь гении sql, которые могут превратить это в один приятный чистый вызов db?

Ответ №1:

Что-то вроде этого должно выполнить эту работу. Могут быть более привлекательные способы…

 class User < ActiveRecord::Base
  #...
  def recommendations
    # get a list of the relevant interests
    the_interests = Interest.for(self).limit(50).map{|x| [x.interestable_type, x.interestable_id]}
    # make some sql
    conditions = the_interests.map{|x| "(`interestable_type`=? AND `interestable_id`=?)"}.join(" OR ")
    # use ruby magic to make a valid finder and get the other user_ids
    user_ids = Interest.all(:select => '`user_id`', :conditions => [conditions, *(the_interests.flatten)]).map(amp;:user_id).uniq
  end
  #...
end