2012-02-11 44 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}%" 
)