ruby-on-railsruby-on-rails-4query-optimizationeager-loadingpublic-activity

How do I do eager loading for multiple associations?


I am using public_activity and one of my partials I have this call:

<%= link_to "#{activity.trackable.user.name} " + "commented on " + "#{activity.trackable.node.name}", node_path(activity.trackable.node.family_tree_id,activity.trackable.node) %>

That is executed by this call:

      <% @unread_act.each do |friend| %>
        <li><%= render_activity friend %> </li>
      <% end %>

That instance variable is assigned in my ApplicationController here:

  before_filter :handle_nofications

  def handle_nofications
    if current_user.present?
      @unread_act = Notification.where(owner_id: current_user).includes(:trackable => :user).unread_by(current_user)
    end
  end

I am using the gem Bullet, to find N+1 queries and the feedback I get is this:

N+1 Query detected
  Comment => [:node]
  Add to your finder: :include => [:node]
N+1 Query method call stack

I initially got that message for :trackable and for :user. Both of which I am now eager loading via includes within that assignment statement.

However, I have no idea how to go 3 levels deep on an eager loading - rather than just two.

Given that the node is called like activity.trackable.node.name, I imagine some appropriate eager loading assignment would look something like:

  @unread_act = Notification.where(owner_id: current_user).includes(:trackable => [:user, :node]).unread_by(current_user)

But the error I get is this:

ActiveRecord::AssociationNotFoundError at /
Association named 'node' was not found on Node; perhaps you misspelled it?

I even get that when I simply do:

@unread_act = Notification.where(owner_id: current_user).includes(:trackable => :node).unread_by(current_user)

So I suspect something else is going wrong here.

Any ideas?

Edit 1

See Node & Notification models and associations below.

Node.rb

class Node < ActiveRecord::Base
  include PublicActivity::Model
  tracked except: :update, owner: ->(controller, model) { controller && controller.current_user }

  belongs_to :family_tree
  belongs_to :user
  belongs_to :media, polymorphic: true, dependent: :destroy
  has_many :comments, dependent: :destroy
  has_many :node_comments, dependent: :destroy

Notification.rb

class Notification < PublicActivity::Activity
  acts_as_readable :on => :created_at
end

Solution

  • It seems the correct way to approach this is by using an array on the trackable attribute like so:

    @unread_act = Notification.where(recipient_id: current_user).includes(:trackable => [:user, :node]).unread_by(current_user).order("created_at desc")
    

    The main part is:

    includes(:trackable => [:user, :node])

    This seems to have worked beautifully.