group_by を group + array_agg で高速化する(PostgreSQL)
こういうパフォーマンス改善もあるよメモ
ユーザーと記事があり、ユーザーは記事をお気に入りできる。お気に入りには種類がある。お気に入りの種類ごとに記事のidの配列を取得するような処理を考える。
1{
2 "like_name_1" => [1,2,3,4],
3 "like_name_2" => [5,6,7,8],
4}
最終的にはこんな感じのデータを取りたい
group_by を使う
1Like
2 .where(user_id: user.id)
3 .select(:name, :post_id)
4 .group_by(&:name)
5 .transform_values { |v| v.pluck(:post_id) }
group_byでDBの結果を取得しtransform_values以降をRailsで処理することになる。数が多い場合はパフォーマンスが気になる
group + array_aggを使う
集約関数のarray_aggを使う
NULLも含めてすべての入力値を収集して配列に格納します。
1Like
2 .where(user_id: user.id)
3 .group(:name)
4 .select(:name, "array_agg(post_id) as post_ids")
5 .to_h{ |g| [g.name, g.post_ids] }
こうすることでgroupの処理をDB側に寄せることができる。ただし、可読性の低下や、PostgreSQLに依存した実装になってしまうためそのあたりのデメリットは考慮すること。
ちなみにMySQLの場合はGROUP_CONCATが使えそう