I have a query like this,
company.users.select("users.id, users.state").includes(:organization)
here I'm eager loading the association organization. I was expecting the attributes id and user_id to be fetched in the objects, but then I get all fields fetched.
Is this the way, rails behaves when we eager load or am I missing something here ?
In your case you will get all company users not organizations.
Eager loading means pre-loading the database rows. It will not fetch only attributes. It loads all rows associated.
For example:
comments = Comment.all(:select => "users.name,comment_text", :include => :user)
Here, it will not just load names from user table. It will get all users rows from the database. So you don't have to fire extra queries. And one more thing is when you use include select clause is ignored when you have attributes of included tables. For more info go through ryan bates rialscast on joins vs include : http://railscasts.com/episodes/181-include-vs-joins
Related
So I've read a lot about the rails includes method but I'm still a bit confused about what's the best situation to use it.
I have a situation where I have a user record and then this user is related to multiple models like client, player, game, team_player, team, server and server_center.
I need to display specific attributes from the related models in a view. I only need around 1-2 attributes from a specific model and I don't use the others.
I already added delegates for example to get the server.name from player I can use server_name but in this situation do I include all of the tables from which I need the attributes or is there something else I do because I only need a couple of attributes from the model.
My query is as follows at the moment:
#user_profile = User
.includes({:client => [:player, :team_player => [:team]]},
:game,
{:server_center => :server})
.where(game_id: #master.admin.games)
Includes ensures that all of the specified associations are loaded using the minimum possible number of queries.
Let say we have 2 models named User and Profile :
class User < ActiveRecord::Base
has_one :profile
end
class Profile < ActiveRecord::Base
belongs_to :user
end
If we are iterating through each of the users and display the name of each user were name field resides in Profile model which has a association with User model, we would normally have to retrieve the name with a separate database query each time. However, when using the includes method, it has already eagerly loaded the associated person table, so this block only required a single query.
without includes:
users = User.all
users.each do |user|
puts user.profile.name # need extra database query for each time we call name
end
with includes
# 1st query to get all users 2nd to get all profiles and loads to the memory
users = User.includes(:profile).all
users.each do |user|
puts user.profile.name # no extra query needed instead it loads from memory.
end
Eager Loading is used to prevent N+1 query problems. basically it does left outer join and this plays an important role in speeding up request response or optimizing the queries. eg: if we are having huge amount users and if we want to iterate through those users and their corresponding profile. no of time which we will be hitting database will be equals to number of users. but if we are using includes it will keep all profile into memory later when we iterate through the users it will fetch from this memory instead of querying.
Eager loading may not always be the best the cure for our N+1 queries for eg: if you are dealing with some complex queries preferably looks for some caching solutions like Russian Doll caching etc.. still both method has his own pros & cons end of the day it's up to you to determine the best approach.
one useful gem which helps to detect N+1 query is bullet
I have two model with has_many belongs to relation.
Scheme has_many navs
I need to fetch all the Schemes with only last nav value. I have 10 Schemes and each scheme has around 100k navs but I need only last record which is the current value.
With eager loading will load all the navs
Scheme.all.includes(:navs)
How can I apply condition to to get only last row of nav for each schemes while eager loading.
UPDATE with Log
If I run
Scheme.includes(:current_nav).limit(3)
these are the queries executed by AR
SELECT `schemes`.* FROM `schemes` LIMIT 3
SELECT `navs`.* FROM `navs` WHERE `navs`.`schemeCode` IN ('D04', 'D01', 'D30') ORDER BY id DESC
How the second query works, it will take all the navs whose schemeCode falls under list and order those by id DESC , but how it will be associated with particular scheme exactly.
How about creating an another association like this:
class Scheme < ActiveRecord::Base
has_one :current_nav, -> { order('id DESC').limit(1) }, class_name: 'Nav'
end
Now you can:
Schema.includes(:current_nav).all
or:
Schema.includes(:current_nav).last(10)
will eager load only last nav of the queried schemes.
Explanation: includes is one of the methods for retrieving objects from database in ActiveRecord. From the doc itself:
Active Record lets you specify in advance all the associations that
are going to be loaded. This is possible by specifying the includes
method of the Model.find call. With includes, Active Record ensures
that all of the specified associations are loaded using the minimum
possible number of queries.
And, since we have the association setup with current_nav, all we had to do is to use it with includes to eager load the data. Please read ActiveRecord querying doc for more information.
Can anyone tell me if it's possible to eager load an association but only return specific attributes?
I'd like to retrieve some orders with their accounts but only require the account names.
Order.select([:id, :account_id]).includes(:account).limit(2)
I think jvnill's comment says it all:
I'm not sure that is possible. You can however add the account_name to the returned order records.
orders = Order.joins(:account).select('orders.id, orders.account_id, accounts.name AS account_name')
then just use account_name like orders.first.account_name
I have an array of Rails model records. Is it possible to eager load an association for all these records in one go (query)?
Sometimes I only have an array instead of an AR::Scope. And sometimes I want to dynamically choose what to eager load later.
In Rails 3, one can use preloader to eager-load associations on existing records.
ActiveRecord::Associations::Preloader.new(posts,:comments).run()
In Rails 4.1+ the call signature changed slightly:
ActiveRecord::Associations::Preloader.new.preload(posts,:comments)
Well, you could refind those objects. Map ':id' over your array, to get the records' ids, and then refind, this time eager-loading.
If your array of, say, Post model records is posts, then it'd be:
Post.find(posts.map &:id).includes(:blah)
Eager loading does not work as I expect.
I have Products that has_many Variants, and of course each Variant belongs_to a Product.
I use code like this to load a product and all its variants:
products = Product.includes(:variants)
This works: all products and all variants are loaded with only two queries. However, the product of each variant is not loaded, so the following code causes another SQL-query:
puts products[0].variants[0].product.title
Why is that, and how can I fix it? I suppose Product.includes(:variants => :product) would work, but it causes one extra big and unnecessary SQL-query, since the Product-data is already available.
Active Record will only eager loading an association on the level you've specified. In its point of view, variant.product will be treated as another level of association. So, if you want to eager loading it, you'd have to do:
products = Product.includes({:variants => :product})