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
,我需要替换ILIKE
的LIKE
才能解决问题:
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中的查询,这将比使用ILIKE
和style
的基本索引更快。
此外,由于PostgreSQL 9.1,您可以使用带有pg_trgm
扩展名的GIN或GIST索引来加速任何 LIKE
和ILIKE
查询 – 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中的全文搜索非常快。
- 将pg_try_advisory_xact_lock()放在嵌套的子查询中?
- 无法安装pg gem,“mkmf.rb无法找到ruby的头文件”(Mac OSX 10.6.5)
- 在Heroku上,“PGError:致命:由于管理员命令终止连接”的原因是什么?
- Ruby on Rails:有没有办法从数据库中提取项目并按指定顺序返回它们?
- Rails连接到database.yml指定的数据库
- 在rails4中设置hstore,动态键/值
- Postgres查询包含内容的JSON数组
- rails migration:postgresql for md5 of random string as default
- ActiveRecord:如何找到所有孩子符合条件的父母?