LoginSignup
0
0

More than 1 year has passed since last update.

【Rails】to_jsonを使わずに1クエリでいい感じにデータを返したい

Last updated at Posted at 2021-09-28

include

to_jsonには便利なオプションが沢山ある.特にinclude.多対多のデータをモデルの要素に配列として返してくれて便利.

例えば,fooとbarそれぞれのモデルが多対多の関係にあるとして

JSON.parse(Foo.queries.to_json(include: :bars))

とすれば,

[{id: 1, bars: [{id: 1}, {id: 2}]}]

ただ,一対多なら+1, 多対多のテーブルなら中間テーブルを足して+2多くクエリーが発行される.まとめて取得したいテーブルが増えると線形に増えていってしまう.

1クエリーで同じ様に返すには?

なんか他にいいメソッドないかな...と思ったがない.ので書いてみる.モデルは上と同じ.DBはMYSQL.

foo.rb
require 'hashie'
require "securerandom"

# 色々

scope hoge, -> () do
separator = SecureRandom.hex(12)

left_outer_joins(:bars)
# .queries
.select("
  foo.ids,
  foo.names,
  GROUP_CONCAT(bars.id separator '#{separator}') AS bar_identifications,
  GROUP_CONCAT(bars.name separator '#{separator}') AS bar_names
")
.group(:id)
.map {|f|
  Hashie::Mash.new({
    **f.attributes,
    bars: [
      f.bar_identifications&.split(separator) || [],
      f.bar_names&.split(separator) || []
    ].transpose.map {|row| [:id, :name].zip(row).to_h}
  })
}
end

ポイント

  • 左外部結合する.
  • GROUP_BY(group(:id))とGROUP_CONCATで関連モデルのカラムの値を文字列に連結して返す.
  • separatorは値とかぶらなければよいのでランダムな文字列.
  • .<関連モデル>_idsはRailsのメソッドして定義済みなのでidentificationsに変えている.
  • Hashie::Mash.newを使うことでコントローラーやビューでモデルと同じ様にドット記法が使える.
  • **f.attributesで要素を展開
  • &.|| []は値がnilだった場合用
  • bars: [{id: 1}, {id: 2}]のようにするために[[id1, id2], [name1, name2]]のような二次元配列を転置させて,それぞれの要素にカラム名を加えてhashにする.

デ/メリット

メリット
・どんなデータが返ってくるか一目でわかる
・加工しやすい

デメリット
・コードがやや長い

他に何かよい方法があったら教えて下さい.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0