Question vraiment simple - comment puis-je effectuer une recherche pour trouver tous les enregistrements où le nom commence par une certaine chaîne à Activerecord. J'ai vu toutes sortes de bits sur Internet où les clauses de SQL comme SQL sont utilisées - mais d'après ce que j'ai entendu ce que ce n'est pas le moyen "correct" de le faire.
Y a-t-il un "bon" Rails Voie?
Je recommande vivement le plugin SearchLogic .
Ensuite, c'est aussi simple que:
@search = Model.new_search(params[:search])
@search.condition.field_starts_with = "prefix"
@models = @search.all
SearchLogic est assez intelligent, comme Activerecord, de prendre le nom du champ dans la condition starts_with
. Il gérera également toute la pagination.
Cette méthode aidera à prévenir l'injection SQL et sera également agnostique de la base de données. SearchLogic finit par gérer la recherche différemment en fonction de l'adaptateur de base de données que vous utilisez. Vous n'avez pas non plus à écrire de SQL!
SearchLogic a une bonne documentation et est facile à utiliser (je suis nouveau à Ruby et Rails moi-même). Lorsque je suis resté bloqué, l'auteur du plugin a même répondu à un email direct dans quelques heures d'aide à résoudre mon problème. Je ne peux pas recommander assez de SearchLogic ... comme vous pouvez le dire.
Si vous souhaitez effectuer la recherche dans la base de données, vous devrez utiliser SQL.
Et, bien sûr, vous devrez effectuer la recherche dans la base de données, sinon vous devez charger tous les objets dans Ruby (ce qui n'est pas une bonne chose ).
Donc, vous aurez besoin de quelque chose comme
MyModel.find(:all, :conditions => ["field LIKE ?", "#{prefix}%"])
où prefix
est une variable avec la chaîne que vous recherchez.
IN Rails 3.0+ Cela devient:
MyModel.where("field LIKE :prefix", prefix: "#{prefix}%")
Disclaimer: Je suis nouveau à Ruby et rails, et j'essaie toujours d'apprendre le Ruby moyen de faire des choses, mais j'ai codé plus de la moitié de ma vie et professionnellement pendant une décennie. DRY est un concept avec lequel je suis très familier. Voici comment je l'ai mis en œuvre. Il est très similaire à la réponse de Narsk, que je pense bien aussi.
# 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
Et puis dans votre modèle:
class User < ActiveRecord::Base
extend NameSearchable
...
end
Et puis quand vous voulez l'utiliser:
User.search_by_prefix('John') #or
User.search_by_prefix("#{name_str}")
Une chose à appeler:
Les bases de données relationnelles traditionnelles ne sont pas extrêmement bonnes pour satisfaire ces types de questions. Si vous recherchez une implémentation très réactive qui ne tuera pas vos bases de données sous charge, vous devez probablement utiliser une solution adaptée à cet effet. Les exemples populaires incluent Solr ou Sphinx, et il y en a beaucoup d'autres. Avec cette implémentation, puisque vous séchez, vous pouvez remplacer la mise en œuvre avec un indexeur séparé dans un seul endroit et vous seriez prêt à rock.
Je ne veux pas écrire une portée à chaque fois que je souhaite voir si une colonne spécifique démarre_with un préfixe.
# 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
Appelez-le comme ceci:
User.starts_with('name', 'ab').limit(1)
Maintenant, c'est 2012, je pensais que je voudrais jeter cette mise à jour à la réponse de Narsk.
named_scope
est devenu simplement scope
il y a un moment, alors l'exemple devient
class User
scope :name_starts_with, lambda {|str|
:conditions => ['lower(name) like ?', "#{str.downcase}%"]
}
end
NOTE J'ai supprimé le premier %
de la solution de Narsk au fur et à mesure que l'OP demande un "Démarre_with" n'est pas un match "contient".
Maintenant tu peux faire des choses comme
User.name_starts_with("b").each do {|bs| puts bs.inspect}
bob = User.name_starts_with("bob").first
… etc
Notez que les scopes toujours renvoient des collections, jamais les résultats individuels. Cela m'a jeté quand je commençais d'abord avec AR et je réussis toujours à oublier que de temps en temps.
La syntaxe très terrese pour la même chose pourrait être:
Model.where(field: ('prefix'...'prefiy'))
Cela rend:
WHERE ( field >= 'prefix' AND field < 'prefiy')
Cela fait le travail comme "préfiy" est la première chaîne alphabétiquement ne correspondant pas au préfixe "préfixe" Préfixe.
Je ne l'utiliserais pas normalement (j'irais à la place de Squeel ou de Ransack), mais cela peut sauver votre journée si pour une raison quelconque, vous devez vous tenir à la syntaxe de hachage pour les requêtes ...
Dave Sag, je suppose que la première partie de votre code devrait être
class User
scope :name_starts_with, (lambda do |str|
{:conditions => ['lower(name) like ?', "#{str.downcase}%"]}
end )
end