Class: ActiveRecord::Associations::Preloader
- Extended by:
- ActiveSupport::Autoload
- Defined in:
- activerecord/lib/active_record/associations/preloader.rb,
activerecord/lib/active_record/associations/preloader/batch.rb,
activerecord/lib/active_record/associations/preloader/branch.rb,
activerecord/lib/active_record/associations/preloader/association.rb,
activerecord/lib/active_record/associations/preloader/through_association.rb
Overview
Implements the details of eager loading of Active Record associations.
Suppose that you have the following two Active Record models:
class Author < ActiveRecord::Base
# columns: name, age
has_many :books
end
class Book < ActiveRecord::Base
# columns: title, sales, author_id
end
When you load an author with all associated books Active Record will make multiple queries like this:
Author.includes(:books).where(name: ['bell hooks', 'Homer']).to_a
# SELECT `authors`.* FROM `authors` WHERE `name` IN ('bell hooks', 'Homer')
# SELECT `books`.* FROM `books` WHERE `author_id` IN (2, 5)
Active Record saves the ids of the records from the first query to use in the second. Depending on the number of associations involved there can be arbitrarily many SQL queries made.
However, if there is a WHERE clause that spans across tables Active Record will fall back to a slightly more resource-intensive single query:
Author.includes(:books).where(books: {title: 'Illiad'}).to_a
# SELECT `authors`.`id` AS t0_r0, `authors`.`name` AS t0_r1, `authors`.`age` AS t0_r2,
# `books`.`id` AS t1_r0, `books`.`title` AS t1_r1, `books`.`sales` AS t1_r2
# FROM `authors`
# LEFT OUTER JOIN `books` ON `authors`.`id` = `books`.`author_id`
# WHERE `books`.`title` = 'Illiad'
This could result in many rows that contain redundant data and it performs poorly at scale and is therefore only used when necessary.
Defined Under Namespace
Classes: Association, Batch, Branch, ThroughAssociation
Instance Attribute Summary collapse
-
#associate_by_default ⇒ Object
readonly
Returns the value of attribute associate_by_default.
-
#associations ⇒ Object
readonly
Returns the value of attribute associations.
-
#records ⇒ Object
readonly
Returns the value of attribute records.
-
#scope ⇒ Object
readonly
Returns the value of attribute scope.
Instance Method Summary collapse
- #branches ⇒ Object
- #call ⇒ Object
- #empty? ⇒ Boolean
-
#initialize(records:, associations:, scope: nil, available_records: [], associate_by_default: true) ⇒ Preloader
constructor
Eager loads the named associations for the given Active Record record(s).
- #loaders ⇒ Object
Methods included from ActiveSupport::Autoload
autoload, autoload_at, autoload_under, eager_autoload, eager_load!, extended
Constructor Details
#initialize(records:, associations:, scope: nil, available_records: [], associate_by_default: true) ⇒ Preloader
Eager loads the named associations for the given Active Record record(s).
In this description, ‘association name’ shall refer to the name passed to an association creation method. For example, a model that specifies belongs_to :author
, has_many :buyers
has association names :author
and :buyers
.
Parameters
records
is an array of ActiveRecord::Base. This array needs not be flat, i.e. records
itself may also contain arrays of records. In any case, preload_associations
will preload all associations records by flattening records
.
associations
specifies one or more associations that you want to preload. It may be:
-
a Symbol or a String which specifies a single association name. For example, specifying
:books
allows this method to preload all books for an Author. -
an Array which specifies multiple association names. This array is processed recursively. For example, specifying
[:avatar, :books]
allows this method to preload an author’s avatar as well as all of his books. -
a Hash which specifies multiple association names, as well as association names for the to-be-preloaded association objects. For example, specifying
{ author: :avatar }
will preload a book’s author, as well as that author’s avatar.
:associations
has the same format as the :include
method in ActiveRecord::QueryMethods
. So associations
could look like this:
:books
[ :books, :author ]
{ author: :avatar }
[ :books, { author: :avatar } ]
available_records
is an array of ActiveRecord::Base. The Preloader will try to use the objects in this array to preload the requested associations before querying the database. This can save database queries by reusing in-memory objects. The optimization is only applied to single associations (i.e. :belongs_to, :has_one) with no scopes.
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'activerecord/lib/active_record/associations/preloader.rb', line 96 def initialize(records:, associations:, scope: nil, available_records: [], associate_by_default: true) @records = records @associations = associations @scope = scope @available_records = available_records || [] @associate_by_default = associate_by_default @tree = Branch.new( parent: nil, association: nil, children: @associations, associate_by_default: @associate_by_default, scope: @scope ) @tree.preloaded_records = @records end |
Instance Attribute Details
#associate_by_default ⇒ Object (readonly)
Returns the value of attribute associate_by_default
54 55 56 |
# File 'activerecord/lib/active_record/associations/preloader.rb', line 54 def associate_by_default @associate_by_default end |
#associations ⇒ Object (readonly)
Returns the value of attribute associations
54 55 56 |
# File 'activerecord/lib/active_record/associations/preloader.rb', line 54 def associations @associations end |
#records ⇒ Object (readonly)
Returns the value of attribute records
54 55 56 |
# File 'activerecord/lib/active_record/associations/preloader.rb', line 54 def records @records end |
#scope ⇒ Object (readonly)
Returns the value of attribute scope
54 55 56 |
# File 'activerecord/lib/active_record/associations/preloader.rb', line 54 def scope @scope end |
Instance Method Details
#branches ⇒ Object
123 124 125 |
# File 'activerecord/lib/active_record/associations/preloader.rb', line 123 def branches @tree.children end |
#call ⇒ Object
117 118 119 120 121 |
# File 'activerecord/lib/active_record/associations/preloader.rb', line 117 def call Batch.new([self], available_records: @available_records).call loaders end |
#empty? ⇒ Boolean
113 114 115 |
# File 'activerecord/lib/active_record/associations/preloader.rb', line 113 def empty? associations.nil? || records.length == 0 end |
#loaders ⇒ Object
127 128 129 |
# File 'activerecord/lib/active_record/associations/preloader.rb', line 127 def loaders branches.flat_map(&:loaders) end |