find_by_ always uses sql?

Hi --

Larry Kluger wrote:

Hi,

If I eagerly load a collection during the initial find, do scoped find_by_ calls later _always_ use sql?

What is the best pattern to use to not do another db trip? Is there some specialized rails method to handle this case?

eg Author   has_many :books

larry = Author.find_by_name('larry', :include => :books)

ruby = larry.books.find_by_title('Ruby') # does this ALWAYS do a db call?

# Is the following the best way to get the one (or nil) book? temp = larry.books.select{|book| book.title == 'ruby'} ruby = temp.size == 1 ? temp[0] : nil

Thank you for your comments and time,

Larry

Sense you now have all Larry's books in memory it is probably better to search through the memory using ruby calls like:

ruby = larry.books.detect {|book| book.title == 'ruby'}

find and find_by_* on a collection are smart, though: they get rolled into one database query with the collection query itself. For example:

   $ ./script/console    Loading development environment.    >> f = Food.find(:first)    => #<Food:0x3544f04 @attributes={"name"=>"bread", "id"=>"1",    "food_group_id"=>"2"}>    >> f.ingredients.find_by_description("flour")    => #<Ingredient:0x3523c28 @attributes={"id"=>"3",    "description"=>"flour", "food_id"=>"1"}>

Here's the query log:

   Food Load (0.000726) SELECT * FROM foods LIMIT 1    Ingredient Load (0.000394) SELECT * FROM ingredients WHERE    (ingredients.food_id = 1) AND (ingredients."description" = 'flour')    LIMIT 1

Notice how the second select combines two where clauses, so the whole collection isn't loaded into memory.

The find_by_* methods have some overhead, though, since they have to pass through method_missing. You can also do:

   f.ingredients.find(:first, :conditions => "description = 'flour'")

which won't have that overhead. Of course, find_by_* is nice and concise, so it's OK for cases where you're not looking to tweak performance.

David