Ruby on Rails
Thursday, February 4, 2016
On 2/4/16 11:30 AM, John Sanderbeck wrote:
I am trying to make this as efficient as possible which is why I was trying to do the group call. I was doing it in the Highchart definition which worked however it does many queries depending on how many initiatives there are. series: [{ name: "Trainings", data: [ <% Initiative.all.each do |initiative| %> ['<%= initiative.name %>',<%= Training.where("initiative_id = ?", initiative.id).count %>], <% end %> ] }],
In order to be most efficient I would first ensure that you have the possessive relationships are in place. If so you would be able to include Training when Calling Initiative.
Initiative.all.includes(:training).each do |initiative|
puts initiative.training.size
end
This will utilize eager loading to reduce this run to 2 queries. The issue with this would be dataset size, imagine if you have 1000 initiatives with a total of 300,000 trainings between them. You will need to be able to hold all of those trainings in memory to run this. Not efficient.
Likewise your methodology is not efficient as it essentially is an n+1 anitpattern, meaning that you will be doing n queries to get the trainings and another 1 for the initiatives. So if you have 1000 initiatives that will be 1001 queries.
So the better way to do this would be to have a counter cache of trainings on the initiative model. This would involve adding a trainings_count field to initiative and then setting the relationship on the training model to something like
belongs_to :initiative, counter_cache: true
Then when you call initiative.training.size, rails would look to the data in that field before trying to pull the data from the database, so you would be able to use
Initiative.all.each do |initiative|
puts initiative.training.size
end
Note that, when adding counter_caching to an existing model set you will need to run a process (pref in the migration) to reset the counters for the model.
This could be your migration file. Though using a pure sql statement might be more efficient.
add_column :initiatives, :trainings_count, null: false, default: 0
Initiative.all.each do |i|
Initiative.reset_counters(i.id, :trainings)
end
hope this helps
One of the issues is that there may not be any record count for a particular initiative so I also need to return a zero but still map the initiative. The example from the Railscast is like this and is similar to what I was trying to do however this example was based on a day mapping. def orders_chart_series(orders, start_time) orders_by_day = orders.where(:purchased_at => start_time.beginning_of_day..Time.zone.now.end_of_day). group("date(purchased_at)").select("purchased_at, sum(total_price) as total_price") (start_time.to_date..Date.today).map do |date| order = orders_by_day.detect { |order| order.purchased_at.to_date == date } order && order.total_price.to_f || 0 end.inspect end I will try the examples you have given me so far to see if I can accomplish what I need. I appreciate the fast responses...
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment