非常简单的问题 - 我如何进行搜索以查找名称以ActiveRecord中的某个字符串开头的所有记录。我已经在互联网上看到了各种各样的比特,其中使用了逐字的LIKE SQL条款 - 但是从我所听到的那些不是'正确'的方式。
是否有'正确'的Rails方式?
答案 0 :(得分:98)
如果您要在数据库中进行搜索,则需要使用SQL。
当然,你需要在数据库中进行搜索,否则你需要将所有对象加载到Ruby中(这不是一件好事)。
所以,你需要像
这样的东西MyModel.find(:all, :conditions => ["field LIKE ?", "#{prefix}%"])
其中prefix
是一个带有您正在寻找的字符串的变量。
在Rails 3.0+中,这变为:
MyModel.where("field LIKE :prefix", prefix: "#{prefix}%")
答案 1 :(得分:11)
我强烈推荐Searchlogic插件。
然后它就像:
一样简单@search = Model.new_search(params[:search])
@search.condition.field_starts_with = "prefix"
@models = @search.all
Searchlogic足够智能,如ActiveRecord,可以在starts_with
条件下获取字段名称。它还将处理所有分页。
此方法有助于防止SQL注入,也将与数据库无关。 Searchlogic最终会根据您使用的数据库适配器以不同方式处理搜索。您也不必编写任何SQL!
Searchlogic有很好的文档,并且易于使用(我自己是Ruby和Rails的新手)。当我遇到困难时,该插件的作者甚至在几个小时内回复了一封直接电子邮件,帮助我解决了我的问题。我不能推荐Searchlogic ......正如你所知道的那样。
答案 2 :(得分:8)
免责声明:我是Ruby和Rails的新手,我仍然在努力学习Ruby方法来做事,但是我已经编写了超过一半的生命,并且专业地编写了十年。 DRY是我非常熟悉的概念。这是我实现它的方式。这与narsk的答案非常相似,我认为这也很好。
# name_searchable.rb
# mix this into your class using
# extend NameSearchable
module NameSearchable
def search_by_prefix (prefix)
self.where("lower(name) LIKE '#{prefix.downcase}%'")
end
end
然后在你的模型中:
class User < ActiveRecord::Base
extend NameSearchable
...
end
然后当你想要使用它时:
User.search_by_prefix('John') #or
User.search_by_prefix("#{name_str}")
要说的一件事:
传统的关系数据库并不能很好地满足这些类型的查询。如果您正在寻找一种不会在负载下杀死数据库的高响应性实现,那么您应该使用针对此目的而定制的解决方案。流行的例子包括Solr或Sphinx,还有很多其他的例子。有了这个实现,既然你干了,你可以在一个地方用一个单独的索引器替换实现,你就可以摇滚了。
答案 3 :(得分:7)
每次我想查看特定列是否以前缀开头时,我都不想写一个范围。
# initializers/active_record_initializers.rb
class ActiveRecord::Base
# do not accept a column_name from the outside without sanitizing it
# as this can be prone to sql injection
def self.starts_with(column_name, prefix)
where("lower(#{column_name}) like ?", "#{prefix.downcase}%")
end
end
这样称呼:
User.starts_with('name', 'ab').limit(1)
答案 4 :(得分:4)
非常类似于上面的答案,但如果你使用ActiveRecord ... 2.1或更高版本,你可以使用一个命名范围,它允许你在通过关联检索的记录上使用这个“finder”:
class User
named_scope :with_name_like, lambda {|str|
:conditions => ['lower(name) like ?', %(%#{str.downcase}%)]
}
end
用过:
User.with_name_like("Samson")
(使用“Samson”等名称返回数据库中的所有用户。
或者:
some_association.users.with_name_like("Samson")
只有“Samson”之类的名称才能关闭该关联。
答案 5 :(得分:2)
现在是2012年,我以为我会把这个更新投入到narsk的回答中。
named_scope
前一段时间变得简单scope
所以示例变为
class User
scope :name_starts_with, lambda {|str|
:conditions => ['lower(name) like ?', "#{str.downcase}%"]
}
end
注意我从narsk的解决方案中移除了第一个%
,因为OP要求'starts_with'而不是'包含'匹配。
现在你可以做像
这样的事情User.name_starts_with("b").each do {|bs| puts bs.inspect}
bob = User.name_starts_with("bob").first
… etc
请注意,范围始终返回集合,而不是单个结果。当我第一次开始使用AR时,这让我感到震惊,我仍然不时地忘记这一点。
答案 6 :(得分:1)
Dave Sag,我想你的代码的第一部分应该是
class User
scope :name_starts_with, (lambda do |str|
{:conditions => ['lower(name) like ?', "#{str.downcase}%"]}
end )
end
答案 7 :(得分:1)
同一件事的非常简洁的语法可能是:
Model.where(field: ('prefix'...'prefiy'))
这呈现:
WHERE ( field >= 'prefix' AND field < 'prefiy')
'prefiy'是第一个按字母顺序不匹配'前缀'前缀的字符串。
我不会正常使用它(我会选择使用squeel或ransack),但如果由于某种原因你必须坚持查询的哈希语法,它可以节省你的一天......