Ruby on Rails歴1年半のビギナーが最近覚えたデータの抽出（参照）方法

# 前提
Ruby on Railsを使い始めて、1年半ほど経ちました。
今まではモデル数が5つ以下のシンプルなアプリしか触ってこなかったのですが、最近10以上のモデル数のアプリを触ることが多くなり、手詰まり感を感じ始めてきました。
 
# 今回取り上げるテーマ
## すぐ書ける？ 条件に沿ったデータ抽出の書き方
知っている人はこのメソッドで取得すれば一発！なのですが知らないことで、データの抽出だけに多大な時間をかけてしまい、、その上結局ガリガリとSQLを書くという可読性の低いコードができてしまい、、なんともRailsの良いとこを全く生かしていない書き方になってしまっていました。
その状況を脱するために最近学んだことをまとめておくことにしました。

# とりあえず`current_user`使えばいいでしょ。今までの知識レベル
データ抽出を書く場面に出くわした場合、チームの他の人のコードを疑いもなく真似し、例えば**ログイン中のユーザーのコメント一覧**を取得するというお題の場合、特に意識することもなく

- current_user.comments という書き方
- Comment.where(user_id: current_user.id) という書き方

という感じでとりあえず取りたいデータが取れればいーじゃんということにしか意識が向いていませんでした。
 
参照方法によってパフォーマンスに影響を与えるなんて考えもせず、とりあえず**取りたいデータが取れればいーじゃん**ということにしか意識が向いていませんでした。

仮にこの発展形で、コメントに子となるテーブルが存在してるようなレコードを抽出してそれを１つづつ表示する・・・というケースの場合に、書き方によってはN+1 問題につながるというか、そもそもN+1も言葉しか知らないレベルでした

そんなレベルだと前述の通り、複数テーブルのデータをいい感じに表示したい状況になった場合、うーんと頭を抱えてしまい数日が過ぎていたのです。。あいたた〜|д･)

