本当に簡単な質問-ActiveRecordで名前が特定の文字列で始まるすべてのレコードを検索するにはどうすればよいですか。私はインターネット上で逐語的なLIKE SQL句が使用されているあらゆる種類のビットを見てきました。
「適切な」Rails way?
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自分自身)に慣れていません。)行き詰まったとき、プラグインの作者も私の問題を解決するのに役立つ数時間以内にダイレクトメールに返信しました。
データベースで検索を行う場合は、SQLを使用する必要があります。
そしてもちろん、データベースで検索を行う必要があります。そうでなければ、オブジェクトをロードする必要がありますallオブジェクトをRubyに読み込みます(これは良いことではありません) )。
したがって、次のようなものが必要になります
MyModel.find(:all, :conditions => ["field LIKE ?", "#{prefix}%"])
ここで、prefix
は、探している文字列を含む変数です。
Rails 3.0+では、これは次のようになります:
MyModel.where("field LIKE :prefix", prefix: "#{prefix}%")
免責事項:私は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}")
注意すべき点の1つ:
従来のリレーショナルデータベースは、このような種類のクエリを満足させることはあまりできません。負荷がかかった状態でデータベースを殺さない応答性の高い実装を探している場合は、代わりに目的に合わせたソリューションを使用する必要があります。人気のある例にはSolrやSphinxが含まれますが、他にもたくさんあります。この実装では、DRYなので、実装を1か所で別のインデクサーに置き換えることができ、すぐに使用できるようになります。
特定の列の先頭にプレフィックスが付いているかどうかを確認するたびにスコープを記述したくありません。
# 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)
上記の回答に非常に似ていますが、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")
(「サムソン」のような名前のデータベース内のすべてのユーザーを返します。
または:
some_association.users.with_name_like("Samson")
「Samson」のような名前のみでその関連付けを解除したユーザー。
今は2012年です。narskの答えに対するこの更新を投入すると思いました。
named_scope
は単にscope
になったため、例は次のようになります。
class User
scope :name_starts_with, lambda {|str|
:conditions => ['lower(name) like ?', "#{str.downcase}%"]
}
end
注:OPが「次を含む」ではなく「starts_with」を要求しているため、narskのソリューションから最初の%
を削除しました。
今、あなたは次のようなことができます
User.name_starts_with("b").each do {|bs| puts bs.inspect}
bob = User.name_starts_with("bob").first
… etc
スコープ常にコレクションを返すであり、個別の結果ではないことに注意してください。私が最初にARを始めたとき、それは私を驚かせました。
同じことをする非常に簡潔な構文は次のようになります。
Model.where(field: ('prefix'...'prefiy'))
これはレンダリングします:
WHERE ( field >= 'prefix' AND field < 'prefiy')
これは'prefiy'がアルファベット順で'prefix'接頭辞に一致しない最初の文字列であるため、ジョブを実行します。
通常は使用しません(代わりにsqueelまたはransackを使用します)が、なんらかの理由でクエリのハッシュ構文に固執する必要がある場合は、1日を節約できます...
デイブ・サグ、あなたのコードの最初の部分は
class User
scope :name_starts_with, (lambda do |str|
{:conditions => ['lower(name) like ?', "#{str.downcase}%"]}
end )
end