开发者

Rails ActiveRecord helper find method not eager loading association

开发者 https://www.devze.com 2023-02-22 01:46 出处:网络
I have the following models: Game and Pick. There\'s a one to many association between Game and Pick. There\'s a third model called Player, a Player has many Picks.

I have the following models: Game and Pick. There's a one to many association between Game and Pick. There's a third model called Player, a Player has many Picks.

There's a method in the Player class that finds a pick for a given game or creates a new one if it doesn't exist.

class Player < ActiveRecord::Base
  has_many :picks

  def pick_for_game(game)
    game_id = game.instance_of?(Game) ? game.id : game
    picks.find_or_initialize_by_game_id(game_id)
  end
end

I want to eager load the games for each pick. However if I do

pick开发者_Python百科s.find_or_initialize_by_game_id(game_id, :include => :game)

It first fetches the picks when this query is run (the method is run multiple times), then fetches the games as each pick is accessed. If I add a default_scope to the Pick class

class Pick < ActiveRecord::Base
  belongs_to :game
  belongs_to :player
  default_scope :include => :game
end

It still generates 2 select statements for each pick, but now it loads the game right after the pick, but it still doesn't do a join like I'm expecting.

Pick Load (0.2ms)  SELECT "picks".* FROM "picks" WHERE "picks"."game_id" = 1 AND ("picks".player_id = 1) LIMIT 1
Game Load (0.4ms)  SELECT "games".* FROM "games" WHERE ("games"."id" = 1)


First, find doesn't support having include or join as a parameter. (As mipsy said, it doesn't make sense for find to support include as it would be the same number of queries as loading it later.)

Second, include eagerly loads the association, so something like

Person.includes(:company)

is roughly equivalent to doing:

Person.all.each { |person| Company.find(person.company_id) }

I say roughly equivalent to because the former has O(1) (really two) queries whereas the latter is O(n) queries, where n is the number of people.

A join, however, would be just one query, but the downside of a join is you can't always use the retrieved data to update the model. To do a join you would do:

Person.join(:companies)

You can read more on joining tables in the Rails Guide.

To sum up, joining isn't eagerly loading because it's not loading the association, it's loading both pieces of data together at once. I realize there's a weird fine line between the two, but eagerly loading is getting other data preemptively, but you wouldn't be getting that data later via a join, or you'd have already gotten it in your original query! Hope that makes sense.


This is the way it's meant to work, I think. Eager loading is primarily used to make iterations over large collections of models more efficient by fetching them all at once-- it won't make any difference if you're just dealing with a single object.

0

精彩评论

暂无评论...
验证码 换一张
取 消