2014-08-27 76 views
0

好的,所以我有这个Concern其中包含一个方法top用于检索记录顺序的次数由某人已经聆听他们(如果该记录是一首歌曲,直接或通过歌曲,如果它是别的东西,如流派或艺术家)。如果是平局,则按其他网站上的受欢迎程度排序记录。如何将此SQL转换为ActiveRecord查询?

以下代码几乎完美地工作。它以正确的顺序返回一个对象数组。我的主要问题是我得到一个数组而不是一个关系。因此,来电者不能再添加诸如Song.top.limit 3Genre.top.offset(10).limit(5)之类的内容。

这里是我的方法:

def top(options = {}) 
    tname = self.name.tableize 

    inner_select = self.select("#{tname}.*,COUNT(DISTINCT(listens.id)) AS listens_count") 
    inner_select = inner_select.joins(:songs) unless self.name == 'Song' 
    inner_select = inner_select.joins("LEFT JOIN listens ON listens.song_id = songs.id") 
    inner_select = inner_select.where("listens.user_id = ?", options[:for].id) if options[:for].is_a? User 
    inner_select = inner_select.group("#{tname}.id").to_sql 

    # this is the part that needs fixin' 
    find_by_sql(" 
     SELECT 
      #{tname}.*, 
      #{tname}.listens_count, 
      SUM(sources.popularity) AS popularity_count 
     FROM (#{inner_select}) AS #{tname} 
     LEFT JOIN sources ON 
      sources.resource_id = #{tname}.id 
      AND 
      sources.resource_type = '#{self.name} 
     GROUP BY #{tname}.id 
     ORDER BY listens_count DESC, popularity_count DESC 
    ") 
end 

下面是生成,按要求SQL查询。这是Song.top

SELECT 
    songs.*, 
    songs.listens_count, 
    SUM(sources.popularity) AS popularity_count 
FROM (SELECT songs.*,COUNT(DISTINCT(listens.id)) AS listens_count FROM "songs" LEFT JOIN listens ON listens.song_id = songs.id GROUP BY songs.id) AS songs 
LEFT JOIN sources ON 
    sources.resource_id = songs.id 
    AND 
    sources.resource_type = 'Song' 
GROUP BY songs.id 
ORDER BY listens_count DESC, popularity_count DESC 

这是Artist.top

SELECT 
    artists.*, 
    artists.listens_count, 
    SUM(sources.popularity) AS popularity_count 
FROM (SELECT artists.*,COUNT(DISTINCT(listens.id)) AS listens_count FROM "artists" INNER JOIN "artists_songs" ON "artists_songs"."artist_id" = "artists"."id" INNER JOIN "songs" ON "songs"."id" = "artists_songs"."song_id" LEFT JOIN listens ON listens.song_id = songs.id GROUP BY artists.id) AS artists 
LEFT JOIN sources ON 
    sources.resource_id = artists.id 
    AND 
    sources.resource_type = 'Artist' 
GROUP BY artists.id 
ORDER BY listens_count DESC, popularity_count DESC 
+0

它可能是有点容易(从日志,或使用'to_sql') – IS04 2014-08-27 20:28:08

回答

0

所以,我找到了答案,原来我已经彻底无缘from方法。我会在这里发表最后的方法,以防万一别人是我瞎:如果添加生成的`sql`发布

def top(options = {}) 
    tname = self.name.tableize 

    inner_select = self.select("#{tname}.*,COUNT(DISTINCT(listens.id)) AS listens_count"). 
     joins("LEFT JOIN listens ON listens.song_id = songs.id"). 
     group("#{tname}.id") 

    inner_select = inner_select.joins(:songs) unless self.name == 'Song' 
    inner_select = inner_select.where("listens.user_id = ?", options[:for].id) if options[:for].is_a? User 

    inner_select = "(#{inner_select.to_sql}) as #{tname}" 

    select("#{tname}.*,#{tname}.listens_count,SUM(sources.popularity) AS popularity_count"). 
     from(inner_select). 
     joins("LEFT JOIN sources ON sources.resource_id = #{tname}.id AND sources.resource_type = '#{self.name}'"). 
     group("#{tname}.id"). 
     order("listens_count DESC, popularity_count DESC") 
end