我使用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
,我需要替换LIKE
ILIKE
来解决问题:
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 Tutorial: Learn Rails by Example - by Michael Hartl,最终Gemfile显示两个数据库......好吧,令人失望......
答案 0 :(得分:45)
问题的根源位于此处:
我使用SQLite3进行开发,使用PostgreSQL进行部署。
这是一个坏主意™。你将继续遇到不相容的情况 - 或者更糟糕的是:在完成损害之前没有意识到这一点 使用相同的RDBMS(PostgreSQL)进行开发和生产,为您节省无意义的麻烦。
当您遇到不幸的设置时,有一个简单修复:
lower(style) LIKE lower(?)
适用于两个平台。
如果您提供小写搜索模式,则可以放弃右侧lower()
。
在标准SQLite lower(X)
中只折叠ASCII字母。更多信息,请引用Core Functions in the SQLite manual一章:
lower(X)函数返回带有all的字符串X的副本 ASCII字符转换为小写。默认内置lower() 函数仅适用于ASCII字符。要进行大小写转换 非ASCII字符,加载ICU扩展名。
强调我的。
PostgreSQL lower(X)
可与开箱即用的UTF-8配合使用。
作为一个受欢迎的副作用,您可以使用index on the expression lower(style)
加速在PostgreSQL中查询该查询,这比使用ILIKE
和style
上的基本索引。
此外,自PostgreSQL 9.1起,您可以使用pg_trgm
extension的GIN或GIST索引来加速任何 LIKE
和ILIKE
查询 - 三元组是大小写不敏感的。相关答案中的详细说明和链接:
答案 1 :(得分:9)
我认为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/
答案 2 :(得分:5)
不,搜索数据库没有“ruby方式” - Ruby on Rails(特别是ActiveRecord)包含帮助方法,用于在ActiveRecord支持的RDB上执行CRUD操作,但是没有更好的方法使用LIKE搜索您提供的示例。
与此讨论相关的Rails文档部分为ActiveRecord::FinderMethods。
作为旁注,您可以find(:all)
而不是Person.exists?(['name LIKE ?', "%#{query}%"])
。
Rails文档使用与用于执行LIKE语句相同的语法,即:
where
使用上述方法非常安全。
下面这样的语句不安全的原因是因为params[:first_name]
字符串直接传递到数据库查询而没有任何清理,这使得数据库可以被利用(即{{1}中的简单撇号可能会破坏整个查询并使数据库易受攻击 - 特别是SQL注入)。在上面的示例中,ActiveRecord可以清理您传递给查询的参数。
Client.where("first_name LIKE '%#{params[:first_name]}%'")
答案 3 :(得分:2)
不幸的是,我不认为你会找到一个很好的解决方案。您可以使用的唯一其他语法,而不是:conditions,是使用.where子句:
where(["style ILIKE ? OR construction ILIKE ?", "%#{search}%", "%#{search}%"])
不幸的是,正如您可能已经意识到的那样,这种替代语法会遇到完全相同的问题。这只是您在生产环境中拥有截然不同的开发环境时遇到的烦恼之一 - SQLite和PostgresSQL之间还存在其他相当大的差异。我建议只在你的开发机器上安装Postgres并使用它。它将使开发变得更加容易,并使您的代码更加清晰。
答案 4 :(得分:2)
虽然在生产和开发中使用不同的数据库不是一个好习惯,但使用squeel
gem仍然是一个好例子:https://github.com/ernie/squeel/
有了它,您可以使用DSL编写查询,这些DSL比原始sql更容易且更清晰可读,并且gem将处理它对特定于所使用的RDBMS的SQL的翻译。
Ryan Bates有一个很好的视频:http://railscasts.com/episodes/354-squeel
答案 5 :(得分:2)
我曾经遇到过同样的问题,这是我的解决方案:
我写了一个小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。
答案 6 :(得分:1)
使用LIKE
进行搜索可能会对数据库造成痛苦。当然,它不会使用一个成本过高的指数。
更长的答案:我建议在开发中使用Postgres(放弃sqlite3),然后通过Postgres'style, construction
类型在所有可搜索字段tsvector
上使用全文索引。
使用索引时Postgres中的全文搜索非常快。