# 最近学んだこと
ということで、最近学んだ抽出方法を例を使いながら挙げていきます。
### 
[サンプルアプリ](https://github.com/kaorina/jpaddress-rails)はこちら

**モデル一覧**

- 都道府県一覧：Prefectureモデル
- 市区一覧：Cityモデル
- 町村一覧：Townモデル


**アソシエーション（関連）**

```ruby:
class Prefecture < ActiveRecord::Base
  has_many :cities
end
```
```ruby:
class City < ActiveRecord::Base
  belongs_to :prefecture
  has_many :towns
end
```
```ruby:
class Town < ActiveRecord::Base
  belongs_to :city
end
```

### 1. 親子関連を定義しているデータの抽出

**親から子のデータを取得する場合**

#### Q. 東京都(id=13)に属するCity一覧を取りたい

```ruby:
Prefecture.find(13).cities
``` 
#### Q. 千代田区(City.first, id=13101)に属するTown一覧を取りたい

```ruby:
City.first.towns
``` 
#### Q. 東京都(id=13)に属するTown一覧を取りたい場合

```ruby:
city_ids = City.where(prefecture_id: 13).pluck(:id)
Town.where(city_id: city_ids)
```
考え方: まず東京都(id=13)に属するCity一覧を取得し、次に各Cityに紐づくTownを取得する。
ActiveRecordのメソッド[pluck](http://railsdoc.com/references/pluck)を使うと必要なカラムのデータのみ取得し、配列にしてくれる。
 
もちろん、 `city_ids`変数に入れずに、ワンライナーで書いてもOK！
  
また、上記の例で既出だがwhereのカラムの条件値に**配列**を指定することもできる〜。
純粋なSQLではINを使っていたので、これは便利！

```ruby:
Town.where(city_id: [13101, 13102, 13103])
```

**子から親のデータを取得する場合**
子が一意に決まっているので、簡単に取れる

#### Q. あるTown（1番目のTown）に紐づく、CityとPrefectureをとる場合

```ruby:
Town.first.city
Town.first.city.prefecture
```

### 2. 件数を取得する


#### Q. 東京都（id=13）に属するCityの件数

```ruby:
City.where(prefecture_id: 13).count
```
countでなくsizeメソッドを使っても同じ結果になる。
 
 
#### Q. 東京都に属するTownの件数

**合計の件数**

```ruby:
Town.where(city_id: City.where(prefecture_id: 13)).count
```

`city_ids = City.where(prefecture_id: 13)` と変数に入れて、↑の `city_id: city_ids` としてもOK。


**個別の件数**

```
A区: xx件
B区: xx件　みたいに表示したい
```

- スタンダードな方法

```ruby:
cities = City.where(prefecture_id: 13)
cities.each { |city| puts "#{city.name} : #{city.towns.count}件" }
```
 
- group句を使った方法

```ruby:
city_ids = City.where(prefecture_id: 13).pluck(:id)
Town.where(city_id: city_ids).group(:city_id).count
```

結果
`{[city_id] => [キーのcityのtown数], ...}` のハッシュの形で返ってきてしまった。。
 
結果を**加工**することが必要！
ここでRubyのメソッドを使ってみよう。Hashクラスの[each](http://ref.xaio.jp/ruby/classes/hash/each)メソッドを使う

```ruby: 
town_counts = Town.where(city_id: city_ids).group(:city_id).count
town_counts.each { |city_id, town_count| puts "#{City.find(city_id).name} : #{town_count}件" }
```
 
これで希望通りの表示ができた！

### 3. ActiveRecordのメソッドだけで無理に完結しなくていいんだよ（Rubyを使おう！）

欲しいデータを取得するためには、ActiveRecordのメソッドだけを使って抽出するだけでなく、Rubyのメソッドを使って抽出した結果を加工するという技もあるのです！
（↑の最後の例がその一つ）
 
Railsビギナーな私は、ついついActiveRecordのメソッドを上手に組み合わせて抽出したい！！という頭しかなかったため、時間をかけて気づけば長々としたSQL文を書いてしまい、後で見たときにこれ何してるの？と自分のコードでもなることがしばしばあったり。。
というのも今までRubyを差し置いて、Ruby on Railsの学習しかしてこなかったので、Rubyの文法やメソッドは正直知らない。。
なんともったいない！Rubyが使えるようになると、ぐっと世界が広がってきた気がします。
では使ってみましょうヽ( ´∇`)ﾉ
<br>

**今までのお題をちょっと応用して**
#### Q. 東京都に属するCityの中で、**23区のCity**と**23区以外のCity**一覧を取得する
 
- ActiveRecordのメソッドを使ったスタンダードな方法

23区のCity一覧 
どれが23区のCityか分からないので、文字列検索を使って抽出してみる

```ruby:
tokyo_metropolitan_cities = City.where("prefecture_id = ? AND name LIKE '%区'", 13)
``` 
ちなみに文字列のみでの検索の場合、
直接値を記述せずに疑問符?を使う方がセキュリティ上良いらしいです。知らなかった〜。[参考リンク](https://railsguides.jp/active_record_querying.html#%E6%9D%A1%E4%BB%B6)
 
23区以外のCity一覧

```ruby:
other_cities = City.where("prefecture_id = ? AND name NOT LIKE '%区'", 13)
``` 
結果は無事取れました！が、同じようなSQLを2回発行した感じですね〜。
<br> 

- Rubyのメソッドを使った方法

```ruby:
tokyo_metropolitan_cities, other_cities = City.where(prefecture_id: 13).partition { |city| city[:name].match(/.+区/) }
```
 
ワンライナーで取れました！
`tokyo_metropolitan_cities`には23区のCity一覧が、`other_cities`にはそれ以外の東京都のCity一覧が入っています。

条件によって結果を二つの配列に格納するEnumerableクラスの[partition](http://ref.xaio.jp/ruby/classes/enumerable/partition)、正規表現を使った文字列の検索を行う[match](http://ref.xaio.jp/ruby/classes/string/match)メソッドを使いました！
詳しい使い方はリンク先のページを参照してください。
特にpartitionメソッド、かなり使えそうですね〜。
 
## まとめ（なぜこれらを知っておくことが重要なのか）
- データの参照はController、Modeと様々な箇所で必要になる。毎日のように書く。なのできちんとした方法を覚えておくべき
- 参照の記述を適当にしているとアプリ全体のパフォーマンスに関わり、バグの要因になる（と思う）
- 書き方を広く知っておくことで自分の開発効率が格段にアップする

まだ学習途中ですが、これからも様々なコードを見て、自分の使える知識を増やしていきたいと思います。
参照周りでも、scopeなど始め使いこなせるようになるべき知識が数多！
そして、じわりじわりと必要性を感じているRubyも学んでいこうと思います╭( ・ㅂ・)و