导轨排序有很多关系

时间:2014-04-13 20:11:15

标签: ruby-on-rails-4 has-many database-relations

我试图通过每个评论的数量来订购我的音乐会。音乐会has_many评论。试过这样做

<% @concerts = Concert.all %>
<% @concerts.order(reviews.size).each do |concert| %>
  -
  -
  -
<% end %>

但我收到此错误

  

未定义的方法`评论&#39;对于   ActiveRecord的::关系:: ActiveRecord_Relation_Concert:0xb5540398&GT;

有什么建议我会如何参考每个音乐会的评论数量?

2 个答案:

答案 0 :(得分:3)

不是最好的,但最简单的解决方案是

@concerts = Concert.all.includes(:reviews).sort_by{|concert| concert.reviews.size}.reverse

答案 1 :(得分:2)

另一个答案的替代方案,最终会给你相同的结果集,但副作用会略有不同:

Concert.select("concerts.*, count(reviews.id) review_count")
       .joins("LEFT JOIN reviews ON concerts.id = reviews.concert_id")
       .group("concerts.id")
       .order("review_count")

主要区别在于此查询在使用之前不会立即执行;您将收到一个活动记录关系,就像您在使用任何活动记录查询方法时一样,这意味着您可以进一步细化或添加到查询(假设说)。

另一个区别是此查询不需要急切加载reviews。根据您的需要,如果您不需要相关评论中的任何信息,此查询将以相当快的速度运行。

就时间/性能而言,使用50个音乐会和43867个评论的数据库(FK上存在索引),两个版本似乎在大约相同的时间内执行(带有急切的加载)。这是我的基准测试结果表(所有结果都以秒为单位):

| # | Sory_by Query | Pure AR Query | No eager loading |
--------------------------------------------------------
| 1 | 2.899806      | 2.840702      | 0.02164          |
| 2 | 3.150556      | 2.818374      | 0.21612          |
| 3 | 2.812867      | 3.025921      | 0.016368         |
| 4 | 3.069562      | 3.055307      | 0.016884         |
| 5 | 2.862722      | 2.680357      | 0.021316         |
|---|---------------|---------------|------------------|
 AVG: 2.9591026     | 2.8841322     | 0.0584836        |

正如您所看到的,使用预先加载的两个查询之间没有显着差异,并且使用我上面的查询而没有急切加载的主要区别。显然,确切的结果对您来说会有所不同,但这可以让您了解相对差异。

旁注: 根据您在问题中发布的内容,您似乎想要在ERB视图中编写此查询。我强烈建议将此查询移动到Concert模型中的方法,并从控制器中该方法的返回创建实例变量,然后视图可以使用该变量。这样你就可以保持一切美好和分离。

修改 为了说明我的建议,我将查询放在Concert模型的类方法中,如下所示:

def self.ordered_by_reviews
    return select("concerts.*, count(reviews.id) review_count")
          .joins("LEFT JOIN reviews ON concerts.id = reviews.concert_id")
          .group("concerts.id")
          .order("review_count")
end

您可以从控制器呼叫(与哪个控制器无关):

... other controller code:
@concerts = Concert.ordered_by_reviews
... and so on

然后,您可以根据需要使用@concerts,并删除@concerts = Concert.all等内容。

或者,您也可以使用范围来做同样的事情(我相信它会被认为更多Rails-y):

class Concert < ActiveRecord::Base
    scope :ordered_by_review, -> { select("concerts.*, count(reviews.id) review_count").joins("LEFT JOIN reviews ON concerts.id = reviews.concert_id").group("concerts.id").order("review_count") }
    ... rest of class