ActiveRecord查找以。开头

时间:2008-10-30 19:05:49

标签: ruby-on-rails ruby activerecord

非常简单的问题 - 我如何进行搜索以查找名称以ActiveRecord中的某个字符串开头的所有记录。我已经在互联网上看到了各种各样的比特,其中使用了逐字的LIKE SQL条款 - 但是从我所听到的那些不是'正确'的方式。

是否有'正确'的Rails方式?

8 个答案:

答案 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),但如果由于某种原因你必须坚持查询的哈希语法,它可以节省你的一天......