web-dev-qa-db-fra.com

Activerecord Trouver commence par

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?

41
robintw

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.

10
Owen

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}%"])

prefix est une variable avec la chaîne que vous recherchez.

IN Rails 3.0+ Cela devient:

MyModel.where("field LIKE :prefix", prefix: "#{prefix}%")
98
Gareth

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.

8
Larry

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)
7
Abdo

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.

2
Dave Sag

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 ...

1
bert bruynooghe

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
1
Oriettaxx