技術メモ

書いておぼえるブログ

【Active Record】カウントを集計してランクづけしたい

状況  

任意の期間における合計カウントにもとづいてレコードをランク付けしたい。カウントは、アクセス数、販売数などを想定している。これを先週、先月、去年などの単位で集計する。

前提  

テーブル  

カウントがあるテーブル

create table  "pages" do |t|
  t.integer access_count
  t.date     date
end

ランキングテーブル

create table "rankings" do |t|
  t.integer page_id
  t.integer rank

アソシエーション

class Page << ActiveRecord::Base
  has_many :ranking
end
class Ranking << ActiveRecord::Base
  belongs_to :page
end

解決  

Active RecordsのgroupとMySQLの日付操作関数を組み合わせる。  

コード

レコードの取り方とそれに対して何か処理したいときどうするか。

取得

先週のカウントを合計してみる。  

selectを使うとリレーションで返ってくる。なので戻り値にメソッドをチェーンできる。firstとかlimitとかできる。selectするカラムにつけたエイリアスでorderできる。

Page.select('id, SUM(access_count) AS sum')
  .where('YEARWEEK(date) = YEARWEEK(CURRENT_DATE())-1')
  .group(:page_id)
  .group('YEARWEEK(date)')
  .order('sum desc')

pluckを使うと配列で返ってくる。

Page
  .where('WEEK(date) = WEEK(CURRENT_DATE())-1')
  .group(:page_id)
  .group('WEEK(date)')
  .pluck('SUM(access_count)')

ランク付け

さっきの方法で取ったレコードをループして、ループのインデックスをrankとして挿入する。

pages = Page.select('id, SUM(access_count) AS sum')
  .where('YEARWEEK(date) = YEARWEEK(CURRENT_DATE())-1')
  .group(:page_id)
  .group('YEARWEEK(date)')
  .order('sum desc')

pages.each.with_index(1) do |page, rank|
  page.update_columns ( rank: rank)
end

ループのコード実際にためしてないのでなんかおかしい可能性がある。 こんだけでランクがつけられる。each.with_indexのインデックスは0はじまり1位からとなるように引数を渡しておく。その他注意としてeachにドットでwith_indexする。each_with_indexだと思って間違えた。  

すっきりさせる

期間の条件をscopeにまとめてやるとよりいい感じになりそう。読みやすくメンテしやすくなる。  

たとえば、週次、月次の条件を書いたりできる。dateを引数にわたす。

先週

scope :lastweek, -> (date) {
  where('WEEK(date) = WEEK(CURRENT_DATE())-1')

先月

scope :lastweek, -> (date) {
  where('MONTH(date) = MONTH(CURRENT_DATE())-1')
}

いっそのことすべての条件をまとめればよいとおもう。order_by_lastweek_access_count だ。

scope :order_by_lastweek_access_count, -> (date) {
  where('MONTH(date) = MONTH(CURRENT_DATE())-1')
 .group(:page_id)
 .group('YEARWEEK(date)')
 .order('sum desc')
}

すると先ほどのコードはこうなる。

pages = Page.select('id, SUM(access_count) AS sum').order_by_lastweek_access_count(date)

このコードも動作確認してない。あとで確かめる。

参考  

こちらの記事がたいへんわかりやすく参考になった。このページは、本記事の内容を個人的に練習したみた結果を書いている。 blog.scimpr.com