Considero que el reflector está "listo" . No tiene dependencias de producción, por lo que no hay razón para que no funcione indefinidamente. También he pasado a otras cosas.
Si encuentra un error, no dude en abrir un problema para que otros puedan encontrarlo y discutirlo, pero es poco probable que responda personalmente. Si Searchlight ya no satisface sus necesidades, ¡descárguelo! :)
Searchlight es una forma poco mágica de crear búsquedas en bases de datos utilizando un ORM.
Searchlight puede funcionar con cualquier ORM u objeto que pueda crear una consulta utilizando llamadas a métodos encadenados (por ejemplo, .where(...).where(...).limit(...)
ActiveRecord, o cadenas similares con Sequel, Mongoid , etc).
Hay una aplicación de demostración y el código de esa aplicación disponibles para ayudarle a comenzar.
El uso principal de Searchlight es admitir formularios de búsqueda en aplicaciones web.
Searchlight no escribe consultas por usted. Lo que hace es:
form_for
en Rails)WHERE first_name =
) Por ejemplo, si tiene una clase de búsqueda de Searchlight llamada YetiSearch
y la crea una instancia de esta manera:
search = YetiSearch . new (
# or params[:yeti_search]
"active" => true , "name" => "Jimmy" , "location_in" => %w[ NY LA ]
)
... llamar search.results
generará una búsqueda llamando a los métodos search_active
, search_name
y search_location_in
en tu YetiSearch
, suponiendo que los hayas definido. (Si lo vuelve a hacer pero omite "name"
, no llamará search_name
).
El método results
devolverá el valor de retorno del último método de búsqueda. Si está utilizando ActiveRecord, esto sería ActiveRecord::Relation
y luego puede llamar each
para recorrer los resultados, to_sql
para obtener la consulta generada, etc.
Una clase de búsqueda tiene dos partes principales: una base_query
y algunos search_
. Por ejemplo:
class PersonSearch < Searchlight :: Search
# This is the starting point for any chaining we do, and it's what
# will be returned if no search options are passed.
# In this case, it's an ActiveRecord model.
def base_query
Person . all # or `.scoped` for ActiveRecord 3
end
# A search method.
def search_first_name
# If `"first_name"` was the first key in the options_hash,
# `query` here will be the base query, namely, `Person.all`.
query . where ( first_name : options [ :first_name ] )
end
# Another search method.
def search_last_name
# If `"last_name"` was the second key in the options_hash,
# `query` here will be whatever `search_first_name` returned.
query . where ( last_name : last_name )
end
end
Llamar PersonSearch.new("first_name" => "Gregor", "last_name" => "Mendel").results
ejecutaría Person.all.where(first_name: "Gregor").where(last_name: "Mendel")
y devolvería el ActiveRecord::Relation
resultante. Si omitió la opción last_name
o proporcionó "last_name" => ""
, el segundo where
no se agregará.
Aquí hay un ejemplo más completo de clase de búsqueda. Tenga en cuenta que , dado que Searchlight no escribe consultas por usted, puede hacer cualquier cosa que admita su ORM . (Consulte spec/support/book_search.rb
para obtener aún más detalles).
# app/searches/city_search.rb
class CitySearch < Searchlight :: Search
# `City` here is an ActiveRecord model
def base_query
City . includes ( :country )
end
# Reach into other tables
def search_continent
query . where ( '`countries`.`continent` = ?' , continent )
end
# Other kinds of queries
def search_country_name_like
query . where ( "`countries`.`name` LIKE ?" , "% #{ country_name_like } %" )
end
# .checked? considers "false", 0 and "0" to be false
def search_is_megacity
query . where ( "`cities`.`population` #{ checked? ( is_megacity ) ? '>=' : '<' } ?" , 10_000_000 )
end
end
A continuación se muestran algunos ejemplos de búsquedas.
CitySearch . new . results . to_sql
# => "SELECT `cities`.* FROM `cities` "
CitySearch . new ( "name" => "Nairobi" ) . results . to_sql
# => "SELECT `cities`.* FROM `cities` WHERE `cities`.`name` = 'Nairobi'"
CitySearch . new ( "country_name_like" => "aust" , "continent" => "Europe" ) . results . count # => 6
non_megas = CitySearch . new ( "is_megacity" => "false" )
non_megas . results . to_sql
# => "SELECT `cities`.* FROM `cities` WHERE (`cities`.`population` < 10000000"
non_megas . results . each do | city |
# ...
end
Para cada método de búsqueda que defina, Searchlight definirá un método de lectura de opciones correspondiente. Por ejemplo, si agrega def search_first_name
, su clase de búsqueda obtendrá un método .first_name
que devuelve options["first_name"]
o, si esa clave no existe, options[:first_name]
. Esto es útil principalmente al crear formularios.
Dado que considera que las claves "first_name"
y :first_name
son intercambiables, Searchlight generará un error si proporciona ambas.
Searchlight proporciona algunos métodos para examinar las opciones proporcionadas para su búsqueda.
raw_options
contiene exactamente con qué se creó la instanciaoptions
contiene todas raw_options
que no estaban empty?
. Por ejemplo, si raw_options
son categories: nil, tags: ["a", ""]
, las opciones serán tags: ["a"]
.empty?(value)
devuelve verdadero para nil
, cadenas de solo espacios en blanco o cualquier otra cosa que devuelva verdadero de value.empty?
(por ejemplo, matrices vacías)checked?(value)
devuelve un valor booleano, que funciona principalmente como !!value
pero considera 0
, "0"
y "false"
como false
Finalmente, explain
le dirá cómo Searchlight interpretó sus opciones. Por ejemplo, book_search.explain
podría generar:
Initialized with `raw_options`: ["title_like", "author_name_like", "category_in",
"tags", "book_thickness", "parts_about_lolcats"]
Of those, the non-blank ones are available as `options`: ["title_like",
"author_name_like", "tags", "book_thickness", "in_print"]
Of those, the following have corresponding `search_` methods: ["title_like",
"author_name_like", "in_print"]. These would be used to build the query.
Blank options are: ["category_in", "parts_about_lolcats"]
Non-blank options with no corresponding `search_` method are: ["tags",
"book_thickness"]
A veces es útil tener opciones de búsqueda predeterminadas, por ejemplo, "pedidos que no se han cumplido" o "casas listadas en el último mes".
Esto se puede hacer anulando options
. P.ej:
class BookSearch < SearchlightSearch
# def base_query...
def options
super . tap { | opts |
opts [ "in_print" ] ||= "either"
}
end
def search_in_print
return query if options [ "in_print" ] . to_s == "either"
query . where ( in_print : checked? ( options [ "in_print" ] ) )
end
end
Puede subclasificar una clase de búsqueda existente y admitir las mismas opciones con una consulta base diferente. Esto puede resultar útil, por ejemplo, para la herencia de una sola tabla.
class VillageSearch < CitySearch
def base_query
Village . all
end
end
O puedes usar super
para obtener el valor base_query
de la superclase y modificarlo:
class SmallTownSearch < CitySearch
def base_query
super . where ( "`cities`.`population` < ?" , 1_000 )
end
end
Puede proporcionar una búsqueda con Searchlight con las opciones que desee; sólo aquellos con un método search_
coincidente determinarán qué métodos se ejecutan. Por ejemplo, si desea hacer AccountSearch.new("super_user" => true)
para encontrar resultados restringidos, simplemente asegúrese de marcar options["super_user"]
al crear su consulta.
Searchlight funciona muy bien con los formularios Rails; simplemente incluya el adaptador ActionView
de la siguiente manera:
require "searchlight/adapters/action_view"
class MySearch < Searchlight :: Search
include Searchlight :: Adapters :: ActionView
# ...etc
end
Esto permitirá usar Searchlight::Search
con form_for
:
# app/views/cities/index.html.haml
...
= form_for ( @search , url : search_cities_path ) do | f |
% fieldset
= f . label :name , "Name"
= f . text_field :name
% fieldset
= f . label :country_name_like , "Country Name Like"
= f . text_field :country_name_like
% fieldset
= f . label :is_megacity , "Megacity?"
= f . select :is_megacity , [ [ 'Yes' , true ] , [ 'No' , false ] , [ 'Either' , '' ] ]
% fieldset
= f . label :continent , "Continent"
= f . select :continent , [ 'Africa' , 'Asia' , 'Europe' ] , include_blank : true
= f . submit "Search"
- @results . each do | city |
= render partial : 'city' , locals : { city : city }
Siempre que su formulario envíe opciones que su búsqueda comprenda, puede conectarlo fácilmente en su controlador:
# app/controllers/orders_controller.rb
class OrdersController < ApplicationController
def index
@search = OrderSearch . new ( search_params ) # For use in a form
@results = @search . results # For display along with form
end
protected
def search_params
# Ensure the user can only browse or search their own orders
( params [ :order_search ] || { } ) . merge ( user_id : current_user . id )
end
end
Para cualquier versión determinada, consulte .travis.yml
para ver qué versiones de Ruby estamos probando para comprobar su compatibilidad.
Agregue esta línea al Gemfile de su aplicación:
gem 'searchlight'
Y luego ejecuta:
$ bundle
O instálelo usted mismo como:
$ gem install searchlight
rake
ejecuta las pruebas; rake mutant
ejecuta pruebas de mutación utilizando mutant.
git checkout -b my-new-feature
)git commit -am 'Add some feature'
)git push origin my-new-feature
)