So I'm struggling with what seems to be a common problem. I developed my rails app with SQLite and have now pushed it to Heroku (postgreSQL) and encountered this error:
GroupingError: ERROR: column "projects.id" must appear in the GROUP BY clause or be used in an aggregate function
as a result of my code:
#projects = #user.projects.select('MAX(id), *').group(:unique_id).all.paginate(page: params[:page])
where I pull all of a user's projects. unique_id identifies each project and I use MAX(id)as a way of pulling the most recent version of the project.
How can I change my code so that it works is both SQLite for development and postgreSQL for production? I've looked at several other questions that deal with this error but haven't found a great solution for what I'm doing. I'm inclined to use distinct instead of group but I'm not sure how. Thanks in advance.
I think your problem is the "*" in the select method. If you replace that with "unique_id", you should satisfy the SQL parser.
#projects = #user.projects.select('MAX(id), unique_id').group(:unique_id).all.paginate(page: params[:page])
It would look like this. Select max(id), unique_id, other_column1, other_column2, etc... You then will have to group by the other columns.
Related
I keep getting this error when we upgraded our codebase to searchkick 4.4. I have tried to search online but to no avail.
So we have an Opportunity Model where we're calling search on a collection of ActiveRecord objects like so to return our search results:
results = r.search q, fields: [:search_term], match: :word_middle, order: { created_at: :desc}
r is the said collection we're calling search on. Is this the reason why this error is throwing? if it is, how do i go around it, we're doing a lot conditional checks and queries on the queries before we run the search.
Yes, that's exactly the reason why you see this warning. There will not be option to search on relation in next major version. It's breaking change so you need to update your code to follow where section if you are going to update the gem to higher version in the future. You simply need to translate your relation to be included in where part.
I want to achieve the following:
Model.where("asdasd = ? AND to <= ?", nil, Time.now).each do |model|
I get the following error:
ActiveRecord::StatementInvalid: SQLite3::SQLException: near "to": syntax error: SELECT "bla".* FROM "table" WHERE (asdasd = NULL AND to <= '2015-11-09 10:18:14.777643')
I also use the same in another controller, where I get the same error. What would be the correct way to achieve what I want? I am also quite sure that it worked already like that, could there be any other thing which causes this issue?
Thanks!!!
TO is an SQL keyword, and needs to be quoted in a query. the list of SQLite keywords is avaiable in the SQLite documentation.
Other database engines have similar (but not identical) rules and lists of keywords, so if you change from SQLite then check the rules for whatever the new engine is.
How can I modify a where/like condition on a search query in Rails:
find(:all, :conditions => ["lower(name) LIKE ?", "%#{search.downcase}%"])
so that the results are matched irrespective of accents? (eg métro = metro). Because I'm using utf8, I can't use "to_ascii". Production is running on Heroku.
Proper solution
Since PostgreSQL 9.1 you can just:
CREATE EXTENSION unaccent;
Provides a function unaccent(), doing what you need (except for lower(), just use that additionally if needed). Read the manual about this extension.
More about unaccent and indexes:
Does PostgreSQL support "accent insensitive" collations?
Poor man's solution
If you can't install unacccent, but are able to create a function. I compiled the list starting here and added to it over time. It is comprehensive, but hardly complete:
CREATE OR REPLACE FUNCTION lower_unaccent(text)
RETURNS text
LANGUAGE sql IMMUTABLE STRICT AS
$func$
SELECT lower(translate($1
, '¹²³áàâãäåāăąÀÁÂÃÄÅĀĂĄÆćčç©ĆČÇĐÐèéêёëēĕėęěÈÊËЁĒĔĖĘĚ€ğĞıìíîïìĩīĭÌÍÎÏЇÌĨĪĬłŁńňñŃŇÑòóôõöōŏőøÒÓÔÕÖŌŎŐØŒř®ŘšşșߊŞȘùúûüũūŭůÙÚÛÜŨŪŬŮýÿÝŸžżźŽŻŹ'
, '123aaaaaaaaaaaaaaaaaaacccccccddeeeeeeeeeeeeeeeeeeeeggiiiiiiiiiiiiiiiiiillnnnnnnooooooooooooooooooorrrsssssssuuuuuuuuuuuuuuuuyyyyzzzzzz'
));
$func$;
Your query should work like that:
find(:all, :conditions => ["lower_unaccent(name) LIKE ?", "%#{search.downcase}%"])
For left-anchored searches, you can use an index on the function for very fast results:
CREATE INDEX tbl_name_lower_unaccent_idx
ON fest (lower_unaccent(name) text_pattern_ops);
For queries like:
SELECT * FROM tbl WHERE (lower_unaccent(name)) LIKE 'bob%';
Or use COLLATE "C". See:
PostgreSQL LIKE query performance variations
Is there a difference between text_pattern_ops and COLLATE "C"?
For those like me who are having trouble on add the unaccent extension for PostgreSQL and get it working with the Rails application, here is the migration you need to create:
class AddUnaccentExtension < ActiveRecord::Migration
def up
execute "create extension unaccent"
end
def down
execute "drop extension unaccent"
end
end
And, of course, after rake db:migrate you will be able to use the unaccent function in your queries: unaccent(column) similar to ... or unaccent(lower(column)) ...
First of all, you install postgresql-contrib. Then you connect to your DB and execute:
CREATE EXTENSION unaccent;
to enable the extension for your DB.
Depending on your language, you might need to create a new rule file (in my case greek.rules, located in /usr/share/postgresql/9.1/tsearch_data), or just append to the existing unaccent.rules (quite straightforward).
In case you create your own .rules file, you need to make it default:
ALTER TEXT SEARCH DICTIONARY unaccent (RULES='greek');
This change is persistent, so you need not redo it.
The next step would be to add a method to a model to make use of this function.
One simple solution would be defining a function in the model. For instance:
class Model < ActiveRecord::Base
[...]
def self.unaccent(column,value)
a=self.where('unaccent(?) LIKE ?', column, "%value%")
a
end
[...]
end
Then, I can simply invoke:
Model.unaccent("name","text")
Invoking the same command without the model definition would be as plain as:
Model.where('unaccent(name) LIKE ?', "%text%"
Note: The above example has been tested and works for postgres9.1, Rails 4.0, Ruby 2.0.
UPDATE INFO
Fixed potential SQLi backdoor thanks to #Henrik N's feedback
There are 2 questions related to your search on the StackExchange:
https://serverfault.com/questions/266373/postgresql-accent-diacritic-insensitive-search
But as you are on Heroku, I doubt this is a good match (unless you have a dedicated database plan).
There is also this one on SO: Removing accents/diacritics from string while preserving other special chars.
But this assumes that your data is stored without any accent.
I hope it will point you in the right direction.
Assuming Foo is the model you are searching against and name is the column. Combining Postgres translate and ActiveSupport's transliterate. You can do something like:
Foo.where(
"translate(
LOWER(name),
'âãäåāăąÁÂÃÄÅĀĂĄèééêëēĕėęěĒĔĖĘĚìíîïìĩīĭÌÍÎÏÌĨĪĬóôõöōŏőÒÓÔÕÖŌŎŐùúûüũūŭůÙÚÛÜŨŪŬŮ',
'aaaaaaaaaaaaaaaeeeeeeeeeeeeeeeiiiiiiiiiiiiiiiiooooooooooooooouuuuuuuuuuuuuuuu'
)
LIKE ?", "%#{ActiveSupport::Inflector.transliterate("%qué%").downcase}%"
)
I'm doing queries like this in Rails 3.x
Speaker.where("name like '%yson%'")
but I'd love to avoid the DB specific code. What's the right way to do this?
If there's a way to do this in Rails 2.x too, that would help too.
In Rails 3 or greater
Speaker.where("name LIKE ?", "%yson%")
In Rails 2
Speaker.all(:conditions => ["name LIKE ?", "%yson%"])
Avoid to directly interpolate strings because the value won't be escaped and you are vulnerable to SQL injection attacks.
You can use .matches for it.
> t[:name].matches('%lore').to_sql
=> "\"products\".\"name\" LIKE '%lore'"
Actual usage in a query would be:
Speaker.where(Speaker.arel_table[:name].matches('%lore'))
Use a search engine like solr or sphinx to create indexes for the columns you would be performing like queries on. Like queries always result in a full table scan when you look at the explain plan so you really should almost never use them in a production site.
Not by default in Rails, since there are so many DB options (MySQL, Postgresql, MongoDB, CouchDB...), but you can check out gems like MetaWhere, where you can do things like:
Article.where(:title.matches => 'Hello%', :created_at.gt => 3.days.ago)
=> SELECT "articles".* FROM "articles" WHERE ("articles"."title" LIKE 'Hello%')
AND ("articles"."created_at" > '2010-04-12 18:39:32.592087')
In general though you'll probably have to have some DB specific code, or refactor your code (i.e redefine the .matches operator on symbols in MetaWhere) to work with a different database. Hopefully you won't be changing your database that often, but if you are you should have a centralized location where you define these operators for re-use. Keep in mind that an operator or function defined in one database might not be available in another, in which case having this generalized operation is moot since you won't be able to perform the search anyways.
I was wondering if you could list/examine what databases/objects are available to you in the Rails console. I know you can see them using other tools, I am just curious.
Thanks.
You are probably seeking:
ActiveRecord::Base.connection.tables
and
ActiveRecord::Base.connection.columns('projects').map(&:name)
You should probably wrap them in shorter syntax inside your .irbrc.
I hope my late answer can be of some help.
This will go to rails database console.
rails db
pretty print your query output
.headers on
.mode columns
(turn headers on and show database data in column mode )
Show the tables
.table
'.help' to see help.
Or use SQL statements like 'Select * from cars'
To get a list of all model classes, you can use ActiveRecord::Base.subclasses e.g.
ActiveRecord::Base.subclasses.map { |cl| cl.name }
ActiveRecord::Base.subclasses.find { |cl| cl.name == "Foo" }
You can use rails dbconsole to view the database that your rails application is using. It's alternative answer rails db. Both commands will direct you the command line interface and will allow you to use that database query syntax.
Run this:
Rails.application.eager_load!
Then
ActiveRecord::Base.descendants
To return a list of models/tables
Its a start, it can list:
models = Dir.new("#{RAILS_ROOT}/app/models").entries
Looking some more...