SQLite3“LIKE”或PostgreSQL“ILIKE”的通用Ruby解决方案?

我使用SQLite3进行开发,使用PostgreSQL进行部署。 但是,我面临以下问题:

我使用SQLite3简单搜索:

 def self.search(search) if search find(:all, :conditions => ["style LIKE ? OR construction LIKE ?", "%#{search}%", "%#{search}%"]) else find(:all) end end 

但是,它不适用于PostgreSQL ,我需要替换ILIKELIKE才能解决问题:

 def self.search(search) if search find(:all, :conditions => ["style ILIKE ? OR construction ILIKE ?", "%#{search}%", "%#{search}%"]) else find(:all) end end 

是否有“Ruby方式”在任何数据库中进行这些搜索?

编辑 – 基于您的答案我不相信我会找到一个通用的Ruby解决方案。

我遵循了Ruby on Rails教程:通过示例学习Rails – 作者:Michael Hartl ,最终的Gemfile显示了两个数据库……好吧,令人失望……

问题根源在于:

我使用SQLite3进行开发,使用PostgreSQL进行部署。

这是一个坏主意™。 你将继续遇到不兼容问题 – 或者更糟糕的是:在完成损害之前没有意识到这一点。
使用相同的RDBMS(PostgreSQL)进行开发和生产,为您节省无意义的麻烦。


当你遇到不幸的设置时,有一个简单的解决方法

 lower(style) LIKE lower(?) 

适用于两个平台。

  • 如果提供小写搜索模式,则可以删除lower()

  • 在标准SQLite lower(X)只折叠ASCII字母。 有关更多信息,请引用SQLite手册中的核心函数一章:

    lower(X)函数返回字符串X的副本,所有ASCII字符都转换为小写。 默认的内置lower()函数仅适用于ASCII字符。 要对非ASCII字符进行大小写转换,请加载ICU扩展名

    强调我的。

  • PostgreSQL lower(X)可与UTF-8一起使用。


作为一个受欢迎的副作用,您可以使用表达式 lower(style) 上的索引 加速 PostgreSQL中的查询,这将比使用ILIKEstyle的基本索引更快。

此外,由于PostgreSQL 9.1,您可以使用带有pg_trgm扩展名的GIN或GIST索引来加速任何 LIKEILIKE查询 – pg_trgm不区分大小写。 此相关答案中的详细说明和链接:

  • 用于自动完成字段的类似UTF-8字符串

我认为Arel是解决这个问题的最佳方法。 它被Rails用于活动记录并且与数据库无关。 您的代码在sqlite3或postgres中的工作方式相同,这似乎适合您的情况。 使用匹配方法将自动切换到postgres环境中的ilike。 例:

 users=User.arel_table User.where(users[:style].matches("%#{search}%").or(users[:construction].matches("%#{search}%"))) 

您可以从github获取更多信息: https : //github.com/rails/arel/

不,没有“ruby方式”来搜索数据库 – Ruby on Rails(特别是ActiveRecord )包含用于在ActiveRecord支持的RDB上执行CRUD操作的辅助方法,但是没有更好的方法来使用LIKE进行搜索然后示例你提供。

与此讨论相关的Rails文档部分将是ActiveRecord :: FinderMethods 。

作为旁注,而不是做find(:all)你可以做所有 。

Rails文档使用与用于执行LIKE语句相同的语法,即:

 Person.exists?(['name LIKE ?', "%#{query}%"]) 

使用上述方法是非常安全的。

下面这样的语句不安全的原因是因为where字符串直接传递到数据库查询而没有任何清理,这使得数据库处于开放状态(即params[:first_name]的简单撇号可能会搞乱整个查询并使您的数据库易受攻击 – 特别是SQL注入。 在上面的示例中,ActiveRecord可以清理您传递给查询的参数。

 Client.where("first_name LIKE '%#{params[:first_name]}%'") 

不幸的是,我认为你不会找到一个很好的解决方案。 您可以使用的唯一其他语法,而不是:conditions,是使用.where子句:

 where(["style ILIKE ? OR construction ILIKE ?", "%#{search}%", "%#{search}%"]) 

不幸的是,正如您可能已经意识到的那样,这种替代语法将遇到完全相同的问题。 这只是您在生产环境中拥有截然不同的开发环境时遇到的烦恼之一 – 在SQLite和PostgresSQL之间还存在其他相当大的差异。 我建议只在你的开发机器上安装Postgres并使用它。 它将使开发变得更加容易,并使您的代码更清洁。

虽然在生产和开发中使用不同的数据库不是一个好习惯,但使用squeel gem仍然是一个好例子: https : //github.com/ernie/squeel/

有了它,你可以使用DSL编写查询,这个DSL比原始的sql更容易,也可以说更清晰和可读,并且gem将处理它对特定于所用RDBMS的SQL的翻译。

Ryan Bates有一个很好的video: http : //railscasts.com/episodes/354-squeel

我曾经遇到过同样的问题,这是我的解决方案:

我写了一个小lib,可以为每个数据库使用该函数:

 class AdapterSpecific class << self def like_case_insensitive case ActiveRecord::Base.connection.adapter_name when 'PostgreSQL' 'ILIKE' else 'LIKE' end end def random #something end end 

要在您的模型中使用它:

 def self.search(search) if search find(:all, :conditions => ["style #{AdapterSpecific.like_case_insensitive} :query OR construction #{AdapterSpecific.like_case_insensitive} :query", {:query => "%#{search}%"}]) else find(:all) end end 

自编写以来,我将部署数据库迁移到Postgres,因为这是一个更好的设置,原因有几个(参见其他答案)。

您也可以考虑使用Postgres的全文搜索,这将使您的文本搜索更加高效,如果您需要更多自定义,请参阅基本实现的texticle或pg_search 。

使用LIKE进行搜索可能会对数据库造成痛苦。 当然它不会使用一个成本过高的指数。

更长的答案:我建议在开发中使用Postgres(放弃sqlite3),然后在所有可搜索的字段style, construction上使用全文索引style, construction通过Postgres的tsvector类型style, construction

使用索引时Postgres中的全文搜索非常快。