Rails 3: has_many and where - ruby-on-rails-3

I have the following models
class City < ActiveRecord::Base
has_many :dealers
end
class Dealer < ActiveRecord::Base
belongs_to :city
end
I need to find all the cities which have dealers. How do I go about writing the where() to get the desired results?

To find only the cities that have dealers:
City.joins(:dealers)
If a city has no dealers then it will not be returned in the results.

After playing around with some of the options this is what I came up with.
City.joins(:dealers).group("dealers.city_id")
this returns the complete records of the cities.
Edit after finding a better way.
Well there is a better way to achieve this as well and that is by using the uniq in the select. So the new query will be
City.joins(:dealers).uniq
which translates to
SELECT DISTINCT `cities`.* FROM `cities` INNER JOIN `dealers` ON `dealers`.`city_id` = `cities`.`id`

Related

Rails select by number of associated records

I have following models in my rails app:
class Student < ApplicationRecord
has_many :tickets, dependent: :destroy
has_and_belongs_to_many :articles, dependent: :destroy
class Article < ApplicationRecord
has_and_belongs_to_many :students, dependent: :destroy
class Ticket < ApplicationRecord
belongs_to :student, touch: true
I need to extract all Students who has less than articles and I need to extract all Students who's last ticket title is 'Something'.
Everything I tried so far takes a lot of time. I tried mapping and looping through all Students. But I guess what I need is a joined request. I am looking for the most efficient way to do it, as database I am working with is quite large.
go with #MCI's answer for your first question. But a filter/select/find_all or whatever (although I havn't heared about filter method in ruby) through students record takes n queries where n is the number of student records (called an n+1 query).
studs = Student.find_by_sql(%{select tmp.id from (
select student_id as id from tickets where name='Something' order by tickets.created_at desc
) tmp group by tmp.id})
You asked
"I need to extract all Students who has less than articles". I'll presume you meant "I need to extract all Students who have less than X articles". In that case, you want group and having https://guides.rubyonrails.org/active_record_querying.html#group.
For example, Article.group(:student_id).having('count(articles.id) > X').pluck(:student_id).
To address your second question, you can use eager loading https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations to speed up your code.
result = students.filter do |student|
students.tickets.last.name == 'Something'
end
Here association is HABTM so below query should work
x = 10
Student.joins(:articles).group("articles_students.student_id").having("count(articles.id) < ?",x)

Active Record query to find records that match all conditions in Rails has_many through relationship

I have two models, Apartments and Amenities, which are associated through ApartmentAmenities. I am trying to implement a filter where I only show apartments that have all of the amenities specified.
class Amenity < ActiveRecord::Base
has_many :apartment_amenities
has_many :apartments, through: :apartment_amenities
end
class ApartmentAmenity < ActiveRecord::Base
belongs_to :apartment
belongs_to :amenity
end
class Apartment < ActiveRecord::Base
has_many :apartment_amenities
has_many :amenities, through: :apartment_amenities
end
I've got a query working that will return all apartments that match at least one of the amenities of given set like so:
Apartment.joins(:apartment_amenities).where('apartment_amenities.amenity_id IN (?)', [1,2,3])
but this isn't quite what I'm going for.
Alright, after giving up for a few days then getting back to it, I finally found this question: How to find records, whose has_many through objects include all objects of some list?
Which led me to the answer that works properly:
def self.with_amenities(amenity_ids)
where("NOT EXISTS (SELECT * FROM amenities
WHERE NOT EXISTS (SELECT * FROM apartment_amenities
WHERE apartment_amenities.amenity_id = amenities.id
AND apartment_amenities.apartment_id = apartments.id)
AND amenities.id IN (?))", amenity_ids)
end

Resume of product query

