Postgresql Group_by error - ruby-on-rails

I am trying to do the following:
#business.placements.includes(:employee).group(:month). order('points DESC').map(&:employee)
But I keep getting the following error (whenever I use group_by I run into problems because of my lack of postgresql knowledge):
ActiveRecord::StatementInvalid (PG::Error: ERROR: column "placements.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT "placements".* FROM "placements" INNER JOIN "employee...
^
: SELECT "placements".* FROM "placements" INNER JOIN "employees" ON "placements"."employee_id" = "employees"."id" WHERE "employees"."business_id" = 43 GROUP BY month ORDER BY points DESC)
My schema looks as follows (if any help):
class Business < ActiveRecord::Base
has_many :employees, dependent: :destroy
has_many :placements, through: :employees
class Employee < ActiveRecord::Base
belongs_to :business
has_many :placements
class Placement < ActiveRecord::Base
belongs_to :employee
class Vote < ActiveRecord::Base
belongs_to :employee

Related

Rails has_many :through scoped with joins

I'm trying to create a has_many :through association with a custom scope. The problem is that the scope has a joins with a 3rd model, and that seems to be breaking the association.
These are the models:
class Student < ApplicationRecord
has_many :lesson_applications, through: :lesson_requests
has_many :lessons, through: :lesson_applications
has_many :scoped_lessons, -> { custom_scope }, through: :lesson_applications, source: :lesson
has_many :tutors, through: :scoped_lessons # Broken association
end
class Lesson < ApplicationRecord
belongs_to :lesson_application
has_one :tutor, through: :lesson_application
has_one :time_range, as: :time_rangeable
scope :custom_scope, (lambda {
joins(:time_range).where('time_ranges.attribute BETWEEN ? AND ?', value1, value2)
})
end
class LessonApplication < ApplicationRecord
belongs_to :lesson_request
belongs_to :tutor
has_one :lesson
end
class Tutor < ApplicationRecord
has_many :lesson_applications
has_many :lessons, through: lesson_applications
end
class TimeRange < ApplicationRecord
belongs_to :time_rangeable, polymorphic: true
end
I expected to get the student's tutors from the scoped lessons, but instead I get an missing FROM-clause entry for table 'time_ranges'. Which is clearly and error that can be seen on the generated query.
The generated query when I do student.tutors is:
SELECT "tutors".* FROM "tutors" INNER JOIN "lesson_applications" ON "tutors"."id" = "lesson_applications"."tutor_id" INNER JOIN "lessons" ON "lesson_applications"."id" = "lessons"."lesson_application_id" INNER JOIN "lesson_applications" "lesson_applications_tutors" ON "lessons"."lesson_application_id" = "lesson_applications_tutors"."id" INNER JOIN "lesson_requests" ON "lesson_applications_tutors"."lesson_request_id" = "lesson_requests"."id" WHERE "lesson_requests"."student_id" = $1 AND (time_ranges.start BETWEEN '2018-04-11 20:27:34.798992' AND '2018-04-11 20:27:34.799090') LIMIT $2
The method I'm using temporarily instead of the association is:
class Student < ApplicationRecord
def tutors
scoped_lessons.map(&:tutor).uniq
end
end
I am using ruby 2.5.1 with Rails 5.1.6
Thank you in advance.
Edit: some associations were wrong.

Rails 5 ActiveRecord Query - Possible to join 3 tables?

Given the following models:
User: id
UserPosition: user_id, job_title_id
JobTitle: id | title
With Rails 5, how can I do something like:
current_user.job_title
What would I need to lookup UserPosition and then JobTitle to get the title?
Is this possible with one query?
You can do this through associations like this:
class User < ApplicationRecord
has_many :user_positions
has_many :job_titles, through: :user_positions
end
class UserPositions < ApplicationRecord
belongs_to :user
belongs_to :job_title
end
class JobTitle < ApplicationRecord
has_many :user_positions
has_many :users, through: :user_positions
end
Here's the documentation for a many to many relationship in Rails.
With your relationships defined as:
class User < ApplicationRecord
has_many :user_positions
end
class JobTitle < ApplicationRecord
has_many :user_positions
end
class UserPosition < ApplicationRecord
belongs_to :user
belongs_to :job_title
end
Then you can use joins, with both models job_title and user_position, through the user model, and knowing the user.id, so, then you can use pluck to get the needed attribute:
User.joins(user_positions: :job_title).where(id: 1).pluck('job_titles.title')
Which would give you an SQL query like:
SELECT job_titles.title
FROM "users"
INNER JOIN "user_positions"
ON "user_positions"."user_id" = "users"."id"
INNER JOIN "job_titles"
ON "job_titles"."id" = "user_positions"."job_title_id"
WHERE (users.id = 1)

