I am fairly new to rails. I have the following models
class Question < ActiveRecord::Base
has_many :options
has_many :response_parts
end
class ResponsePart < ActiveRecord::Base
belongs_to :question
end
The corresponding scaffolds are
rails g scaffold Question qorder:string qtext:text qtype:string
rails g scaffold ResponsePart answer:string question:belongs_to
Now I want all the response parts where qtype is 'mobile'. I have tried a few ways but could not query successfully. Can someone tell a way to make such query. Thanks in advance.
You can include the relation between the two model and add a constraint on it:
ResponsePart.includes(:question).where(questions: { qtype: 'mobile' })
This will retrieve all the ResponsePart objects from the DB having a question which match "qtype == 'mobile'"
This is also the most efficient way to retrieve these records.
Question.where(qtype: 'mobile').collect(&:response_parts)
This will query the DB to get the corresponding response_parts of each question having "qtype == 'mobile'"
Example: If you have 6 questions with "qtype == 'mobile'", it will create 6 SQL queries for each Question.
Question.where(qtype: "mobile").first.response_parts
This just retrieves the ResponsePart objects related to the first question matching the condition "qtype == 'mobile'"
try this
Question.where(qtype: "mobile").first.response_parts
Try:
Question.where(qtype: 'mobile').collect(&:response_parts)
This will give you all the response_parts for all the questions with qtype = 'mobile'
Update: (Avoiding N+1 queries)
Question.where(qtype: 'mobile').collect(&:response_parts)
This will execute a select query on each response_parts for each question leading to the "N+1" queries.
In order to avoid "N+1 queries" i.e. one Query to retrieve question and n queries to retrieve resposne_parts, you can add includes(:join_relation) (where :join_relation is response_parts in your case) as follows:
Question.includes(:response_parts).where(qtype: 'mobile').collect(&:response_parts)
Related
Given the following 2 models
class PropertyApplication
has_and_belongs_to_many :applicant_profiles
end
class ApplicantProfile
has_and_belongs_to_many :property_applications
end
I have a query that lists all property_applications and gets the collection of applicant_profiles for each property_application.
The query is as follows and it is very inefficient.
applications = PropertyApplication.includes(:applicant_profile).all.select |property_application| do
property_application.applicant_profile_ids.include?(#current_users_applicant_profile_id)
do
assume #current_users_applicant_profile_id is already defined.
How can I perform one (or few) queries to achieve this?
I want to achieve something like this
PropertyApplication.includes(:applicant_profile).where('property_application.applicant_profiles IN (#current_users_applicant_profile))
This may be a simple question, but I seem to be pulling my hair out to find an elegant solution here. I have two ActiveRecord model classes, with a has_one and belongs_to association between them:
class Item < ActiveRecord::Base
has_one :purchase
end
class Purchase < ActiveRecord::Base
belongs_to :item
end
I'm looking for an elegant way to find all Item objects, that have no purchase object associated with them, ideally without resorting to having a boolean is_purchased or similar attribute on the Item.
Right now I have:
purchases = Purchase.all
Item.where('id not in (?)', purchases.map(&:item_id))
Which works, but seems inefficient to me, as it's performing two queries (and purchases could be a massive record set).
Running Rails 3.1.0
It's quite common task, SQL OUTER JOIN usually works fine for it. Take a look here, for example.
In you case try to use something like
not_purchased_items = Item.joins("LEFT OUTER JOIN purchases ON purchases.item_id = items.id").where("purchases.id IS null")
Found two other railsey ways of doing this:
Item.includes(:purchase).references(:purchase).where("purchases.id IS NULL")
Item.includes(:purchase).where(purchases: { id: nil })
Technically the first example works without the 'references' clause but Rails 4 spits deprecation warnings without it.
A more concise version of #dimuch solution is to use the left_outer_joins method introduced in Rails 5:
Item.left_outer_joins(:purchase).where(purchases: {id: nil})
Note that in the left_outer_joins call :purchase is singular (it is the name of the method created by the has_one declaration), and in the where clause :purchases is plural (here it is the name of the table that the id field belongs to.)
Rails 6.1 has added a query method called missing in the ActiveRecord::QueryMethods::WhereChain class.
It returns a new relation with a left outer join and where clause between the parent and child models to identify missing relations.
Example:
Item.where.missing(:purchase)
I am feeling a bit slow when it comes to rails and the Active Record associations... I have two tables.
Table = Rings
Table = Variations with foreign_key => "ring_id".
class Ring < ActiveRecord::Base
has_many :variations
end
class Variation < ActiveRecord::Base
belongs_to :ring
end
So in my "index/list" view i want to display all the rings, and both the variations, and i was thinking it would be possible to do this through one SQL query... however, i have tried the join and the include methods and i think i am just not understanding how they work properly.
So my question is, how would i write a query in my controller, that would pull my "title" and "value" column values from the "variations" and combine them into one simple object for easy looping? Or do i have to loop through all rings and look up the variation values during the loop?
thanks
In your controller:
#rings = Ring.includes(:variations).all
In index.html.erb:
#rings.each do |ring|
...
ring.variations.each do |variation|
...
end
end
The includes portion of the query will prevent Rails from repeatedly querying the database as you loop through and render your rings and variations in the view.
You need to use the includes method: Ring.inclues(:variations). Then the variation will be loaded along with the rings in a single SQL query.
For more info: http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
I am using Rails v2.3.2.
I have a model called UsersCar:
class UsersCar < ActiveRecord::Base
belongs_to :car
belongs_to :user
end
This model mapped to a database table users_cars, which only contains two columns : user_id, car_id.
I would like to use Rails way to count the number of car_id where user_id=3. I konw in plain SQL query I can achieve this by:
SELECT COUNT(*) FROM users_cars WHERE user_id=3;
Now, I would like to get it by Rails way, I know I can do:
UsersCar.count()
but how can I put the ...where user_id=3 clause in Rails way?
According to the Ruby on Rails Guides, you can pass conditions to the count() method. For example:
UsersCar.count(:conditions => ["user_id = ?", 3])
will generates:
SELECT count(*) AS count_all FROM users_cars WHERE (user_id = 3)
If you have the User object, you could do
user.cars.size
or
user.cars.count
Another way would be to do:
UserCar.find(:user_id => 3).size
And the last way that I can think of is the one mentioned above, i.e. 'UserCar.count(conditions)'.
With the belogngs to association, you get several "magic" methods on the parent item to reference its children.
In your case:
users_car = UsersCar.find(1) #=>one record of users_car with id = 1.
users_car.users #=>a list of associated users.
users_car.users.count #=>the amount of associated users.
However, I think you are understanding the associations wrong, based on the fact that your UsersCar is named awkwardly.
It seems you want
User has_and_belongs_to_many :cars
Car has_and_belongs_to_manu :users
Please read abovementioned guide on associations if you want to know more about many-to-many associations in Rails.
I managed to find the way to count with condition:
UsersCar.count(:condition=>"user_id=3")
Suppose I have a Post model which has_many Comments. I want to get the top 10 most popular posts based on those who have the most comments. Assuming I have hundreds of thousands of posts, what's the most efficient way of getting those 10 top posts?
Also, how do I cache that query?
Thanks!
I'd suggest you add a counter-cache column on Post called comments_count. Add an index on this column, and then you can select the most popular posts by:
# app/models/post.rb
scope :popular, lambda { order("comments_count DESC").limit(10) }
Check out the ActiveRecord associations class methods document for more info on counter-caches.