diff options
author | Frederick Cheung <frederick.cheung@gmail.com> | 2008-05-18 20:02:37 +0100 |
---|---|---|
committer | Frederick Cheung <frederick.cheung@gmail.com> | 2008-05-18 20:24:48 +0100 |
commit | c9c4f7bababda23f33693bdca35d1bb37fdfcd15 (patch) | |
tree | e3bd60fce7fbafc4c1eb0d8d6a99a6191ea658f6 /activerecord/lib | |
parent | a49ebf6d7909c344d2fe570cb82c97fa271db03e (diff) | |
download | rails-c9c4f7bababda23f33693bdca35d1bb37fdfcd15.tar.gz rails-c9c4f7bababda23f33693bdca35d1bb37fdfcd15.tar.bz2 rails-c9c4f7bababda23f33693bdca35d1bb37fdfcd15.zip |
Update documentation on :include.
Diffstat (limited to 'activerecord/lib')
-rw-r--r-- | activerecord/lib/active_record/associations.rb | 116 | ||||
-rwxr-xr-x | activerecord/lib/active_record/base.rb | 2 |
2 files changed, 64 insertions, 54 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 95caf68692..902deeb35c 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -441,9 +441,9 @@ module ActiveRecord # # == Eager loading of associations # - # Eager loading is a way to find objects of a certain class and a number of named associations along with it in a single SQL call. This is + # Eager loading is a way to find objects of a certain class and a number of named associations. This is # one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100 posts that each need to display their author - # triggers 101 database queries. Through the use of eager loading, the 101 queries can be reduced to 1. Example: + # triggers 101 database queries. Through the use of eager loading, the 101 queries can be reduced to 2. Example: # # class Post < ActiveRecord::Base # belongs_to :author @@ -462,14 +462,15 @@ module ActiveRecord # # for post in Post.find(:all, :include => :author) # - # This references the name of the +belongs_to+ association that also used the <tt>:author</tt> symbol, so the find will now weave in a join something - # like this: <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Doing so will cut down the number of queries from 201 to 101. + # This references the name of the +belongs_to+ association that also used the <tt>:author</tt> symbol. After loading the posts, find + # will collect the +author_id+ from each one and load all the referenced authors with one query. Doing so will cut down the number of queries from 201 to 102. # # We can improve upon the situation further by referencing both associations in the finder with: # # for post in Post.find(:all, :include => [ :author, :comments ]) # - # That'll add another join along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt>. And we'll be down to 1 query. + # This will load all comments with a single query. This reduces the total number of queries to 3. More generally the number of queries + # will be 1 plus the number of associations named (except if some of the associations are polymorphic belongs_to - see below). # # To include a deep hierarchy of associations, use a hash: # @@ -482,36 +483,46 @@ module ActiveRecord # the number of queries. The database still needs to send all the data to Active Record and it still needs to be processed. So it's no # catch-all for performance problems, but it's a great way to cut down on the number of queries in a situation as the one described above. # - # Since the eager loading pulls from multiple tables, you'll have to disambiguate any column references in both conditions and orders. So - # <tt>:order => "posts.id DESC"</tt> will work while <tt>:order => "id DESC"</tt> will not. Because eager loading generates the +SELECT+ statement too, the - # <tt>:select</tt> option is ignored. + # Since only one table is loaded at a time, conditions or orders cannot reference tables other than the main one. If this is the case + # Active Record falls back to the previously used LEFT OUTER JOIN based strategy. For example + # + # Post.find(:all, :include => [ :author, :comments ], :conditions => ['comments.approved = ?', true]) # - # You can use eager loading on multiple associations from the same table, but you cannot use those associations in orders and conditions - # as there is currently not any way to disambiguate them. Eager loading will not pull additional attributes on join tables, so "rich - # associations" with +has_and_belongs_to_many+ are not a good fit for eager loading. + # will result in a single sql query with joins along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and + # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions like this can have unintended consequences. + # In the above example posts with no approved comments are not returned at all, because the conditions apply to the sql statement as a whole + # and not just to the association. You must disambiguate column references for this fallback to happen, for example + # <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not. + # + # If you do want eagerload only some members of an association it is usually more natural to :include an association + # which has conditions defined on it: + # + # class Post < ActiveRecord::Base + # has_many :approved_comments, :class_name => 'Comment', :conditions => ['approved = ?', true] + # end + # + # Post.find(:all, :include => :approved_comments) + # + # will load posts and eager load the approved_comments association, which contains only those comments that have been approved. # # When eager loaded, conditions are interpolated in the context of the model class, not the model instance. Conditions are lazily interpolated # before the actual model exists. # - # Eager loading is not supported with polymorphic associations up to (and including) - # version 2.0.2. Given + # Eager loading is supported with polymorphic associations. # # class Address < ActiveRecord::Base # belongs_to :addressable, :polymorphic => true # end # - # a call that tries to eager load the addressable model - # - # Address.find(:all, :include => :addressable) # INVALID - # - # will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent model's type - # is a column value so its corresponding table name cannot be put in the +FROM+/+JOIN+ clauses of that early query. + # A call that tries to eager load the addressable model # - # In versions greater than 2.0.2 eager loading in polymorphic associations is supported - # thanks to a change in the overall preloading strategy. + # Address.find(:all, :include => :addressable) # - # It does work the other way around though: if the <tt>User</tt> model is <tt>addressable</tt> you can eager load - # their addresses with <tt>:include</tt> just fine, every piece needed to construct the query is known beforehand. + # will execute one query to load the addresses and load the addressables with one query per addressable type. + # For example if all the addressables are either of class Person or Company then a total of 3 queries will be executed. The list of + # addressable types to load is determined on the back of the addresses loaded. This is not supported if ActiveRecord has to fallback + # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent + # model's type is a column value so its corresponding table name cannot be put in the +FROM+/+JOIN+ clauses of that query. # # == Table Aliasing # @@ -519,44 +530,44 @@ module ActiveRecord # the standard table name is used. The second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>. Indexes are appended # for any more successive uses of the table name. # - # Post.find :all, :include => :comments - # # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ... - # Post.find :all, :include => :special_comments # STI - # # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ... AND comments.type = 'SpecialComment' - # Post.find :all, :include => [:comments, :special_comments] # special_comments is the reflection name, posts is the parent table name - # # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ... LEFT OUTER JOIN comments special_comments_posts + # Post.find :all, :joins => :comments + # # => SELECT ... FROM posts INNER JOIN comments ON ... + # Post.find :all, :joins => :special_comments # STI + # # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment' + # Post.find :all, :joins => [:comments, :special_comments] # special_comments is the reflection name, posts is the parent table name + # # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts # # Acts as tree example: # - # TreeMixin.find :all, :include => :children - # # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ... - # TreeMixin.find :all, :include => {:children => :parent} # using cascading eager includes - # # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ... - # LEFT OUTER JOIN parents_mixins ... - # TreeMixin.find :all, :include => {:children => {:parent => :children}} - # # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ... - # LEFT OUTER JOIN parents_mixins ... - # LEFT OUTER JOIN mixins childrens_mixins_2 + # TreeMixin.find :all, :joins => :children + # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ... + # TreeMixin.find :all, :joins => {:children => :parent} + # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ... + # INNER JOIN parents_mixins ... + # TreeMixin.find :all, :joins => {:children => {:parent => :children}} + # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ... + # INNER JOIN parents_mixins ... + # INNER JOIN mixins childrens_mixins_2 # # Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix: # - # Post.find :all, :include => :categories - # # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ... - # Post.find :all, :include => {:categories => :posts} - # # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ... - # LEFT OUTER JOIN categories_posts posts_categories_join LEFT OUTER JOIN posts posts_categories - # Post.find :all, :include => {:categories => {:posts => :categories}} - # # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ... - # LEFT OUTER JOIN categories_posts posts_categories_join LEFT OUTER JOIN posts posts_categories - # LEFT OUTER JOIN categories_posts categories_posts_join LEFT OUTER JOIN categories categories_posts + # Post.find :all, :joins => :categories + # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ... + # Post.find :all, :joins => {:categories => :posts} + # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ... + # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories + # Post.find :all, :joins => {:categories => {:posts => :categories}} + # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ... + # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories + # INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2 # # If you wish to specify your own custom joins using a <tt>:joins</tt> option, those table names will take precedence over the eager associations: # - # Post.find :all, :include => :comments, :joins => "inner join comments ..." - # # => SELECT ... FROM posts LEFT OUTER JOIN comments_posts ON ... INNER JOIN comments ... - # Post.find :all, :include => [:comments, :special_comments], :joins => "inner join comments ..." - # # => SELECT ... FROM posts LEFT OUTER JOIN comments comments_posts ON ... - # LEFT OUTER JOIN comments special_comments_posts ... + # Post.find :all, :joins => :comments, :joins => "inner join comments ..." + # # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ... + # Post.find :all, :joins => [:comments, :special_comments], :joins => "inner join comments ..." + # # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ... + # INNER JOIN comments special_comments_posts ... # INNER JOIN comments ... # # Table aliases are automatically truncated according to the maximum length of table identifiers according to the specific database. @@ -840,7 +851,6 @@ module ActiveRecord # this results in a counter with +NULL+ value, which will never increment. # Note: Specifying a counter cache will add it to that model's list of readonly attributes using +attr_readonly+. # * <tt>:include</tt> - Specify second-order associations that should be eager loaded when this object is loaded. - # Not allowed if the association is polymorphic. # * <tt>:polymorphic</tt> - Specify this association is a polymorphic association by passing +true+. # Note: If you've enabled the counter cache, then you may want to add the counter cache attribute # to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>). diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 00083a50fd..c51743af16 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -465,7 +465,7 @@ module ActiveRecord #:nodoc: # or named associations in the same form used for the <tt>:include</tt> option, which will perform an INNER JOIN on the associated table(s). # If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table's columns. # Pass <tt>:readonly => false</tt> to override. - # * <tt>:include</tt>: Names associations that should be loaded alongside using LEFT OUTER JOINs. The symbols named refer + # * <tt>:include</tt>: Names associations that should be loaded alongside. The symbols named refer # to already defined associations. See eager loading under Associations. # * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you, for example, want to do a join but not # include the joined columns. |