在ActiveRecord中加入表和过滤表

时间:2014-04-01 19:57:43

标签: ruby-on-rails activerecord

假设我在AlbumSong类之间存在一对多的关系。我想对专辑进行一些复杂的过滤并检索相应的歌曲。

让我们假设一种过滤相册的相当简单的方法

filtered_albums = Album.where('released_on >= ?', from)
                       .where('rating > ?', rating)
                       .where(genre: genres)

现在,要检索我目前使用的歌曲

Song.where(album_id: filtered_albums.pluck_id)

产生类似

的查询
SELECT * 
FROM songs 
WHERE album_id IN (1, 5, 34, 92, ..., 2392);

由于需要两个查询(一个用于提取专辑ID,另一个用于获取歌曲),因此效率非常低。

ActiveRecord允许连接表格,例如Songs.joins(:albums),但我还没有办法做Songs.joins(filtered_albums)之类的事情。有没有其他方法可以在没有"拔除"的情况下进行连接。 ids首先?

2 个答案:

答案 0 :(得分:1)

您可以使用join来获得所需的结果。

Song.joins(:album).where('albums.released_on >= ?', from)
                   .where('albums.rating > ?', rating)
                   .where(albums: {genre: genres})

joins方法将关联作为参数。我的意思是,无论你在连接方法中传递什么,都应该是模型中定义的关联名称,否则会引发错误。

修改

如果您希望将过滤方法保留在Album模型本身中,换句话说,您希望在joins中使用范围。然后你可以这样做

Song.joins(:album).merge(Album.filtered_albums)

此外,您可以将filtered_albums定义为命名范围。因此,请在相册模型中将其定义为:

scope :filtered_albums, ->(from, rating, genres){ where('released_on >= ?', from)
                   .where('rating > ?', rating).where(albums: {genre: genres}) }

或者定义为类方法。

答案 1 :(得分:1)

您可以这样做:

Song.joins(:albums).where('albums.released_on >= ?', from).where('albums.rating > ?', rating).where('albums.genre = ?', genres)

只进行一次查询。