【Rails】いいねが多い順に並び替え
本の投稿サイトの投稿一覧ページに、過去一週間でいいねの合計カウントが多い順に投稿を表示させます!
実装にあたり、以下記事も参考にしてくださいね。
では実装していきます。
Model追記
Bookモデルに以下を記載します。
has_many :week_favorites, -> { where(created_at: 1.week.ago.beginning_of_day..Time.current.end_of_day) }
解説:
Bookモデルに対してweek_favoreites
という関連付けを追加します。
この関連付けをすることで、1週間以内のいいねを取得します。
-
has_many
BookモデルとFavoriteモデル間に1対多の関係にします。 -
:week_favorites
関連付けの名前を定義します。 -
-> { ... }
ラムダ(Lambda)または無名関数を表す構文です。関連付けに特定の条件を指定することができます。関連するデータを絞り込んだり、条件を満たすデータのみを取得するために使います。 -
where(created_at: 1.week.ago.beginning_of_day..Time.current.end_of_day)
関連するデータを絞り込むための条件です。created_at
カラムが指定した期間に該当するデータのみを取得します。
1.week.ago.beginning_of_day
は、1週間前の開始日時を表し、Time.current.end_of_day
は現在の終了日時を表します。
コントローラ修正
indexを以下のように修正します。
ここでは二通り紹介します。
- sort_byを使った実装
def index
to = Time.current.at_end_of_day
from = (to - 6.day).at_beginning_of_day
@books = Book.includes(:favorites).sort_by { |book| -book.favorites.where(created_at: from...to).count }
@book = Book.new
end
解説:
-
to = Time.current.at_end_of_day
現在の日時を基準にして期間の終了日を設定します。 -
from = (to - 6.day).at_beginning_of_day
終了日から6日前の日時を開始日として設定します。これにより、1週間の期間が設定されます。
例えば、今が7月10日の場合、「to」には7月10日の23時59分59秒が設定されます。「(to - 6.day)」で、6日前の7月4日の23時59分59秒を取得します。「at_beginning_of_day」によって2023年7月4日の0時0分0秒に変換します。 -
@books = Book.includes(:favorites)
N+1問題を解決するために Book モデルに関連するいいね情報を一括で取得するために使用します。 -
.sort_by { |book| -book.favorites.where(created_at: from...to).count }
各書籍のいいね数を降順にソートするための処理です。期間内に作成されたいいね情報を取得し、その数でbookをソートしています。
文法解説
-
.sort_by
要素の並び替えを行うためのメソッド。ブロックを受け取り、ブロック内の式の結果に基づいて要素をソートする。 -
|book|
ブロック内で使用する変数で、ソートの対象となる要素(book)。 -
book.favorites.where(created_at: from...to)
特定の期間内に作成されたいいね情報のクエリを実行。book の関連するいいね情報を取得し、その中から指定した期間内のものを抽出しています。 -
.count
クエリ結果の行数(いいねの数)を返すメソッド。book.favorites.where(created_at: from...to)
の結果に対して行数を数えます。 -
-
マイナス符号を使用することで、降順(大きい順)にすることができる。
- sortを使った実装
def index
to = Time.current.at_end_of_day
from = (to - 6.day).at_beginning_of_day
@books = Book.all.sort {|a,b|
b.favorites.where(created_at: from...to).size <=>
a.favorites.where(created_at: from...to).size
}
@book = Book.new
end
解説:
-
@books = Book.all
Bookモデルからすべてのbookを取得し、@booksインスタンス変数に格納。 -
sort {|a,b| ... }
:sort
bookをいいねの数でソート。a
とb
はそれぞれ比較対象のbookを表す。 -
b/a.favorites.where(created_at: from...to).size
b/aのいいねの数を取得。where
メソッドでcreated_at
の範囲を指定し、その結果の数を取得する。 -
<=>
比較演算子で、左辺と右辺の値を比較。この場合、b.favorites.where(created_at: from...to).size
とa.favorites.where(created_at: from...to).size
のいいねの数を比較する。
sortの働き:
例えば以下のようなデータがあったとします。
book | いいね数 |
---|---|
book A | 5 |
book B | 8 |
book C | 3 |
いいね数が大きい順にbookを並び替えます。
sortを使うと次の処理がされます。
初めに、a
にはbook A、b
にはbook Bが割り当てられ、以下の比較が行われます。
5 (book Aのいいね数) <=> 8 (book Bのいいね数)
この比較結果はマイナス(-1)です。a
(book A) のいいね数が b
(book B)のいいね数よりも少ないため、順序は変わりません。
a <=> b の計算結果は、以下のルールに基づいて決定します。
左辺の値が右辺の値よりも小さい場合、比較結果はマイナスの値(-1)。
左辺の値が右辺の値と等しい場合、比較結果はゼロ(0)。
左辺の値が右辺の値よりも大きい場合、比較結果はプラスの値(+1)。
次に、a
にはbook B、b
にはbook Cが割り当てられ、以下の比較が行われます。
8 (book Bのいいね数) <=> 3 (book Cのいいね数)
この比較結果はプラス(+1)です。a
(book B)のいいね数が b
(book C)のいいね数よりも多いため、順序が逆転します。
最終的に、bookを大きい順に並び替えると、以下の通りです。
book B > book A > book C
このように、a
と b
を使って比較することで、いいね数の多い順に並び替えることができます。
完成
-
Before
-
After
参考
並び替えはいろいろな書き方ができそう。
以下のような書き方もあるようなので参考までに。
いいねをしたユーザーを経由してbookをソートする、というやり方です。
続きはこちら。
Discussion