17

如何在 Rails 中修改搜索查询的 where/like 条件:

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

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

4

5 回答 5

32

适当的解决方案

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". 看:

于 2012-02-13T23:54:37.823 回答
21

对于像我这样在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)) ...

于 2015-07-04T23:31:56.980 回答
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 后门

于 2013-12-24T23:03:27.470 回答
2

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

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

在 SO 上也有这个:Removing accents/diacritics from string 同时保留其他特殊字符

但这假设您的数据存储时没有任何重音。

我希望它会为您指明正确的方向。

于 2012-02-12T10:12:48.377 回答
0

假设Foo是您正在搜索的模型并且name是列。结合 Postgres translate和 ActiveSupport 的transliterate。您可以执行以下操作:

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