I have the following schema table:
I have three activerecord models with their associations. I am struggling with a query which will show the following information for each product:
Product Name, Money Total, Quantity Sold Total
It should also take account on the status of the order that the product_line are associated with, which it should be equal to "successful".
I also want a second one query which it will show the above but it will have restriction based on the month (based on the orders.created_at column). For example if I want the sales for January of this product.
Product Name, Total Money so far, Quantity total, Month
I managed to create something but I think it isn't very optimized and I used ruby's group_by which it is doing many additional queries on the view. I would appreciate how you usually start thinking about creating a query like that.
Update
I think I almost managed to solve the first query and it is the following:
products = Product.joins(:product_lines).select("products.name, SUM(product_lines.quantity) as sum_amount, SUM(product_lines.quantity*products.price) as money_total"),group("products.id")
I tried to split each columns separately and find out how I could calculate it. I haven't take into account the order status though.
The associations are the following:
ProbudtLine
class ProductLine < ActiveRecord::Base
belongs_to :order
belongs_to :cart
belongs_to :product
end
Product
class Product < ActiveRecord::Base
has_many :product_lines
end
Order
class Order < ActiveRecord::Base
has_many :product_lines, dependent: :destroy
end
I finally did it.
First query:
#best_products_so_far = Product.joins(product_lines: :order)
.select("products.*, SUM(product_lines.quantity) as amount_total, SUM(product_lines.quantity*products.price) as money_total")
.where("orders.status = 'successful'")
.group("products.id")
Second query:
#best_products_this_month = Product.joins(product_lines: :order)
.select("products.*, SUM(product_lines.quantity) as amount_total, SUM(product_lines.quantity*products.price) as money_total")
.where("orders.status = 'successful'")
.where("extract(month from orders.completed_at) = ?", Date.today.strftime("%m"))
.group("products.id")

join query with select distinct in rails 3

I have two models
class User < ActiveRecord::Base
has_one :work
end
class Work < ActiveRecord::Base
belongs_to :user
end
I need to have distinct description from Work table for specific country's users
I have written following query
Work.includes(:user).where("users.country_name = ?",'IN').select("distinct works.description").limit(10)
It works but it does not give me distinct works.description
It worked with following query,
Work.includes(:user).where("users.country_name = ?",'IN').group("works.description").limit(10)

Complex Query with Has many and belongs to for RAILS 3

I am trying to do a QUERY in my controller to get a list of suppliers with a category ID.
I have my models set up like this.
class Supplier < ActiveRecord::Base
has_and_belongs_to_many :sub_categories
end
class Category < ActiveRecord::Base
has_many :sub_categories
end
class SubCategory < ActiveRecord::Base
belongs_to :category
has_and_belongs_to_many :suppliers
end
A supplier can have Many sub_categories that are under one single category. So i can grab the category of a supplier by doing this.
#supplier.sub_categories.first.category.name
This returns the category that the supplier comes under because they have to have at least 1 sub category which is then linked to a category.
What i am trying to do is by passing a category_id i wish to return all suppliers that come under that category.
I had it written like this but it doesnt seem to be working.
#category = Category.find(params[:category_id])
#suppliers = Supplier.where('sub_category.first.category.id = ?', #category.id)
i get the following sql error
Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.id = 20)' at line 1: SELECT `suppliers`.* FROM `suppliers` WHERE (sub_category.first.category.id = 20)
Well, that's certainly an sql error. The stuff inside the where() call gets translated directly to SQL, and that's not sq;l. :)
You need to join tables together. I'm assuming there's a sub_category_suppliers table that completes the habtm association. (BTW, I much prefer to use has_many :through exclusively)
I think it would be something like this:
Supplier.joins(:sub_category_suppliers => :sub_categories).
where('sub_categories.category_id =?', #category.id).
group('suppliers.id')
As Caley Woods suggested, this should be placed in the Supplier model as a scope:
scope :by_category, lambda { |category_id|
joins(:sub_category_suppliers => :sub_categories).
where('sub_categories.category_id =?', category_id).
group('suppliers.id')
}
and then called as Supplier.by_category(#category.id)