ProGM
ProGM

Reputation: 7108

Rails: `includes` a `has_many` relation with `limit`

I'm using Rails 4.2. I have 3 tables like this:

class Collection < ActiveRecord::Base
    has_many :shares
    has_many :images, through: :shares
    has_many :latest_images, -> { order(created_at: :desc).limit(10) }, class_name: 'Image', through: :shares, source: :image
end

class Share < ActiveRecord::Base
    belongs_to :image
    belongs_to :collection
end

class Image < ActiveRecord::Base
    has_many :shares
    has_many :collections, through: :shares
end

My goal is to select some collections and preload the first 10 newest cards of each collection using the latest_images relation.

If I do simply:

collections = Collection.where(some_condition).includes(:latest_images)

The problem is latest_images will contain all cards, not only the last 10 (even if there's a limit(10))

collections.first.latest_images.count # => more than 10!!!

Instead, if I add the limit(10) after loading the collections, I'll have a N+1 query problem:

collections.each { |collection| collection.latest_images.limit(10).do_something } # N+1 QUERY

Any solution?

Upvotes: 7

Views: 4962

Answers (1)

Shadwell
Shadwell

Reputation: 34784

There is a note tucked away in the associations documentation under "Eager loading of associations":

If you eager load an association with a specified :limit option, it will be ignored, returning all the associated objects.

So, it is behaving as documented even though that might not be the intuitive behaviour.

The work around is to not eager load the limited associated and to access separately afterwards. As you point out that's not ideal but it is almost certainly preferable to loading all of the associated objects without a limit.

Upvotes: 8

Related Questions