I have an Entry model that belongs to a Category. The Category model is using ancestry so that I can nest Categories.
I want to group a selection of entries by their root category. The problem I have is that in order to do this, rails executes a query on every single entry to get the root category. So if I have 20 records, it'll execute 20 queries to group by the root category.
I've already reduced the number of queries by eager loading the category (as below), but I can't figure out how to eager load the category's root as well.
Here's the code I'm using:
Controller
@entries = current_user.entries.includes(:category)
@entries_by_category = @entries.group_by { |entry| entry.category.root }
entry.rb
class Entry < ActiveRecord::Base
belongs_to :category
end
category.rb
class Category < ActiveRecord::Base
has_ancestry
has_many :entries
end
I have tried putting a default scope on the Category model like so: default_scope { includes(:ancestry) }
. But that didn't change the number of executed queries. And I can't figure out how to use includes()
on the original query to include both the category and the category's root.
As a bit of extra information, categories can only be nested 2 levels deep (just categories and subcategories). So root technically means parent, it doesn't have to traverse multiple levels.
Any ideas?
I had a very similar problem except I wanted to eager load the category's path
instead of just the root
. Unfortunately, eager loading only works for associations. The Ancestry navigators (parent
, root
, path
, ancestors
, etc.) are all methods, not associations, so you can't eager load them.
My solution was to eager load the entry's category association and then use model-level caching (see Railscast #115) on Category
to cache the path
(you could amend to root
in your case):
category.rb
def cached_path
Rails.cache.fetch([self, "path"]) { path.to_a }
end
entries_controller.rb
def index
@entries = Entry.includes(:category)
end
views/entries/index.html.haml
- @entries.each do |entry|
= entry.category.cached_path.map{ |c| c.name }.join(" / ")
Hope that helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With