Domanda

Domanda davvero semplice: come faccio una ricerca per trovare tutti i record in cui il nome inizia con una determinata stringa in ActiveRecord. Ho visto tutti i tipi di bit su Internet in cui sono state usate clausole LIKE SQL alla lettera, ma da quello che ho sentito non è il modo "corretto" di farlo.

Esiste un modo "corretto" di Rails?

È stato utile?

Soluzione

Consiglio vivamente il plug-in Searchlogic .

Quindi è facile come:

@search = Model.new_search(params[:search])
@search.condition.field_starts_with = "prefix"
@models = @search.all

Searchlogic è abbastanza intelligente, come ActiveRecord, per acquisire il nome del campo nella condizione start_with . Gestirà anche tutta l'impaginazione.

Questo metodo aiuterà a prevenire l'iniezione di SQL e sarà anche indipendente dal database. Searchlogic finisce per gestire la ricerca in modo diverso a seconda dell'adattatore del database che stai utilizzando. Inoltre non devi scrivere alcun SQL!

Searchlogic ha un'ottima documentazione ed è facile da usare (sono una novità di Ruby e Rails). Quando mi sono bloccato, l'autore del plugin ha anche risposto a una e-mail diretta entro poche ore aiutandomi a risolvere il mio problema. Non posso raccomandare abbastanza Searchlogic ... come puoi dire.

Altri suggerimenti

Se stai cercando di effettuare la ricerca nel database, dovrai usare SQL.

E, naturalmente, dovrai fare la ricerca nel database, altrimenti dovrai caricare tutti gli oggetti in Ruby (che non è una buona cosa).

Quindi, avrai bisogno di qualcosa come

MyModel.find(:all, :conditions => ["field LIKE ?", "#{prefix}%"])

dove prefisso è una variabile con la stringa che stai cercando.

In Rails 3.0+ questo diventa:

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

Disclaimer: sono nuovo di Ruby e Rails, e sto ancora cercando di imparare il Ruby Way per fare le cose, ma ho programmato per più della metà della mia vita e professionalmente per un decennio. DRY è un concetto con cui ho molta familiarità. Ecco come l'ho implementato. È molto simile alla risposta di Narsk, che penso sia anche buona.

# 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

E poi nel tuo modello:

class User < ActiveRecord::Base
  extend NameSearchable
  ...
end

E poi quando vuoi usarlo:

User.search_by_prefix('John')   #or
User.search_by_prefix("#{name_str}")

Una cosa da chiamare:

I database relazionali tradizionali non sono estremamente bravi a soddisfare questo tipo di query. Se stai cercando un'implementazione altamente reattiva che non uccida i tuoi database sotto carico, probabilmente dovresti usare una soluzione che è adattata allo scopo. Esempi popolari includono Solr o Sfinge, e ce ne sono anche molti altri. Con questa implementazione, dal momento che DRY, è possibile sostituire l'implementazione con un indicizzatore separato in un solo posto e si sarebbe pronti per il rock.

Non voglio scrivere un ambito ogni volta che voglio vedere se una colonna specifica inizia_ con un prefisso.

# 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

Chiamalo così:

User.starts_with('name', 'ab').limit(1)

Molto simile alla risposta sopra, ma se si utilizza ActiveRecord ... 2.1 o versioni successive, è possibile utilizzare un ambito denominato che consenta di utilizzare questo "finder" " sui record recuperati tramite le associazioni:

class User
  named_scope :with_name_like, lambda {|str|
    :conditions => ['lower(name) like ?', %(%#{str.downcase}%)]
  }
end

Usato come:

User.with_name_like("Samson")

(restituisce tutti gli utenti nel database con un nome come " Samson " ;.

o

some_association.users.with_name_like("Samson")

Utenti fuori da tale associazione solo con un nome come " Samson " ;.

Ora è il 2012, ho pensato di aggiungere questo aggiornamento alla risposta di Narsk.

named_scope è diventato semplicemente scope poco tempo fa, quindi l'esempio diventa

class User
  scope :name_starts_with, lambda {|str|
    :conditions => ['lower(name) like ?', "#{str.downcase}%"]
  }
end

Nota: ho rimosso il primo % dalla soluzione di narsk poiché l'OP chiede una corrispondenza 'start_with' non una 'contiene'.

Ora puoi fare cose come

User.name_starts_with("b").each do {|bs| puts bs.inspect}
bob = User.name_starts_with("bob").first

… etc

Nota che gli ambiti restituiscono sempre raccolte , mai singoli risultati. Questo mi ha colpito quando ho iniziato con AR e riesco ancora a dimenticarmene ogni tanto.

Dave Sag, immagino che la prima parte del tuo codice dovrebbe essere

class User
  scope :name_starts_with, (lambda do |str|
                              {:conditions => ['lower(name) like ?', "#{str.downcase}%"]}
                            end )
end

La sintassi molto concisa per la stessa cosa potrebbe essere:

Model.where(field: ('prefix'...'prefiy'))

Questo rende:

WHERE ( field >= 'prefix' AND field < 'prefiy')

Questo fa il lavoro come 'prefiy' è la prima stringa in ordine alfabetico che non corrisponde al 'prefisso' prefisso.

Non lo userei normalmente (preferirei usare il tergipavimento o il saccheggio), ma può salvare la giornata se per qualche motivo dovessi attenersi alla sintassi hash per le query ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top