Home > other >  How to eliminate N 1 queries from database query in Ruby on Rails?
How to eliminate N 1 queries from database query in Ruby on Rails?

Time:12-09

In my Rails 6 app I have these models:

class User < ApplicationRecord

  has_many :read_news_items
  has_many :news_items, :through => :read_news_items

end

class NewsItem < ApplicationRecord

  has_many :read_news_items
  has_many :users, :through => :read_news_items

  def read?(user)
    read_news_items.where(:user_id => user.id).any?
  end

end

class ReadNewsItem < ApplicationRecord

  belongs_to :user
  belongs_to :news_item

end

In my controller action I want to list all news items and highlight the ones that have not yet been read by the user:

class NewsItemsController < ApplicationController

  def index
    @news_items = NewsItem.all
  end

end

The problem is that this generates N 1 queries for each record because the read?(current_user) gets called for each user record.

How can this problem be overcome?

I tried appending includes(:read_news_items) and joins(:read_news_items) to the database query in my controller but to no avail.

CodePudding user response:

You could try:

class NewsItem < ApplicationRecord
  has_many :read_news_items

  def read?(user)
    if read_news_items.loaded?
      read_news_items.any? {|rni| rni.user_id == user.id }
    else
      read_news_items.where(:user_id => user.id).any?
    end
  end
end

class NewsItemsController < ApplicationController
  def index
    @news_items = NewsItem.includes(:read_news_items).all
  end
end

CodePudding user response:

OK, I learned something from every answer that was given here. So thanks for that.

I changed my read? method to the following which seems to have eliminated the N 1 queries:

class NewsItem < ApplicationRecord

  def read?(user)
    (user.read_news_items.pluck(:news_item_id)).include?(id)
  end

end
  • Related