views:

54

answers:

1

I need to implement product pages visits statistics (by days). How better to do this in Rails?

+1  A: 

The quick and dirty way to build something like this within Rails is to make a tracking table:

create_table :product_views do |t|
  t.integer :product_id, :null => false
  t.date :viewed_at, :null => false
  t.integer :views, :null => false, :default => 1
end

add_index :product_views, [ :product_id, :viewed_at ], :unique => true
add_index :product_views, :viewed_at

The unique index will mean you have one entry per product per day. There will be missing entries for days with no views for that product. It also means you can use the ON DUPLICATE KEY feature of MySQL if that's your back-end database.

You can populate this using a single SQL call:

class Product
  def self.record_view!(product_id)
    connection.execute(
      sanitize_sql([
        "INSERT INTO product_views (product_id, viewed_at)
          VALUES (?, CURRENT_DATE())
          ON DUPLICATE KEY UPDATE views=views+1",
        product_id
      ])
    )
  end

  def record_view!
    self.class.record_view!(self.id)
  end
end

This can be called at any time and will add 1 to your counter table. You can report on this quite easily using standard SQL.

tadman
And also can to write statistic to memcached or redis and copy it db at midnight, what do you think about it?
alexandr
I'd only go to such lengths if you're getting killed on the DB side of things. Usually an `INSERT` call like this happens in near zero time, so it takes thousands to be measurably slow. Where you'll hit a bottleneck is not in recording the data, but in grouping it together into weekly or monthly reports. You'll want to make a summary table for those kind of derivative values and perhaps update it once a night. Memcached is only really necessary for the 100+ requests a second kind of sites, but even then MySQL can keep up quite well if properly tuned.
tadman
And how can i group visits by months and years?
alexandr
You need to create alternate columns for these, then index them specifically. For instance, a `DATE` column with the first of the month, and a `year` column as an integer.
tadman
Thanks for your help! And how i can to select statistics from databse, including "zero"- days?
alexandr
Zero days don't really exist in the database so you will have to expand those after fetching those that do exist. You can usually do this by pre-generating a calendar array, then matching up values that you've fetched.
tadman