如何在Rails中修改搜索查询的where / like条件:
find(:all, :conditions => ["lower(name) LIKE ?", "%#{search.downcase}%"])
这样无论重音如何,结果都是匹配的? (例如métro= metro)。因为我使用的是utf8,所以我不能使用“to_ascii”。生产正在Heroku上运行。
答案 0 :(得分:28)
如果您能够创建一个功能,则可以使用此功能。我编制了从here开始的列表并随着时间的推移添加到列表中。它非常完整。您甚至可能想要删除一些字符:
CREATE OR REPLACE FUNCTION lower_unaccent(text)
RETURNS text AS
$func$
SELECT lower(translate($1
, '¹²³áàâãäåāăąÀÁÂÃÄÅĀĂĄÆćčç©ĆČÇĐÐèéêёëēĕėęěÈÊËЁĒĔĖĘĚ€ğĞıìíîïìĩīĭÌÍÎÏЇÌĨĪĬłŁńňñŃŇÑòóôõöōŏőøÒÓÔÕÖŌŎŐØŒř®ŘšşșߊŞȘùúûüũūŭůÙÚÛÜŨŪŬŮýÿÝŸžżźŽŻŹ'
, '123aaaaaaaaaaaaaaaaaaacccccccddeeeeeeeeeeeeeeeeeeeeggiiiiiiiiiiiiiiiiiillnnnnnnooooooooooooooooooorrrsssssssuuuuuuuuuuuuuuuuyyyyzzzzzz'
));
$func$ LANGUAGE sql IMMUTABLE;
您的查询应该是这样的:
find(:all, :conditions => ["lower_unaccent(name) LIKE ?", "%#{search.downcase}%"])
对于左锚定搜索,您可以在函数上使用非常快速结果的索引:
CREATE INDEX tbl_name_lower_unaccent_idx
ON fest (lower_unaccent(name) text_pattern_ops);
对于以下查询:
SELECT * FROM tbl WHERE (lower_unaccent(name)) ~~ 'bob%'
在 PostgreSQL 9.1 + 中,拥有必要的权限,您可以:
CREATE EXTENSION unaccent;
提供了一个功能 unaccent()
,执行您需要的功能(lower()
除外,如果需要,只需另外使用)。阅读manual about this extension
也可用于 PostgreSQL 9.0 ,但CREATE EXTENSION
语法是9.1中的新功能。
关于unaccent和索引的更多信息:
答案 1 :(得分:14)
对于那些在为PostgreSQL添加unaccent
扩展程序并使其与Rails应用程序一起工作时遇到问题的人,以下是您需要创建的迁移:
class AddUnaccentExtension < ActiveRecord::Migration
def up
execute "create extension unaccent"
end
def down
execute "drop extension unaccent"
end
end
当然,在rake db:migrate
之后,您可以在查询中使用unaccent
功能:unaccent(column) similar to ...
或unaccent(lower(column)) ...
答案 2 :(得分:3)
首先,安装postgresql-contrib。然后连接到数据库并执行:
CREATE EXTENSION unaccent;
为您的数据库启用扩展程序。
根据您的语言,您可能需要创建一个新规则文件(在我的情况下为greek.rules
,位于/usr/share/postgresql/9.1/tsearch_data
),或者只是附加到现有unaccent.rules
(非常简单) )。
如果您创建自己的.rules
文件,则需要将其设为默认值:
ALTER TEXT SEARCH DICTIONARY unaccent (RULES='greek');
此更改是持久的,因此您无需重做。
下一步是向模型添加一个方法以使用此函数。
一个简单的解决方案是在模型中定义一个函数。例如:
class Model < ActiveRecord::Base
[...]
def self.unaccent(column,value)
a=self.where('unaccent(?) LIKE ?', column, "%value%")
a
end
[...]
end
然后,我可以简单地调用:
Model.unaccent("name","text")
在没有模型定义的情况下调用相同的命令将如下所示:
Model.where('unaccent(name) LIKE ?', "%text%"
注意:上面的示例已经过测试,适用于postgres9.1,Rails 4.0,Ruby 2.0。
更新信息
通过@Henrik N的反馈确定了潜在的SQLi后门
答案 3 :(得分:2)
您在StackExchange上搜索有两个问题: https://serverfault.com/questions/266373/postgresql-accent-diacritic-insensitive-search
但是当你在Heroku上时,我怀疑这是一个很好的匹配(除非你有一个专门的数据库计划)。
SO上也有这个:Removing accents/diacritics from string while preserving other special chars。
但这假设您的数据存储时没有任何重音。
我希望它会指出你正确的方向。
答案 4 :(得分:0)
假设Foo
是您要搜索的模型,name
是列。结合Postgres translate和ActiveSupport的transliterate。你可以这样做:
Foo.where(
"translate(
LOWER(name),
'âãäåāăąÁÂÃÄÅĀĂĄèééêëēĕėęěĒĔĖĘĚìíîïìĩīĭÌÍÎÏÌĨĪĬóôõöōŏőÒÓÔÕÖŌŎŐùúûüũūŭůÙÚÛÜŨŪŬŮ',
'aaaaaaaaaaaaaaaeeeeeeeeeeeeeeeiiiiiiiiiiiiiiiiooooooooooooooouuuuuuuuuuuuuuuu'
)
LIKE ?", "%#{ActiveSupport::Inflector.transliterate("%qué%").downcase}%"
)