Angelo Chrysoulakis
Angelo Chrysoulakis

Reputation: 946

Rails Counter Cache

I have a db schema with multiple counter caches. Rather than have the counter caches spread across many tables, I'd like to have a table called user_counters with all of the various counter caches. I'd update these caches from a service object with a method like this:

def update_user_counter(column, user_id)
  UserCounter.increment_counter(column, user_id)
end

But, the UserCounter record for the associated user will most likely not have the same ID as the user. What I'd like to do is something like this:

def update_user_counter(column, user_id)    
  UserCounter.increment_counter(column, UserCounter.where('user_id = "#{user_id}"')
end

Any thoughts as to how I can accomplish this? Thanks!!

Upvotes: 3

Views: 1998

Answers (1)

mikeryz
mikeryz

Reputation: 527

If these are 10 attributes associated with a User, is there any reason to not simply make them a part of the User model? If that is the case, the below code will still work (just make the method part of User instead of UserCounter).

If you want to implement it your way, I'd say first make sure that your user_counters table has an index on user_id (since Rails is dumb about adding indexes and FK's).

Then, you could do this (passing in "column" as a symbol):

class UserCounter < ActiveRecord::Base
  #other code

  def self.update_user_counter(column, user_id)
    counter = UserCounter.find_by_user_id(user_id)
    counter.update_attribute(column, counter.read_attribute(column) + 1)
    counter.save!
  end
end

And in your other model:

class SomeOtherModel < ActiveRecord::Base
  #Model code

  def update_counter(user)
    UserCounter.update_user_counter(:this_column, user.id)
  end
end

This solution (or using the update_counter method) both require two database hits, one for the lookup and one for the update. If you need the speed, you could write this directly in SQL:

query = "UPDATE user_counters SET #{column} = #{column} + 1 WHERE user_id = #{user_id};"
ActiveRecord::Base.connection.execute(query);

This assumes that user_id is unique on user_counters.

Upvotes: 2

Related Questions