【Rails】groupメソッドとorderメソッドを一緒に使う
今回はSQLとのやりとりで沼った、groupとorderを一緒に使う時の注意をつらつらと書いていきます
現在の自身の環境です
- gem 'sqlite3'
あと、binding.pry大好き人間なので
- gem 'pry-byebug', group: :development
を導入
やりたいこと
ユーザーモデルがあり、トークルームモデルがあります
中間テーブルとして、ルームユーザー(どのルームにどのユーザーがいるか)と、トーク(メッセージがどのユーザーのものか)があります
今回やりたいことは、ラインのトークを押すと、最近トークが追加されたトークルーム順で並べる、というものです
実際のラインの表示
ちょっとわかり辛いですが、もしみられる方は自身の物を参考にしてもらえればと思います...
始めに行ったこと
最初に、まずTalkの全てを作成順 (.order(created_at: :desc))で並び替え、重複する talk_room_id でまとめる、ということをシンプルに記述してみました
latest_talk = Talk.order(created_at: :desc).group(:talk_room_id)
一見取得できているように見えたのですが、実はgroupメソッドの方が先に働いてしまうようです
動きとしては、
グループで同じtalk_room_idでまとめる(この時点で一番古いidでまとまる)
⇩
まとめたレコードで、新しい順に並べる
なので、最初に送ったトークを基準にルームを並べるという動きになってしまいます
全く意味がなくなってしまうので色々調べてみました
サブクエリ(from)を使う
fromを使うと、その中の記述が先に動いてくれるようです
実際に書いてみます
Talk.from(Talk.order(created_at: :asc)).group(:talk_room_id)
#<Talk::ActiveRecord_Relation:0x2bff408> という結果が帰ってきました
こいつはなんぞや、という話ですが、他の方の記事を流用させてもらうと
- データベースにクエリを発行する。
- 様々なデータベースとの互換性がある。
- データベースの種類にかかわらず同じ表記を使用できる。
ということらしいです
なるほど?
なので色々と追加してみます
latest_talk = Talk.from(Talk.order(created_at: :asc)) .group(:talk_room_id) .select(:talk_room_id) .order(created_at: :desc)
トークを並び替えてルームidでまとめた後、ルームidを指定しorderで並び替えます
これで一旦は、自分が求めていた「トークが追加されたルーム順で並べる」ということができました
ただこの状態ではただidを引っ張っただけでどうにもならないので、eachで回し配列にデータを追加します
# トークが新しい順にトークルームを並べる latest_talk = Talk.from(Talk.order(created_at: :asc)) .group(:talk_room_id) .select(:talk_room_id) .order(created_at: :desc) @latest_talks = Array.new latest_talk.each do |talk| room = TalkRoom.find_by(id: talk.talk_room_id) @latest_talks.push(room) end
変数に一旦新しく配列を持たせ、その中にidと一致するルームの情報を入れていきます
これでやっとview側でデータを使うことができます。やったね
もうちょっと簡潔にする方法があるとは思いますが、当初の目的は達成できました
まとめ
groupとorderを使うときのまとめです
- groupとorderを使うと、groupの方が優先される
- もし2つを使うのであれば、fromを使うことで解決出来る
- 取得したデータはActiveRecord::Relationという形になる
こんな感じですかね〜
2日ほど沼って解決できたので忘備録意味合いも込めて残しておきます
またボチボチと更新していきます