Question

How do i memoize an expensive query in parent object and reuse it in child objects? The problem i'm facing is that each time i make a reference from child to parent: child1.parent or child2.parent, it gives different object ids and memoization does not happen.

class Post
  has_many :comments
  def total_comments
    unless @total_comments
      puts "Loading comments"
      @total_comments = comments.count
    end
    @total_comments
  end
end

class Comment
  belongs_to :post
  def total_comments
    post.total_comments
  end
end

post.comments[0].total_comments
post.comments[1].total_comments

This should've queried for comments only once, but because it's not memoized on the same object it's loading twice

Loading comments...
Loading comments...
Was it helpful?

Solution

Several ways to do it:

1. Use ActiveRecord Association Extensions

class Post
  has_many :comments do

      def total
          proxy_association.target.size
      end

  end
end

Allows you to call proxy_association object, and appends the method to the instance of comments (so you can call @post.comments.total)


2. Use :inverse_of

#app/models/post.rb
Class Post < ActiveRecord::Base
    has_many :comments, inverse_of: :post
end

#app/models/comment.rb
Class Comment < ActiveRecord::Base
    belongs_to :post, inverse_of: :comments
end

Allows you to reference parent object from self (self.post.total_comments)


3. Use "Eager Loading" (keeping objects in memory)

This is query-level, and alluded to in NitinJ's answer & this RailsCast:

Post.includes(:comments)

I think NitinJ's comments are better than mine here, as I only have experience in creating DB calls with .includes (not using it in an association capacity)


BONUS - Counter-Cache -- use this instead of comments.count - it stores the count in memory, which will remove an expensive DB call!

OTHER TIPS

Try this post =Post.last(include: :comments) this statement eager loads the relation now perform operation post.comments[0] this will not trigger any SQL query to find that as the associated records is already there

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top