2012-02-11 100 views
16

的Rails 3.1如何修改一个地方/像在Rails的搜索查询条件:Postgres的口音不敏感的LIKE搜索中在Heroku

find(:all, :conditions => ["lower(name) LIKE ?", "%#{search.downcase}%"])

,这样的结果是,不论口音的匹配? (例如métro= metro)。因为我使用的是utf8,所以我不能使用“to_ascii”。生产在Heroku上运行。

+1

我想知道,你使用了什么解决方案?是否有仅基于导轨的解决方案?谢谢! – ipegasus 2013-08-02 19:49:21

回答

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.0CREATE EXTENSION语法是9.1中新增的。

更多unaccent和索引:

所有的
+0

嗨Erwin,谢谢你。我在9.1上,所以CREATE EXTENSION unaccent;看起来像前进的道路。你会如何建议我通过我的Rails应用激活它(因为我需要这种情况发生在Heroku以及我的开发环境)......谢谢! – user1051849 2012-02-14 10:20:11

+0

如果你被困在9.0,如果你执行C:\ Program Files \ PostgreSQL \ 9.0 \ share \ contrib \ unaccent.sql – Edo 2014-10-21 13:59:24

+2

(3年后:),Heroku还包括'unaccent':https:/ /devcenter.heroku.com/articles/heroku-postgres-extensions-postgis-full-text-search您可以通过运行'echo'show extwlist.extensions'| heroku pg:psql' – 2015-01-23 20:06:35

2

有2点与您的StackExchange搜索的问题: https://serverfault.com/questions/266373/postgresql-accent-diacritic-insensitive-search

但是当你是在Heroku上,我怀疑这是一个很好的匹配(除非你有一个专门的数据库计划)。

SO上还有这个:Removing accents/diacritics from string while preserving other special chars

但是这里假设你的数据没有任何口音存储。

我希望它能指引您正确的方向。

+0

嗨皮埃尔 - 谢谢 - 是的,我看到了这两个,但不幸的是,在这种情况下,也没有帮助我。 – user1051849 2012-02-12 10:47:38

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。

UPDATE INFO
固定电位SQLI后门感谢@Henrik N为反馈

+0

危险!如果你只是将值插入到SQL中,并且这个值是用户提供的,那么你可以打开自己的SQL注入。这是更安全的,因为Rails会为你逃避:Model.where(“unaccent(name)LIKE unaccent(?)”,“%#{value}%”)或者'Model.where(“unaccent(name) LIKE?“,”%#{value}%“),如果你不关心不值的话。 – 2015-01-23 20:29:45

+0

你是对的,当然...我现在不会这样做,但这是旧的..我会修复它,谢谢注意 – 2015-01-23 21:51:48

+0

没问题。嗯,我怀疑使用'unaccent(?)'列名将它视为一个字符串而不是列名,但我不确定。 – 2015-01-25 15:33:36

13

对于那些像我谁是有麻烦添加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)) ...

0

假设Foo是你正在寻找对抗和name是列模型。结合Postgres translate和ActiveSupport的transliterate。你可以这样做:

Foo.where(
    "translate(
    LOWER(name), 
    'âãäåāăąÁÂÃÄÅĀĂĄèééêëēĕėęěĒĔĖĘĚìíîïìĩīĭÌÍÎÏÌĨĪĬóôõöōŏőÒÓÔÕÖŌŎŐùúûüũūŭůÙÚÛÜŨŪŬŮ', 
    'aaaaaaaaaaaaaaaeeeeeeeeeeeeeeeiiiiiiiiiiiiiiiiooooooooooooooouuuuuuuuuuuuuuuu' 
) 
    LIKE ?", "%#{ActiveSupport::Inflector.transliterate("%qué%").downcase}%" 
)