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