Как создать условие поиска, которое работает на связанной модели - PullRequest
1 голос
/ 17 января 2009

Предполагается, что приложение rails имеет следующие модели song, play, radio_station: песня has_many играет play has_many radio_stations radio_station имеет атрибут "call_sign"

Я хочу создать одну находку для Песни, которая возвратила бы мне все песни, где последняя игра (песни) была воспроизведена радиостанцией "WKRP"

Я мог бы сделать

найдено = [] Song.find (: all,: include => [{: plays => [: radio_station]}]). Each do | song | найдено << song if song.plays.last.radio_station.call_sign == "WKRP" конец </p>

но это будет означать, что все песни должны быть сначала извлечены из БД и зациклены ... что замедлится по мере сборки # песен и пьес.

Как я могу поместить это в одно условие поиска?

Кажется, что это то, что должно быть выполнимо - но я не могу понять, как, и я на самом деле не гуру SQL ...

и затем .. хотел бы втиснуть его в именованную область видимости, чтобы я мог назвать его так:

Song.find_all_last_played_at ( "WKRP")

Ответы [ 5 ]

1 голос
/ 17 января 2009

Это, вероятно, не может быть сделано искателями ActiveRecord. Вы должны свернуть некоторый пользовательский SQL с подзапросом (требуется MySQL 5):


SELECT s.* FROM songs s LEFT JOIN plays p
 WHERE p.radio_station_id = [id of WKRP] 
   AND  p.created_at = (SELECT MAX(created_at) FROM plays p2 
                         WHERE p2.song_id = s.id)

Загрузите эти песни, используя find_by_sql:


Song.find_by_sql('SELECT ...')

Вы можете изменить его, используя named_scope, если хотите.



class Song < ActiveRecord::Base
  has_many :plays

  named_scope :last_played_on, lambda { |radio_station| 
    { :conditions => ['plays.created_at = (SELECT MAX(p2.created_at) FROM plays p2 
        WHERE p2.song_id = songs.id) AND plays.radio_station_id = ?', radio_station.id],
      :joins => [:plays] }
  }
end

@wkrp = RadioStation.find_by_name('WKRP') 
Song.last_played_on(@wkrp).each { |song| ... }

Обратите внимание, что использование подзапросов может быть медленным. Вы можете кэшировать последнюю play_id песни в виде поля в таблице songs, чтобы упростить и ускорить запросы. Начните с добавления поля last_play_id в таблицу песен.


class Play < ActiveRecord::Base
  belongs_to :song

  after_create do |play|
    play.song.last_play_id = play.id
    play.song.save!
  end
end 
0 голосов
/ 09 апреля 2009

Создает named_scope, который извлекает все пьесы для этой конкретной радиостанции, с порядком 'create_at DESC' и затем цепочкой .last в конце возвращенного запроса. По линиям:

Song.played_by('WRXD').last
0 голосов
/ 17 января 2009

Я думаю, что вы можете сделать это, начиная с ваших ассоциаций (обратите внимание, что по умолчанию 'order' равен 'create_at'):

class Song
  has_many :plays
  has_many :radio_staions, :through => :plays, :uniq => true
  has_one  :last_radio_station, :through => :plays, :source => :last_radio_station

  named_scope :last_played_at, lambda {|call_sign| {:include => :last_radio_station, :conditions => ["radio_stations.call_sign = ?", call_sign]}}
end

Ожидается, что ваша модель Play будет иметь:

class Play
  has_one :last_radio_station, :class_name => 'RadioStation', :order => 'created_at'
  has_many :radio_stations

Тогда вы можете просто позвонить:

Song.last_played_at("WKRZ")

Я сам не пробовал, но, надеюсь, это сработает.

0 голосов
/ 17 января 2009

Для производительности вы, вероятно, должны делать то, что предлагает wvangergen. Но для синтетического сахара, я думаю, это может сработать:

class Song
  has_many :plays, :order => 'created_at' do
    def last_played
      self.maximum(:created-at)
    end
  end
end

class Play
  named_scope :by, lambda {|call_sign| {:conditions => ["radio_stations.call_sign = ?", call_sign]}}
end

Для использования:

Song.plays.last_played.by("WKRZ")

Я не тестирую код, поэтому извиняюсь за синтаксические ошибки =) Теперь, когда я читаю это, я не думаю, что это сработает, но я надеюсь, что вы сможете это выяснить .

0 голосов
/ 17 января 2009

Вы, вероятно, должны посмотреть на именованные области.

class Song
  named_scope :last_played_by, lambda {|call_sign| {:conditions => ["plays.radio_stations.call_sign = ?", call_sign]}}
end

Вы бы использовали его как таковое

Song.last_played_by("WKRZ")

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

class Song
  named_scope :played_by, lambda {|call_sign| {:conditions => ["plays.radio_station.call_sign = ?", call_sign]}}
  named_scope :last_play {:conditions => ["MAX(plays.created_at)"]
end

Чтобы использовать это:

Song.last_play.played_by("WKRZ")

Я считаю, что это правильно, но я не проверял это, и я не настолько хорошо знаком с именованными областями.

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