如何在 Rails 中修改搜索查询的 where/like 条件:
find(:all, :conditions => ["lower(name) LIKE ?", "%#{search.downcase}%"])
这样无论口音如何,结果都匹配?(例如地铁 = 地铁)。因为我使用的是utf8,所以不能使用“to_ascii”。生产在 Heroku 上运行。
如何在 Rails 中修改搜索查询的 where/like 条件:
find(:all, :conditions => ["lower(name) LIKE ?", "%#{search.downcase}%"])
这样无论口音如何,结果都匹配?(例如地铁 = 地铁)。因为我使用的是utf8,所以不能使用“to_ascii”。生产在 Heroku 上运行。
从PostgreSQL 9.1开始,您可以:
CREATE EXTENSION unaccent;
提供一个功能unaccent()
,做你需要的(除了lower()
,如果需要,只需额外使用它)。阅读有关此扩展的手册。
有关非重音和索引的更多信息:
如果您无法安装unacccent
,但能够创建功能。我从这里开始编译列表并随着时间的推移添加到其中。它很全面,但几乎不完整:
CREATE OR REPLACE FUNCTION lower_unaccent(text)
RETURNS text
LANGUAGE sql IMMUTABLE STRICT AS
$func$
SELECT lower(translate($1
, '¹²³áàâãäåāăąÀÁÂÃÄÅĀĂĄÆćčç©ĆČÇĐÐèéêёëēĕėęěÈÊËЁĒĔĖĘĚ€ğĞıìíîïìĩīĭÌÍÎÏЇÌĨĪĬłŁńňñŃŇÑòóôõöōŏőøÒÓÔÕÖŌŎŐØŒř®ŘšşșߊŞȘùúûüũūŭůÙÚÛÜŨŪŬŮýÿÝŸžżźŽŻŹ'
, '123aaaaaaaaaaaaaaaaaaacccccccddeeeeeeeeeeeeeeeeeeeeggiiiiiiiiiiiiiiiiiillnnnnnnooooooooooooooooooorrrsssssssuuuuuuuuuuuuuuuuyyyyzzzzzz'
));
$func$;
您的查询应该像这样工作:
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)) LIKE 'bob%';
或使用COLLATE "C"
. 看:
对于像我这样在unaccent
为 PostgreSQL 添加扩展并使其与 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)) ...
首先,你安装 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 后门
有 2 个与您在 StackExchange 上的搜索相关的问题: https ://serverfault.com/questions/266373/postgresql-accent-diacritic-insensitive-search
但是当你在 Heroku 上时,我怀疑这是一个很好的匹配(除非你有一个专门的数据库计划)。
在 SO 上也有这个:Removing accents/diacritics from string 同时保留其他特殊字符。
但这假设您的数据存储时没有任何重音。
我希望它会为您指明正确的方向。
假设Foo
是您正在搜索的模型并且name
是列。结合 Postgres translate和 ActiveSupport 的transliterate。您可以执行以下操作:
Foo.where(
"translate(
LOWER(name),
'âãäåāăąÁÂÃÄÅĀĂĄèééêëēĕėęěĒĔĖĘĚìíîïìĩīĭÌÍÎÏÌĨĪĬóôõöōŏőÒÓÔÕÖŌŎŐùúûüũūŭůÙÚÛÜŨŪŬŮ',
'aaaaaaaaaaaaaaaeeeeeeeeeeeeeeeiiiiiiiiiiiiiiiiooooooooooooooouuuuuuuuuuuuuuuu'
)
LIKE ?", "%#{ActiveSupport::Inflector.transliterate("%qué%").downcase}%"
)