Rails ActiveRecordでgroup_by countによる集計結果をrelationとして取得する

# 先に結論だけ
countメソッドを利用するとhashが返却されるため
selectメソッド内でmysqlのcount処理を記述

```ruby
articles =
  Article
    .joins(:comments)
    .select('articles.id, articles.category_id, count(comments.id) as comments_count')
    .group(:id)
    .order('comments_count desc')
```

# はじめに
RailsのActiveRecordを通してmysqlでcountした結果を取得しようと思いました。
その際、countメソッドを利用すると返却値がhashとなってしまったため
ActiveRecord::relationで取得する方法を模索します。

# 前提
ブログサイトをイメージして、以下のようなデータモデルを想定します。
## データモデル
![名称未設定ファイル.png](https://qiita-image-store.s3.amazonaws.com/0/71387/a18c41ad-2711-fdf1-22cb-c5c4af4ea04f.png)
### 各テーブルの説明
#### 記事(articles)
ブログ記事を表します。
#### カテゴリ(categories)
記事に付与されるカテゴリです。
この投稿記事だとRailsのようなものです。
モデルをシンプルにするために、記事はカテゴリを一つだけ持つことにします。
#### コメント(comments)
記事に付与されるコメントです。
一つの記事に対して複数のコメントがひもづきます。
### データ例
#### categories
| id               | name             |
|:-----------------|:-----------------|
| 1                | 音楽              |
| 2                | プログラミング     |
| 3                | 旅行             |
#### articles
| id               | category_id      | title                 | body             |
|:-----------------|:-----------------|:----------------------|:-----------------|
| 1                | 1                | 6月に言ったライブ         | ...              |
| 2                | 2                | rubyについて思うこと     | ...     |
| 3                | 1                | 6月に聞いた新譜一覧       | ...             |
| 4                | 2                | sidekiqを触ってみた              | ...              |
| 5                | 2                | enumerableを理解する     | ...     |
| 6                | 3                | 夏休みフランス旅行             | ...             |
#### comments
| id               | article_id      | body                 | user_id             |
|:-----------------|:-----------------|:----------------------|:-----------------|
| 1                | 1                | いいね         | 1              |
| 2                | 2                | なるほど     | 2     |
| 3                | 1                | 面白い       | 4             |
| 4                | 4                | 深いなぁ              | 5              |
| 5                | 3                | うける     | 9     |
| 6                | 3                | ナイス             | 10             |
| 7                | 1                | 勉強になる       | 8             |
# 今回取得したいデータ
## 概要
ActiveRecordを通じて **各記事ごとのコメント数ランキング** を取得します。
また、その際に

- 記事タイトル(articles.title)
- カテゴリ名(categories.name)

も取得します。

## 必要データ
| id               | category_name     | title                 | comment_count|
|:-----------------|:-----------------|:----------------------|:--------------|
| 1                | 音楽                | 6月に言ったライブ      |3|
| 3                | 音楽                | 6月に聞いた新譜一覧    |2|
| 2                | プログラミング        | rubyについて思うこと   |1|
| 4                | プログラミング        | sidekiqを触ってみた   |1|

こんなイメージです。

# まずは: count・sizeメソッドを使う
ActiveRecordを通じてcount文を発行するメソッドに countやsizeがあります。(どちらを使うべきかは他の記事で多々挙げられているので、そちらを参考のこと)
今回はcountメソッドを用いて集計をしてみます。

```ruby
counts = 
  Article
    .joins(:comments)
    .group(:id)
    .order('count_all desc')
    .count
```

発行されるクエリは以下の通りです。

```ruby
SELECT  
  COUNT(*) AS count_all, 
  `articles`.`id` AS articles_id 
FROM `articles` 
INNER JOIN `comments` 
  ON `comments`.`article_id` = `articles`.`id` 
GROUP BY `articles`.`id` 
ORDER BY count_all desc
```


返却されるデータは以下の通りです。

```ruby
{ 1=>3, 3=>2, 2=>1, 4=>1 }
```

ActiveRecord::relationではなくhashが返却されてしまったため、
記事タイトルおよびカテゴリ名を取得するには、
記事リレーションを取得するクエリを再発行することが必要になります。

```ruby
articles = Article.find(counts.map{|id, count| id})
```

# つぎに: selectメソッドを使う
あまり綺麗な方法ではないですが、
countメソッドを使う代わりにselectメソッドを用いて
明示的にselect文内で集約処理を実現します。

```ruby
articles =
  Article
    .joins(:comments)
    .select('articles.id, articles.category_id, count(comments.id) as comments_count')
    .group(:id)
    .order('comments_count desc')
```

発行されるクエリは以下の通りです。

```ruby
SELECT  
  articles.id, 
  articles.category_id, 
  count(comments.id) as comments_count 
FROM `articles` 
INNER JOIN `comments` 
  ON `comments`.`article_id` = `articles`.`id` 
GROUP BY `articles`.`id` 
ORDER BY comments_count desc
```

返却されるデータは以下の通りです。

```ruby
[
  #<Article: id: 1, category_id: 1>,
  #<Article: id: 3, category_id: 1>,
  #<Article: id: 2, category_id: 2>,
  #<Article: id: 4, category_id: 2>
]
```

Articleモデルのrelationが返ってきました。

コメント数を取得する場合は

```ruby
articles[0].comments_count
=> 3
```

カテゴリ名を取得する場合は

```ruby
articles[0].category.name
=> "音楽"
```

relationとして取得することができるようになりました。