はしばみあきら blog

プログラミングアウトプットするブログ。202010スタート

【Rails】groupメソッドとorderメソッドを一緒に使う

今回はSQLとのやりとりで沼った、groupとorderを一緒に使う時の注意をつらつらと書いていきます

現在の自身の環境です

sqlRailsのデフォルト

  • gem 'sqlite3'

あと、binding.pry大好き人間なので

  • gem 'pry-byebug', group: :development

を導入

やりたいこと

ユーザーモデルがあり、トークルームモデルがあります

中間テーブルとして、ルームユーザー(どのルームにどのユーザーがいるか)と、トーク(メッセージがどのユーザーのものか)があります

f:id:hashibamiakira:20201019122347p:plain

今回やりたいことは、ラインのトークを押すと、最近トークが追加されたトークルーム順で並べる、というものです

実際のラインの表示

f:id:hashibamiakira:20201031115525p:plain

ちょっとわかり辛いですが、もしみられる方は自身の物を参考にしてもらえればと思います...

始めに行ったこと

最初に、まずTalkの全てを作成順 (.order(created_at: :desc))で並び替え、重複する talk_room_id でまとめる、ということをシンプルに記述してみました

latest_talk = Talk.order(created_at: :desc).group(:talk_room_id)

f:id:hashibamiakira:20201031121105p:plain

一見取得できているように見えたのですが、実はgroupメソッドの方が先に働いてしまうようです

動きとしては、

グループで同じtalk_room_idでまとめる(この時点で一番古いidでまとまる)
 ⇩
まとめたレコードで、新しい順に並べる

なので、最初に送ったトークを基準にルームを並べるという動きになってしまいます

全く意味がなくなってしまうので色々調べてみました

サブクエリ(from)を使う

fromを使うと、その中の記述が先に動いてくれるようです

実際に書いてみます

Talk.from(Talk.order(created_at: :asc)).group(:talk_room_id)

f:id:hashibamiakira:20201031121834p:plain

#<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)

f:id:hashibamiakira:20201031122339p:plain

トークを並び替えてルーム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と一致するルームの情報を入れていきます

f:id:hashibamiakira:20201031122828p:plain

これでやっとview側でデータを使うことができます。やったね

もうちょっと簡潔にする方法があるとは思いますが、当初の目的は達成できました

まとめ

groupとorderを使うときのまとめです

  • groupとorderを使うと、groupの方が優先される
  • もし2つを使うのであれば、fromを使うことで解決出来る
  • 取得したデータはActiveRecord::Relationという形になる

こんな感じですかね〜

2日ほど沼って解決できたので忘備録意味合いも込めて残しておきます

またボチボチと更新していきます