【问题标题】:Postgres accent insensitive LIKE search in Rails 3.1 on HerokuHeroku 上 Rails 3.1 中 Postgres 口音不敏感的 LIKE 搜索
【发布时间】:2012-02-11 19:24:56
【问题描述】:

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

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

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

【问题讨论】:

  • 我想知道,你用的是什么解决方案?是否有仅基于 Rails 的解决方案?谢谢!

标签: ruby-on-rails postgresql rails-postgresql unaccent


【解决方案1】:

适当的解决方案

PostgreSQL 9.1 开始,您可以:

CREATE EXTENSION unaccent;

提供一个函数unaccent(),做你需要的事情(lower() 除外,如果需要,只需额外使用它)。阅读manual about this extension

更多关于非重音和索引:

穷人的解决办法

如果您无法安装unacccent,但能够创建功能。我从here 开始编译列表并随着时间的推移添加到其中。它很全面,但并不完整:

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"。见:

【讨论】:

  • 嗨欧文,谢谢。我在 9.1 所以 CREATE EXTENSION 不重音;似乎是前进的方向。你如何建议我通过我的 rails 应用程序激活它(因为我需要在 heroku 以及我的开发环境中发生这种情况)......谢谢!
  • 如果卡在 9.0,执行 C:\Program Files\PostgreSQL\9.0\share\contrib\unaccent.sql 仍然可以安装 unaccent
  • (3 年后:)Heroku 包含unaccentdevcenter.heroku.com/articles/… 您可以通过运行echo 'show extwlist.extensions' | heroku pg:psql 进行验证
  • 效果很好。我通过迁移将它插入到我的 Rails 应用程序中。这是一个很棒的例子,说明如何做到这一点:stackoverflow.com/questions/16611226/…
【解决方案2】:

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

【讨论】:

  • 通过这样做验证扩展是否不存在将防止迁移崩溃:``` def up execute "create extension if not exist unaccent" end ```
  • 轻微错字-正确的代码是:执行“如果不存在则创建扩展名;” - 存在是复数。见postgresql.org/docs/9.1/sql-createextension.html
【解决方案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 后门

【讨论】:

  • 危险!如果您只是像这样将值字符串插入到 SQL 中,并且该值是用户提供的,那么您将自己暴露在 SQL 注入中。这更安全,因为 Rails 会为您转义:Model.where("unaccent(name) LIKE unaccent(?)", "%#{value}%") 或只是Model.where("unaccent(name) LIKE ?", "%#{value}%"),如果您不关心取消重音值。
  • 你是对的,当然......我现在不会犯这个错误,但这是旧的......我会修复它,感谢您的注意
  • 没问题。嗯,我怀疑使用unaccent(?) 作为列名会将其视为字符串而不是列名,但我不确定。
  • 未测试,我最终使用了lucene solr,但使用字符串作为列名是可以的。
  • 有人在 Cloud9 上试过吗?找不到 usr/share 文件夹...也环顾 cloud9 论坛。还没有什么真正有用的东西。
【解决方案4】:

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

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

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

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

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

【讨论】:

  • 嗨皮埃尔 - 谢谢 - 是的,我看到了这两个,但不幸的是在这种情况下都没有帮助我。
【解决方案5】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-07
    • 2011-02-12
    • 1970-01-01
    • 1970-01-01
    • 2017-08-21
    • 2011-11-22
    • 1970-01-01
    相关资源
    最近更新 更多