Changing from joins to includes breaking query - sql

I had this working query:
#search = Availability.joins{facility.activities}
.where{activities.id == s_activity}
But my view was getting a lot of information from Facilities and this resulted in an N+1 issue.
So I decided I should be using includes instead to eager load my associations
#search = Availability.includes{facility.activities}
.where{ facility.activities.id == s_activity)}
But this results in an error:
!! #<ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "activities"
LINE 1: ...T "availabilities".* FROM "availabilities" WHERE ("activitie...
^
These are the associations:
class Activity < ActiveRecord::Base
has_and_belongs_to_many :facilities
end
class Availability < ActiveRecord::Base
# Associations
belongs_to :facility
end
class Facility < ActiveRecord::Base
# Associations
has_and_belongs_to_many :activities
has_many :availabilities
end
There's a table called activities_facilities for the has_and_belongs_to_many

You need to append .references when using includes with conditions.
Availability.includes(facility: [:activities]).where('activities.id = ?', s_activity).references(:facility, :activities)
If you want to add conditions to your included models you’ll have to
explicitly reference them
Refer conditions part in includes

Related

Creating a "joins" query 2 associations up

I have a situation where a CourseSession belongs to a Course, which belongs to a Program. In my controller I'd like to get the name of the program from the CourseSession. I've been looking around and have been seeing a lot of people suggest using the joins method. Unfortunately for me this didn't work in my case. I get this error:
Can't join 'CourseSession' to association named 'program'; perhaps you misspelled it?
What am I doing wrong?
#sessions = if params[:program]
CourseSession.joins(:course).joins(:program).where("program.name = params[:program]")
else
CourseSession.all
end
class Program < ApplicationRecord
has_many :courses, dependent: :nullify
end
class Course < ApplicationRecord
has_many :sessions, class_name: "CourseSession", inverse_of: :course, dependent: :destroy
belongs_to :program
end
class CourseSession < ApplicationRecord
belongs_to :course
end
When you use CourseSession.joins(:course).joins(:program) (i.e. chaining joins), you're just joining course_sessions table with courses table and programs table hence the error.
What you've asked for can be achieved using the following syntax:
CourseSession.joins(course: :program)
Here, courses and programs table are inner joined and course_sessions and courses table are inner joined.
Second issue is in your where method. The table names are plural by convention so you should be using programs.name instead of program.name. Try the following instead:
CourseSession.joins(course: :program).where("programs.name = ?", params[:program])
It's a little unclear what your trying to do. Your code suggests you want:
CourseSessions associated with the Program if params[:program] is present, and
All CourseSessions if params[:program] is not present.
In which case I believe you'd do something like:
#sessions = if params[:program]
CourseSession.where(course: Course.where(program: Program.find_by(name: params[:program])))
else
CourseSession.all
end

How to manually join two different table with different attribute name in Ruby on Rails controller

I am currently making a website that runs on Ruby on Rails. I am facing some issues while I was trying to join two tables, Rates and Locations, that I have with two different attributes name.
Rates: id rater_id rateable_id (and a few more attributes in this table)
Locations: id title body user_id (and a few more attributes in this table)
Here is the query that I am trying to do in SQL.
SELECT *
FROM rates, locations
WHERE rates.rater_id = locations.user_id AND rates.rateable_id = locations.id
I have read the official active record documents that provided by rubyonrails.org. I have tried doing these, but it does not work. Here is the code that I am trying to implant in app\controllers\users_controller.rb
#join_rating = Rate.joins(:locations).where("rates.rateable_id = locations.id AND rates.rater_id = locations.id")
#all_rating = #all_rating.where(rater_id: #user)
#count_all_rating = #all_rating.count
#join_rating, is trying to join the attributes with different names.
#all_rating, is trying to filter which location to show using the user ID
#join_rating, is trying to calculate the total numbers of locations that are rated by the user
Assume that everything is setup correctly and the only error is in the query that I am trying to do, how should I rewrite the statement so that I am able to show the locations that the user has rated using #all_rating.
Thank you!
A few points:
When in ActiveRecord you're starting a statement with the Rate class, it means the result is going to be a collection of Rate objects. So if you're trying to show locations, you should start with a Location class.
#locations_user_rated = Location.joins('INNER JOIN rates ON
rates.rateable_id = locations.id').where('rates.rater_id' => #user)
And if your ActiveRecord associations are well defined, you could simply do:
#locations_user_rated = Location.joins(:rates).where('rates.rater_id' => #user)
"Well defined" simply means you'll need to do something like the following. Note that I am not sure I understand your model relationships correctly. I assume below that every location has multiple rates, and that the reason your Rate model has the field called rateable_id instead of a location_id is because you want :rateable to be polymorphic. This means you probably also have a rateable_type field in rates table.
class Location < ActiveRecord::Base
has_many :rates, as: :rateable
end
class Rate < ActiveRecord::Base
belongs_to :rateable, polymorphic: true
end
If this polymorphism is not the case, things should actually be simpler, and I highly recommend that you follow Rails's conventions and simply name the relationship field location_id on your Rate model instead of rateable_id. Then you can do:
class Location < ActiveRecord::Base
has_many :rates
end
class Rate < ActiveRecord::Base
belongs_to :location
end
If still you are not convinced about the field name, you can customize things and do:
class Location < ActiveRecord::Base
has_many :rates, foreign_key: :rateable_id
end
class Rate < ActiveRecord::Base
belongs_to :location, foreign_key: :rateable_id
end
You can find more about how to customize associations here, and here.
I highly recommend taking advantage of ActiveRecord's has_many, belongs_to, and has_many through: functionality.
If you set up a model for each of these tables, with the correct relationships:
class User < ActiveRecord::Base
has_many :ratings, foreign_key: :rater_id
has_many :rated_locations, through: ratings, class_name: Location.name, source: :rater
end
class Rating < ActiveRecord::Base
belongs_to :rater, class_name: User.name
belongs_to :location
end
class Location < ActiveRecord::Base
has_many :ratings
end
Then to access the locaitons that a user has rated, you just call
user.rated_locations

Chain scopes between models in Rails

I'm having some trouble querying between models in Rails. I have a class Message that belongs_to: booking. My goal is to add an active scope to Message that depends on a Booking scope.
class Booking < ActiveRecord::Base
has_one :event
has_many :messages
def self.active
includes(:event).
where('events.endtime >= ? AND status IS NOT ?'
Time.current.beginning_of_week,
statuses['canceled'])
end
end
class Message < ActiveRecord::Base
belongs_to :booking
belongs_to :person
self.active(person_id)
where(person_id: person_id).merge(Booking.active)
end
end
I want to find the Messages directed to a specific Person where the associated Booking is active. I therefore wish to use the Booking.active when creating Message.active.
Calling Message.active(1) with above implementation returns the following error:
Association named 'event' was not found on Message; perhaps you misspelled it?
Is there any way I can use Booking.active in the implementation of Message.active and get Messages returned?
If you are adding conditions on associations, you also need to join them, not only merge or include them, i.e. the following should work:
class Booking < ActiveRecord::Base
# ...
def self.active
joins(:event).
where('events.endtime >= ? AND status IS NOT ?'
Time.current.beginning_of_week,
statuses['canceled'])
end
end
class Message < ActiveRecord::Base
# ...
self.active(person_id)
where(person_id: person_id).joins(:booking).merge(Booking.active)
end
end
There is not much documentation on this, see this for more info.

How to get this SQL query into rails (3) syntax

I have a sql-Statement and I'd like to "convert" it into rails (activerecord) method calls.
This is my query
'SELECT * FROM clients WHERE company_id IN (SELECT company_id FROM companies_projects WHERE project_id= ? )
companies_projects is a join table for an n:n relation of companies and projects
clients belong to companies (1:n)
project is an external resource and has no has_many companies, so I can't go from that direction
I want to get all clients that belong to companies that belong to one project, so I can list them in the index-page
My models
class Client < ActiveRecord::Base
belongs_to :company
end
class Company < ActiveRecord::Base
has_many :companies_projects
has_many :clients
has_many :projects, :through => :companies_projects
end
I checked the statement in rails console and it works.
I have two problems impelementing this query.
1. find_by_sql
I tried this method
Client.find_by_sql('SELECT * FROM clients WHERE company_id IN (SELECT company_id FROM companies_projects WHERE project_id= ? )',project.id)
But it throws an InvalidStatement Exception, MySQL Syntax Error near "?"
I also tried to put the sql and bindings into an array [sql,bind1], that works but I get an array and need an ActiveRecordRelation
2. where
I'm new to rails and can't figure out a valid method chain for such a query.
Could someone point me in the right direction?
I would prefer using ActiveRecord methods for the query, but I just don't know which methods to use for the nested selects.
You should have following associations between your models:
class Client < ActiveRecord::Base
belongs_to :company
end
class Company < ActiveRecord::Base
has_and_belongs_to_many :projects
has_many :clients
end
class Project < ActiveRecord::Base
has_and_belongs_to_many :companies
has_many :clients, through: :companies
end
Then it is simply:
project.clients
Client.where(company_id: CompanyProject.where(project_id: project.id).pluck(:id))
Or you can use JOIN
Client.joins(:company_project).where('companies_projects.project_id = ?', project.id)
But the best solution was proposed by #arup-rakshit
Considering that you have an intermediate model CompanyProject, this can be achieved with following query:
Client.where(:company_id => CompanyProject.where(:project_id => project_id).map(&:company_id) )
[Edit: made company_id a symbol)

Rails4 query help, find unique records with has_many though and a joining model

I have the following table structure
manufacturers --> products ---> available_sizes_products <-- sizes
and the following models
class Manufacturer < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
has_many :sizes, :through => :available_sizes_products
has_many :available_sizes_products
end
class AvailableProductSize < ActiveRecord::Base
belongs_to :sizes
belongs_to :products
end
class Size < ActiveRecord::Base
has_many :products, :through => :available_sizes_products
has_many :available_sizes_products
end
I need to get a unique list of manufacturers, that have products in size "XL" or "L" for example.I'm getting lost in the chaining of joins etc.
class Manufacturer < ActiveRecord::Base
def self.with_sizes(sizes=[])
#sizes = Sizes.find(sizes)
...
end
end
Can someone help me with that ? Trying to do the Rails 4 way rather than drop down to SQL, since I need the query to run on several DBS
Thanks
First of all you have to use single form of noun in belongs_to expression.
And for the query try this one:
Manufacturer.includes(:products).where(products: (size: "XL"))
I use "includes" to avoid N+1 query. Otherwise it will send two queries: one for Manufacturers and one for products. Write back, if this one doesn't fit your need.
EDIT
BTW, if you want to use exactly joining, write joins instead of includes.
Everything is here:
http://guides.rubyonrails.org/active_record_querying.html#joining-tables
and here:
http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
After going through the docs for joins
this is what worked :
Manufacturer.joins(products: :sizes).where(sizes: {id:ids}).distinct
This Rails way returns the model correctly.