【问题标题】:rails ordering by has many relationrails order by 有很多关系
【发布时间】:2014-05-27 16:35:35
【问题描述】:

我正在尝试按每个音乐会的评论数量来排序我的音乐会。音乐会有很多评论。试过这样做

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

但我收到此错误

未定义的方法“审查” ActiveRecord::Relation::ActiveRecord_Relation_Concert:0xb5540398>

我有什么建议可以参考每个订购音乐会的评论数量吗?

【问题讨论】:

  • 未经测试,但这应该可以工作:Concert.joins(:reviews).group('reviews.concert_id').order('COUNT(reviews.id)')
  • 不起作用:错误:列“concerts.id”必须出现在 GROUP BY 子句中或用于聚合函数第 1 行:SELECT“concerts”。* FROM “concerts”INNER JOIN “评论”开启...

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


【解决方案1】:

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

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

【讨论】:

  • 谢谢你。我实际上是在 ERB 视图中使用它进行评论。我会考虑让它成为 Concert 的一种方法
  • 它需要进入 Concert 控制器,对吧?我还应该注意,我只想为 10 场音乐会做这件事,但这并不重要,因为我必须在显示前 10 场之前将所有音乐会都组织好
  • @user2739431 好吧,添加.limit(10) 或一些.where 子句参数之类的内容应该不会有任何问题,但是您打算将它们限制为~10。将会发生的事情是对整个事情进行排序,然后通过您指定的任何.wherelimit 参数进行过滤。至于你的问题需要进入 Concert 控制器,不是 100% 确定你的意思,你能详细说明一下吗?我将编辑问题并添加一个示例,说明我的意思是查询应该去哪里,如果这就是你的意思。
【解决方案2】:

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

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

【讨论】:

  • 所以这对大多数评论都有效,对大多数评论来说如何?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多