ActiveRecord column does not exist - ruby-on-rails

I have a Rails app using a Postgres database with a table called geolite_blocks. If I call ActiveRecord like this:
GeoliteBlock.find_by_startIpNum 2776360991
The query works perfectly. However, if I do the query like this:
GeoliteBlock.where("startIpNum >= ?", 2776360991)
I get this error:
ActiveRecord::StatementInvalid: PGError: ERROR: column "startipnum" does not exist
LINE 1: ... "geolite_blocks".* FROM "geolite_blocks" WHERE (startIpNum...
^
: SELECT "geolite_blocks".* FROM "geolite_blocks" WHERE (startIpNum >= 2776360991)
But I know that the column exists because I just queried by it with the first code example. Any ideas as to why this might be happening, and how I can eliminate it? Thanks for any help!

Column names in SQL are case insensitive unless they were quoted when they were created. Someone created your startIpNum column with quotes around it so you have to quote it every time you use it:
GeoliteBlock.where('"startIpNum" >= ?', 2776360991)
The error you're getting from PostgreSQL mentions startipnum because PostgreSQL normalizes identifiers to lower case (the SQL standard says that they should be normalized to upper case though).
This:
GeoliteBlock.find_by_startIpNum 2776360991
works because AR will quote the startIpNuM part behind your back. Similarly, GeoliteBlock.where(:startIpNum => 2776360991) would also work.
I'd recommend that you change the schema to use lower case column names so that you won't have to worry about this anymore.

Related

Combining distinct with another condition

I'm migrating a Rails 3.2 app to Rails 5.1 (not before time) and I've hit a problem with a where query.
The code that works on Rails 3.2 looks like this,
sales = SalesActivity.select('DISTINCT batch_id').where('salesperson_id = ?', sales_id)
sales.find_each(batch_size: 2000) do |batchToProcess|
.....
When I run this code under Rails 5.1, it appears to cause the following error when it attempts the for_each,
ArgumentError (Primary key not included in the custom select clause):
I want to end up with an array(?) of unique batch_ids for the given salesperson_id that I can then traverse, as was working with Rails 3.2.
For reasons I don't understand, it looks like I might need to include the whole record to traverse through (my thinking being that I need to include the Primary key)?
I'm trying to rephrase the 'where', and have tried the following,
sales = SalesActivity.where(salesperson_id: sales_id).select(:batch_id).distinct
However, the combined ActiveRecordQuery applies the DISTINCT to both the salesperson_id AND the batch_id - that's #FAIL1
Also, because I'm still using a select (to let distinct know which column I want to be 'distinct') it also still only selects the batch_id column of course, which I am trying to avoid - that's #FAIL2
How can I efficiently pull all unique batch_id records for a given salesperson_id, so I can then for_each them?
Thanks!
How about:
SalesActivity.where(salesperson_id: sales_id).pluck('DISTINCT batch_id')
May need to change up the ordering of where and pluck, but pluck should return an array of the batch_ids

Order by Nearest using PostGIS, RGeo, Spatial Adapter

I'm asking this question because the answers I've found in Order by nearest - PostGIS, GeoRuby, spatial_adapter wasn't able to provide a solution. I'm trying to create a controller method that can return the n closest records to a certain record's lonlat. The record it is querying against is from the same table. This concept isn't such a big stretch if I was doing this completely in SQL. That much is clear in the linked example and below is a specific case where I obtained a result:
condos_development=#
SELECT id, name FROM condos
ORDER BY ST_Distance(condos.lonlat, ST_PointFromText('POINT(-71.06 42.45)'))
condos_development-#
LIMIT 5;
My problem is in making this work with ActiveRecord. I'm using a method that was inspired by the response by #dc10 but I'm unable to create a working query either through the RGeo methods, or direct SQL. Here's what I have so far:
def find_closest_condos(num, unit)
result = Condo.order('ST_Distance(condos.lonlat, ST_PointFromText("#{unit.lonlat.as_text)}")')
.limit(5)
end
The response from this attempt is as follows:
ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: syntax error
at or near "LIMIT" 10:29:50 rails.1 LINE 1: ...lonlat,
ST_PointFromText("#{unit.lonlat.as_text)}") LIMIT $1
Would someone be able to set me on the right track on how to put this work query together so that I can make it work in Rails?
The problem is with how active record is resolving your query to SQL, also the position of the Limit clause. If you change the query to this:
Condo.order("ST_Distance(lonlat, ST_GeomFromText('#{unit.lonlat.as_text}', 4326))")
.limit(num)
You should find this works.

column "pg_search_***" must appear in the GROUP BY clause or be used in an aggregate function

Tool.select('tools.id, tools.name').search('f')
the above query works fine but
Tool.select('tools.id, tools.name').group('tools.id').search('f')
produces the error
ActiveRecord::StatementInvalid: PG::GroupingError: ERROR: column
"pg_search_3aaef8932e30f4464f664f.pg_search_469c73b9b63bebacc2607f"
must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: ...xt, '')), 'D') || to_tsvector('english',
coalesce(pg_search_...
I am using pg_search(https://github.com/Casecommons/pg_search) gem for full text search..
I am not able to figure out the reason even tried adding
group("tools.id, tools.*, #{PgSearch::Configuration.alias('tools')}.rank")
As mentioned in the read me but still same error.
What is the proper way to frame the query?
PG requires all selected fields to be either present in the GROUP BY clause or in an aggregate function(eg: max, min) - if you're grouping by anything, of course.
I'm guessing a pg_search gem migration created a field named pg_search_469c73b9b63bebacc2607f on a table named pg_search_3aaef8932e30f4464f664f and that is part of your query, but isn't part of the GROUP BY clause.
I can't see how #{PgSearch::Configuration.alias('tools')}.rank would translate to pg_search_3aaef8932e30f4464f664f.pg_search_469c73b9b63bebacc2607f
Try using group("tools.id, tools.name, pg_search_3aaef8932e30f4464f664f.pg_search_469c73b9b63bebacc2607f") and see if you get rid of that error. If it works, try to find a programatic way of retrieving that pg_search_469c73b9b63bebacc2607f field name.
Given your example, I don't think grouping by tools.id is doing you any good, assuming id is unique. If you were joining with something else it could make sense, though.

Quote column name

I have a table containing 2 "uncommon" columns - order and like.
The table are replicated on two databases - MySQL and PostgreSQL.
And i need same app to connect to both databases and use same query on both:
PageModel.where("`like` >= ?", params[:liked])
This will work in MySQL only.
How to make ActiveRecord to quote the column name?
Something like:
PageModel.where("%s >= ?" % quote_column_name(:like), params[:liked])
I found a method that is useless for now - it just returns the column name without quote it.
http://www.rubydoc.info/docs/rails/3.2.8/ActiveRecord/ConnectionAdapters/Quoting:quote_column_name
Perhaps it is just a placeholder and there are another method that does this?
This depends from from one database adapter to the other. So basically, each adapter overrides this method with their own definition.
You're interested in:
ActiveRecord::Base.connection.quote_column_name "my_column"

how to query for activerecord's select_value method?

can anyone please tell me how to write query in select_value.
I have tried,
ActiveRecord::Base.connection.select_value("select count(*) from leave_details where status= 'Pending' and 'employeedetails_id'=25")
but it showing error
invalid input syntax for integer: "employeedetails_id".
I am using PostgreSQL.
Single quotes are used to quote strings in PostgreSQL (and every other SQL database that even pretends to respect the SQL standard) so you're saying something like this:
some_string = some_integer
when you do this:
'employeedetails_id'=25
and that doesn't make any sense: you can't compare strings and integers without an explicit type cast. You don't need to quote that identifier at all:
ActiveRecord::Base.connection.select_value(%q{
select count(*)
from leave_details
where status = 'Pending'
and employeedetails_id = 25
})
If you even do need to quote an identifier (perhaps it is case sensitive or contains spaces), then you'd use double quotes with PostgreSQL.
Apparently you created your column as "EmployeeDetails_id" so that it is case sensitive. That means that you always have to use that case and you always have to double quote it:
ActiveRecord::Base.connection.select_value(%q{
select count(*)
from leave_details
where status = 'Pending'
and "EmployeeDetails_id" = 25
})
I'd recommend reworking your table to not use mixed case identifiers:
They go against standard Ruby/Rails naming.
They force you to double quote the mixed case column names everywhere you use them.
They go against standard PostgreSQL practice.
This is going to trip you up over and over again.
Executing SQL directly isn't really The Rails Way, and you lose any database portability by doing it that way.
You should create a model for leave_details. E.g.
rails g model LeaveDetails status:string employeedetails_id:integer
Then, the code would be:
LeaveDetails.where({ :status => 'Pending', :employeedetails_id => 25 }).count

Resources