Larsenal
Larsenal

Reputation: 51156

How to avoid N + 1 with DataMapper aggregates

If I have a model of type Foo that has many child records of type Bar, I'd like to be able to show a list of Foo records and show the number of child Bar records. So I have something like...

@foos.each do |foo|
  puts foo.name
  puts foo.bars.count
end

How can I avoid the N+1 problem on my aggregates? In other words, I don't want a new SELECT COUNT(*)... query for each row. I could simply create a SQL view and map it to a new Model, but is there a simpler approach?

Upvotes: 2

Views: 485

Answers (1)

Yuri Gadow
Yuri Gadow

Reputation: 1813

DataMpper is fickle about these things so I'll give you a couple options to try that may work depending on what your real code looks like.

  1. Just change count to size, i.e., puts foo.bars.size. DM's strategic eager loading can sometimes work with this approach.

  2. Force an eager load before the @foos.each loop, and change count to size, e.g.

    @foos = Foo.all(...)
    @foos.bars.to_a
    @foos.each do | foo |
      puts foo.name
      puts foo.bars.size
    end
    
  3. Issue a raw SQL query before your @foos.each loop that returns structs with foo ids and bar counts, #map those into a Hash by food id and get them inside the loop. (I've only had to resort to this level of nonsense once or twice, I'd recommend fiddling with #1 and 2 for bit before it.)

Upvotes: 1

Related Questions