Rails counter_cache Balance
-
04-06-2021 - |
Question
Currently I am calculating a users balance by
def balance
transactions.where('txn_type = ?', Transaction::DEPOSIT).sum(:amount) -
transactions.where('txn_type = ?', Transaction::PURCHASE).sum(:amount)
end
I am running a query that subtracts adds all the users deposits and subtracts all their purchases. This will not scale well when there are thousands of transactions. What would be the best approach to calculating a users balance? Is there a way to customize counter_cache to calculate this per user?
Solution
Yes, you can specify :counter_sql
which will be used to set the counter_cache
.
The rails guide goes into detail about it.
OTHER TIPS
I would suggest two solutions for this
- Simple
First add balance
column to the User model and
Add callback inside Transaction model to update the user's balance based on transaction type.
- Complex
If you think you'll also need deposit_amount
and purchase amount
that too without wasting more time then counter_culture
is what you need here.
counter_culture
is somewhat similar to rails counter_cache but allow us to do various customizations.
This will involve following changes:
Add counter_culture to your Gemfile:
gem 'counter_culture', '~> 0.1.33'
then do bundle install
Add columns
deposit_amount
andpurchase_amount
to theusers
table.class AddDepositAmountPurchaseAmountToUsers < ActiveRecord::Migration def self.change add_column :users, :deposit_amount, :decimal, :default => 0 add_column :users, :purchase_amount, :decimal, :default => 0 end end then do
rake db:migrate
Make appropriate changes to the code
Inside Transaction model
counter_culture :user,
column_name: Proc.new {|model|
(model.txn_type == Transaction::DEPOSIT) ? 'deposit_amount' : 'purchase_amount' },
delta_column: :amount
Inside User model
def balance
@balance ||= deposit_amount - purchase_amount
end
If you need more information on Counter Culture then please read here