Search code examples
ruby-on-railslimiteager-loading

Rails Eager Load and Limit


I think I need something akin to a rails eager loaded query with a limit on it but I am having trouble finding a solution for that.

For the sake of simplicity, let us say that there will never be more than 30 Persons in the system (so Person.all is a small dataset) but each person will have upwards of 2000 comments (so Person.include(:comments) would be a large data set).

Parent association

class Person < ActiveRecord::Base
  has_many :comments
end

Child association

class Comment < ActiveRecord::Base
  belongs_to :person
end

I need to query for a list of Persons and include their comments, but I only need 5 of them.

I would like to do something like this:

Limited parent association

class Person < ActiveRecord::Base
  has_many :comments
  has_many :sample_of_comments, \
    :class_name => 'Comment', :limit => 5
end

Controller

class PersonController < ApplicationController
  def index
    @persons = Person.include(:sample_of_comments)
  end
end

Unfortunately, this article states: "If you eager load an association with a specified :limit option, it will be ignored, returning all the associated objects"

Is there any good way around this? Or am I doomed to chose between eager loading 1000s of unneeded ActiveRecord objects and an N+1 query? Also note that this is a simplified example. In the real world, I will have other associations with Person, in the same index action with the same issue as comments. (photos, articles, etc).


Solution

  • Regardless of what "that article" said, the issue is in SQL you can't narrow down the second sql query (of eager loading) the way you want in this scenario, purely by using a standard LIMIT

    You can, however, add a new column and perform a WHERE clause instead

    1. Change your second association to Person has_many :sample_of_comments, conditions: { is_sample: true }
    2. Add a is_sample column to comments table
    3. Add a Comment#before_create hook that assigns is_sample = person.sample_of_comments.count < 5