I have a rails application where I have following models -
City, Hotel, Restaurant, Park.
Associations are like this -
class City < ActiveRecord::Base
has_many :hotels
has_many :restaurants
has_many :parks
end
I want to find all cities that have at least one hotel or restaurant or Park.
How do I write single query to fetch such cities ?
For Rails 5, you can use like below
cities = City.includes(:hotels, :restaurants, :parks)
cities = ((cities.where.not(hotels: {id: nil})).or(cities.where.not(restaurants: {id: nil})).or(cities.where.not(parks: {id: nil})))
For lower version of rails , you need to use arel_table
Most appropriate solution would be using counter cache
Then you should be able to query like
City.where('hotels_count > 0 OR restaurants_count > 0 OR parks_count > 0')
P.S. This query can be re-written many ways e.g use .or method. Also, don't forget to reset cache counter if you have some data in associated tables.
The City model doesn't have any information about related stuff.
You need to select the data from hotel/park/etc.
Use AR's includes to find the all Cities with specified relations.
City.includes(:hotels, :restaurants, :parks)
Related
I have the following active record models:
class Catalog < ActiveRecord::Base
has_and_belongs_to_many :customers
end
class Customer < ActiveRecord::Base
has_and_belongs_to_many :catalogs
end
Now in my index, i want to list all customers sorted like this:
first the ones who already are member of the catalog, then all the others.
I have tried something like this:
#customers = Customer.all.joins('LEFT JOIN catalogs_customers ON catalogs_customers.customer_id = customers.id').order('catalogs_customers.catalog_id DESC, customers.company_name ASC')
That is near to my goal but i got all the customers who are member of a catalog (whatever it is) and then all the other customers.
Your question is a tiny bit unclear, but I can't comment to ask more, so I'll do my best regardless.
It sounds like you want to list all customers with priority given to those associated with one specific catalog, but your code samples don't tell us how you plan on setting that. I'll assume that you have an instance variable #catalog_id. Then you want to provide a condition on your join where you only select catalogs_customers with that catalog_id. So try something like:
#customers = Customer.all.joins('LEFT JOIN catalogs_customers ON catalogs_customers.customer_id = customers.id').
.where(catalogs_customers: { catalog_id: #catalog_id }).
order('catalogs_customers.catalog_id DESC, customers.company_name ASC')
Hope that helps.
I am implementing an availability model nested within a listing. Its for a rental app.
class Listing
has_many :availabilities, dependent: :destroy
end
class Availability
belongs_to :listing
end
availabilities table has start and end date columns.
I am writing a query through search form to find listings where availabilities are present and the date given in the form lies in between start and end dates fo those availabilities.
My query in a class method looks like:
def self.search(params)
date = params[:date]
listingsids = Availability.where('startdate <= ?', date).where('enddate >= ?', date).pluck('listing_id')
products = Listing.where(id: listingsids)
end
However i feel this is not efficient. I wish I can write Listing.joins(:availability) and then use it but rails won't allow it. I can only join the other way which will give me a relation with availability objects and I want listings i.e. parent resource.
How can I make it more efficient and reduce number of queries I am doing?
Will appreciate your help :)
You should be able to use joins on listing to get you availablity relations, joins works using the relation name, not the model name, so instead of joins(:availability) you should be using joins(:availabilities). Something like this should work and use just a single query for your case:
Listing.joins(:availablities).where('availability.startdate <= ?', date).where('availability.enddate >= ?', date)
notice that joins uses the relation name joins(:availabilities) but the string in the where uses the table name where('availability.startdate <=?', date)
I have a product model and a shop model. The relationship between two is shop has_many products and products belongs_to shop.
The shop model has two fields longitude and latitude used for distance calculation using geokit-rails. I have been successful in sorting shops by nearest distance to any given longitude and latitude using:
Shop.by_distance(origin:[params[:latitude], params[:longitude]]).sort_by{|s| s.distance_to([params[:latitude], params[:longitude]])}
The problem is with products. The products needs to be sorted according to nearest shop location as well. I have searched through and found out that a child model can be sorted from parents attributes like this:
Product.joins(:shop).order('shops.name')
The order function works only if supplied with model field. How can I sort products calculating shop distance.
Please help.
Have a look at the documentation on using :through - this should be exactly what you need.
So Product would look like:
class Product < ActiveRecord::Base
belongs_to :shop
acts_as_mappable through: :shop
...
end
And your query would be something like:
Product.joins(:shop).by_distance(origin: [params[:latitude], params[:longitude]])
If you already can filter and sort the Shop
#shops = Shop.by_distance(origin:[params[:latitude], params[:longitude]]).sort_by{|s| s.distance_to([params[:latitude], params[:longitude]])}
This will get all products from each shops according to the distance:
#closest_products = #shops.map(&:products)
If you want to weed out duplicate products, use this instead:
#closest_products = #shops.map(&:children).flatten.uniq
You may try an alternative method (I have not tested this):
#closest_products = Product.where(shop: #shops)
I'm having some trouble figuring out how to implement the SQL query where I can show the closest results (by calculated distance) first and paginate through them.
Class Location
belongs_to :student
geocoded_by :address
end
Class Student
has_one :location
has_one :school
end
Class School
belongs_to :student
end
now within SQL, I want to have a query that can go through the association Student.joins(:location) and find me the closest student from the perspective of the student who is searching. This is after specifying a specific school (so a subset of the overall Students)
For example, Joe goes to LSU and wants to be shown a list of the closest students that also go to LSU. But the distance is based on Joe's location so this will be different if Bob runs the same query.
So I know geocoder provides something like Location.nearbys(10) but what I'd really like to do is say
joe = Student.find("Joe")
closest_lsu_stdents_to_joe = Student.where("school = LSU").order(distance_to(joe)).paginate(:page => params[:page])
So I don't want to limit the search to a specified radius like nearbys does. I just want the database to calculate the distance between Joe and all the other students at LSU and return the closest ones first. And then Joe can go through all the results via the pagination.
Any help would be much appreciated. I'm using MySQL but open to other solutions.
Thanks a lot!!!
I have two tables: Members and Addresses. How can I merge these two tables together in my model, so that I could combine all their columns together? For example, Members has a column named position and Addresses has a column named street. How could I make it so that I could have Position and Address in the same virtual table. Is there a merge function for this?
Not sure why you need to create a separate model, you can use the basic Ruby constructs for this:
I'll assume Member has a column called address_id, that is a member belongs_to an Address. This will automatically associate the joins for you.
So all you need to do is something like this:
class Address < ActiveRecord::Base
end
class Member < ActiveRecord::Base
belongs_to :address
end
member = Member.create(:address => Address.find(123))
If you want to get the address and just the position field then go:
member = Member.find(456)
position = member.address.position
or perhaps:
Member.joins(:address).select("members.position, addresses.street")
Not sure what you are trying to do, but if it is something very specific, you could also try creating a View in your database and then making a model for it, treating it like a regular table.