How to query has_one through relationship?

My models:
metro_station.rb
class MetroStation < ActiveRecord::Base
belongs_to :metro_line
has_one :city, through: :metro_line, autosave: false
end
metro_line.rb`
class MetroLine < ActiveRecord::Base
belongs_to :city
has_many :metro_stations
end
city.rb
class City < ActiveRecord::Base
has_many :metro_lines
end
When I run:
MetroStation.where(city: City.first)
I get
PG::UndefinedColumn: ERROR: column metro_stations.city_id does not exist
: SELECT "metro_stations".* FROM "metro_stations" WHERE "metro_stations"."city_id" = 1
(pry) output error: #<ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column metro_stations.city_id does not exist
LINE 1: ...CT "metro_stations".* FROM "metro_stations" WHERE "metro_sta...
While this query works:
MetroStation.joins(:metro_line).where(metro_lines: {city_id: 1})
To find out why your first approach doesn't work type in
MetroStation.where(city: City.first).to_sql
You should see something like
... WHERE metro_stations.city_id = 1
forming part of the query. The MetroStation model simply doesn't have the city_id attribute, as a result your first approach forms an invalid SQL statement.
The join works since it is filtering on the MetroLine table which has the relationship to the City model in form of the city_id field.
Nothing wrong with your models it is just the way Rails generates the SQL statements which in the world of SQL makes sense.
A has_many relationship on City to MetroStation through MetroLine delivers the desired results for your question (which metro stations are in a city).
Example
class City < ActiveRecord::Base
has_many :metro_lines
has_many :metro_stations, through: :metro_lines
end
class MetroLine < ActiveRecord::Base
belongs_to :city
has_many :metro_stations
end
class MetroStation < ActiveRecord::Base
belongs_to :metro_line
has_one :city, through: :metro_line
end
# Return all metro stations for a city
# * assuming City has name 'name' field
london_stations = City.find_by_name('London').metro_stations
Hope this helps!
City should also:
has_many :metro_stations, :through => :metro_lines
And then you write:
City.first.metro_stations

Rails where having query

Here's the query I'm trying to run:
Stack.joins(:services)
.select('stacks.id, stacks.name, count(services.id) as services_count')
.group('stacks.id').having('services_count > 2')
The error I'm getting is:
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column "services_count" does not exist
LINE 1: ...ack_items"."service_id" GROUP BY stacks.id HAVING services_c...
Here are how my Stack model is related:
class Stack < ActiveRecord::Base
has_many :services, through: :stack_items
end
class StackItem < ActiveRecord::Base
belongs_to :service
belongs_to :stack
end
class Service < ActiveRecord::Base
has_many :stacks, through: :stack_items, dependent: :destroy
end
I just want to get a collection of Stacks that have at least 3 stack items where service.weight == 1.
Any suggestions?
Aliases that you set in the SELECT list in Postgres are not available in a group by clause, so simply change .having('services_count > 2') to .having('count(services.id) > 2').

Retrieving single record from each group in group_by?

I am trying to get the employees of a business with the highest points in placements for each month.
The schema looks like this:
class Business < ActiveRecord::Base
has_many :employees
has_many :placements, through: :employees
class Employee < ActiveRecord::Base
attr_accessible :name
belongs_to :business
has_many :placements
class Placement < ActiveRecord::Base
attr_accessible :month, :employee_id, :points
belongs_to :employee
I have tried the following:
#business.placements.includes(:employee).group(:month).order('points DESC').map(&:employee)
But I get a PostgreSQL group_by error:
: SELECT "placements".* FROM "placements" INNER JOIN "employees" ON "placements"."employee_id" = "employees"."id" WHERE "employees"."business_id" = 43 GROUP BY month ORDER BY points DESC
ActiveRecord::StatementInvalid: PG::Error: ERROR: column "placements.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT "placements".* FROM "placements" INNER JOIN "employee...
Any help will be appreciated.
When you group any columns you select either need to be in the group_by or have some aggregation function on them. By default AR pulls back all the columns so you probably want to restrict that with a .select

Resources