Eager Loading
As was said Jennifer provide lazy query evaluation so it will be performed only after invoking executing method. Here is a list of all executing methods:
#last
- returns last found record ornil
if nothing was found;#last!
- returns last record or raiseRecordNotFound
if nothing was found;#first
- returns first found record ornil
if nothing was found;#first!
- returns first record or raiseRecordNotFound
if nothing was found;#pluck
- returns array of specified fields values instead of records; also can return array of named tuples;#delete
- deletes all records in db by query;#exists?
- checks if there is any record in DB that fits query;#update
- updates fields by given values;#modify
- modify fields by given values using specified operators;#increment
- increments fields by given values;#decrement
- decrements fields by given values;#to_a
- calls#results
;#db_results
- returns array of hashes found by query execution;#results
- returns array of results found by query execution;#ids
- plucksid
field asInt64
array;#each
- calls#to_a
and yields each object;#each_result_set
- perform request and yield each result set;#find_in_batches
- retrieves from db result sets of given size and yields them;#find_each
- same as#find_in_batches
but yields each result instead of array;#find_records_by_sql
- returns array ofJennifer::Record
found by given plain SQL request;#destroy
- invokes#to_a
and calls#destroy
on each record`.
Pluck
User #pluck
as a shortcut to select one or more attributes without loading a bunch of records to grab the attributes you want.
Contact.all.pluck(:name)
instead of
Contact.all.map(&.name)
#pluck
returns array of values if only one field was given and array of arrays otherwise. If query object has no specified custom attributes to select given attributes is used to compose SELECT
clause (query’s table is used as a target - contacts.name
for example above).
To grab custom attributes you need to specify custom select clause:
Contact.all
.select("COUNT(id) as count") # same as .select { [sql("COUNT(id)").alias("count")] }
.group("name")
.having { sql("COUNT(id)") > 1 }
.pluck(:count)
To return array of named tuple instead of array of arrays specify fields and desired data types as splatted named tuple:
Contact.all
.select { [sql("COUNT(id)").alias("count"), _name] }
.group("name")
.having { sql("COUNT(id)") > 1 }
.pluck(count: Int32, name: String) # => Array(NamedTuple(count: Int32, name: String))
Relation Eager Loading
To prevent N+1 problem you may want to automatically load some related objects. There 3 possible ways how to do this:
- using
#eager_load
- will automatically join all provided relations usingLEFT OUTER JOIN
; - using
#relation
(or manual joining) +#with
- works as#eager_load
but allows you to manipulate loaded records; - using
#includes
/#preload
- will load all specified relations using separate requests.
Examples:
# #eager_load
Contact.all.eager_load(:addresses)
# manual eager load
Contact.all.left_join(Address) { _contacts__id == _contact_id }.with_relation(:addresses)
# #includes/#preload
Contact.all.includes(:addresses)
# or
Contact.all.preload(:addresses)
NOTE:
#preload
is just an alias for#includes
.
Also #eager_load
and #includes
accepts splatted named tuple as last arguments specifying nested relations to load:
City.all.eager_load(country: { contact: [:passport, :addresses]})