I have a complicated db query that selects schools based on real estate listing attributes, performance statistics for the schools, and the distance of each listing to public transit. Users create a Search
object, and a method find_schools
in the search.rb
has this query:
School.where(id: school_ids).narrow_schools_for_search(self,prop_type,status,year).joins(listings:
:cta_listings).joins(:performance_stats).where("cta_listings.distance <= ?",
self.cta_distance).where.not(performance_stats: {"#{sort_column.to_sym}" =>
nil}).distinct.limit(30).order("performance_stats.#{sort_column} DESC")
School.rb
scope :narrow_schools_for_search, ->(search,prop_type,status,year) {joins(:listings).joins(:performance_stats)
.where("listings.beds >= ?",search.beds).where("listings.price <= ?",search.max_price)
.where("listings.price >= ?",search.min_price).where(listings: {prop_type: prop_type, status: status})
.where(performance_stats: {year: year}).distinct}
has_many :performance_stats, dependent: :destroy
has_many :assignments, dependent: :destroy
has_many :listings, through: :assignments
Listing.rb
has_many :assignments, dependent: :destroy
has_many :schools, through: :assignments
has_many :cta_listings, dependent: :destroy
has_many :cta_stations, through: :cta_listings
has_many :metra_listings, dependent: :destroy
has_many :metra_stations, through: :metra_listings
PerformanceStat.rb
belongs_to :school
I need the Schools ordered by an attribute in the associated table PerformanceStats, which is a user defined attribute sort_column
. The query works in development env (sqlite3), but fails on the staging app (PG) with this error:
ActiveRecord::StatementInvalid (PG::InvalidColumnReference: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
I need to add a select statement that includes the column name by which I am sorting schools.
The advice on other posts like this one is to do something like:
Widget.select('"widgets".*, "widget_steps.name"')
So, for my case, I tried this:
sort_for_select = "performance_stats.#{sort_column}"
School.select('"schools".*, "#{sort_for_select"').where(id: school_ids).narrow_schools_for_search(self,prop_type,status,year).joins(listings:
:cta_listings).joins(:performance_stats).where("cta_listings.distance <= ?",
self.cta_distance).where.not(performance_stats: {sort_column.to_sym =>
nil}).distinct.limit(30).order("performance_stats.#{sort_column} DESC")
But my editor is indicating I'm not actually escaping to to ruby. I tried it anyway, and sure enough, it fails with
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column "#{sort_for_select}" does not exist.
Then I tried hard coding the sort_column
:
School.select('"schools".*, "performance_stats.grall_adjpicalc"').where(id: school_ids).narrow_schools_for_search(self,prop_type,status,year).joins(listings:
:cta_listings).joins(:performance_stats).where("cta_listings.distance <= ?",
self.cta_distance).where.not(performance_stats: {grall_adjpicalc:
nil}).distinct.limit(30).order("performance_stats.grall_adjpicalc DESC")
This works in dev environment, but if fails on the staging app with this error:
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column "performance_stats.grall_adjpicalc" does not exist
So at this point I have to deploy every time to test new ideas. I know PG in development would be ideal, but I lost an entire week trying to change over and couldn't get it to work. ended up losing everything and had to reseed from scratch.
I have 3 questions:
What am I doing wrong with the Select statement?
Is there another fast way to do this that avoids this issue? I was thinking instead of Distinct, perhaps I could go with uniq, converting to an array then sort the array accordingly.
How can I get the variable
sort_column
into the select statement?
Any thoughts or suggestions are much appreciated!