Ruby on Rails で日付ごとに評価データをグループ化する最良かつ最もパフォーマンスの高い方法は何ですか?
概要
タスク テーブルがあり、完了したタスクごとに、ユーザーはタスクを評価できます (1 から 5)。
タスク テーブルは次のようになります。
tasks
id: integer
closed_at: datetime
rating: integer
次に、次のような形式で日付ごとの評価を取得する API 応答を構築する必要があります。
{
"2023-02-23": [
{ rating: 1, count: 1 },
{ rating: 2, count: 5 },
{ rating: 3, count: 2 },
{ rating: 4, count: 30 },
{ rating: 5, count: 15}
],
"2023-02-24": [
{ rating: 1, count: 2 },
...
]
}
これは単なる例です。私が望むのは、日付ごとのすべての評価数を取得することです。 上の例では、2023 年 2 月 23 日に完了したタスクが 1 つあり、評価が 1、同じ日に完了したタスクが 5 つあり、評価が 2 です…
Rubyでそれを行う最善の方法は何でしょうか? それに対する SQL ソリューションを見つけようとしていましたが、それが可能かどうかさえわかりません。
それ以外の場合は、すべてのタスクを取得した後で Ruby コードのみを使用する最もパフォーマンスの高い方法を喜んで採用します。
私はすでにこの実用的なソリューションを持っています:
grouped_tasks_per_date = Task.all.group_by { |t| t.closed_at.to_date.to_s }
data = {}
grouped_tasks_per_date.each do |date, tasks|
tasks_per_rating = tasks.group_by{|t| t.rating}
data[date] = tasks_per_rating.map {|k, v| [k, v.length] }.to_h
end
data
=> {"2020-12-01"=>{1=>1, 2=>5, 3=>2, 4=>30, 5=>15}}
私が受け取った上記の形式はまったく問題ありませんが、私の好みに合わないループを多用しすぎています…それで…コードを改善するアイデアやその他の方法がある場合は、アイデアを共有してください。
ありがとう!
解決策
Model.all を実行したことがある場合は、テーブルが常に非常に小さいことを知っているか、より良い方法があるかのどちらかです。
SQL でグループ化、カウント、並べ替えを実行します…
select date_trunc('day', closed_at), rating, count(*)
from foo
group by 1, 2
order by 1, 2
1 と 2 は、最初と 2 番目に選択された列、date_trunc(‘day’, Closed_at) と Rating を参照します。
Closed_at を日付に正確に変換する方法は、使用している SQL データベースによって異なります。 Postgres の場合は date_trunc、MySQL の場合は day。
デモンストレーション。
同等の Rails では、group、order、count が使用されます。
grouped = Task
.select("datetrunc('day', closed_at)", rating)
.group(1, 2)
.order(1, 2)
.count
通常と同じように、特定の日に限定することもできます。
これにより、次のような配列のハッシュが得られます。
{[“2023-02-23”, 1] => 1, [“2023-02-23”, 2] => 5, …}