Je considère le projecteur comme "terminé" . Il n'a aucune dépendance de production, il n'y a donc aucune raison pour qu'il ne fonctionne pas indéfiniment. Je suis également passé à autre chose.
Si vous trouvez un bug, n'hésitez pas à ouvrir un problème afin que d'autres puissent le trouver et en discuter, mais il est peu probable que je réponde personnellement. Si Searchlight ne répond plus à vos besoins, déboursez ! :)
Searchlight est un moyen peu magique de créer des recherches dans des bases de données à l'aide d'un ORM.
Searchlight peut fonctionner avec n'importe quel ORM ou objet capable de créer une requête à l'aide d'appels de méthodes chaînés (par exemple, .where(...).where(...).limit(...)
d'ActiveRecord, ou des chaînes similaires avec Sequel, Mongoid , etc).
Une application de démonstration et le code de cette application sont disponibles pour vous aider à démarrer.
L'utilisation principale de Searchlight est de prendre en charge les formulaires de recherche dans les applications Web.
Searchlight n'écrit pas de requêtes à votre place. Ce qu'il fait, c'est :
form_for
dans Rails)WHERE first_name =
) Par exemple, si vous disposez d’une classe de recherche Searchlight appelée YetiSearch
et que vous l’instanciez comme ceci :
search = YetiSearch . new (
# or params[:yeti_search]
"active" => true , "name" => "Jimmy" , "location_in" => %w[ NY LA ]
)
... appeler search.results
créera une recherche en appelant les méthodes search_active
, search_name
et search_location_in
sur votre YetiSearch
, en supposant que vous les ayez définies. (Si vous recommencez mais omettez "name"
, il n'appellera pas search_name
.)
La méthode results
renverra alors la valeur de retour de la dernière méthode de recherche. Si vous utilisez ActiveRecord, ce serait un ActiveRecord::Relation
, et vous pouvez ensuite appeler each
pour parcourir les résultats, to_sql
pour obtenir la requête générée, etc.
Une classe de recherche comporte deux parties principales : une base_query
et des méthodes search_
. Par exemple:
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
L'appel de PersonSearch.new("first_name" => "Gregor", "last_name" => "Mendel").results
exécuterait Person.all.where(first_name: "Gregor").where(last_name: "Mendel")
et retournerait le ActiveRecord::Relation
résultant. Si vous avez omis l'option last_name
ou fourni "last_name" => ""
, la seconde where
ne serait pas ajoutée.
Voici un exemple de classe de recherche plus complet. Notez que , comme Searchlight n'écrit pas de requêtes pour vous, vous êtes libre de faire tout ce que votre ORM prend en charge . (Voir spec/support/book_search.rb
pour encore plus de fantaisie.)
# 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
Voici quelques exemples de recherches.
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
Pour chaque méthode de recherche que vous définissez, Searchlight définira une méthode de lecture d'options correspondante. Par exemple, si vous ajoutez def search_first_name
, votre classe de recherche obtiendra une méthode .first_name
qui renvoie options["first_name"]
ou, si cette clé n'existe pas, options[:first_name]
. Ceci est utile principalement lors de la création de formulaires.
Puisqu'il considère les clés "first_name"
et :first_name
comme étant interchangeables, Searchlight générera une erreur si vous fournissez les deux.
Searchlight propose certaines méthodes pour examiner les options proposées pour votre recherche.
raw_options
contient exactement ce avec quoi il a été instanciéoptions
contient toutes raw_options
qui n'étaient pas empty?
. Par exemple, si raw_options
est categories: nil, tags: ["a", ""]
, les options seront tags: ["a"]
.empty?(value)
renvoie true pour nil
, les chaînes contenant uniquement des espaces ou tout autre élément qui renvoie true à partir de value.empty?
(par exemple, des tableaux vides)checked?(value)
renvoie un booléen, qui fonctionne principalement comme !!value
mais considère 0
, "0"
et "false"
comme étant false
Enfin, explain
vous dira comment Searchlight a interprété vos options. Par exemple, book_search.explain
pourrait afficher :
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"]
Il est parfois utile d'avoir des options de recherche par défaut, par exemple « commandes qui n'ont pas été exécutées » ou « maisons répertoriées le mois dernier ».
Cela peut être fait en remplaçant options
. Par exemple :
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
Vous pouvez sous-classer une classe de recherche existante et prendre en charge toutes les mêmes options avec une requête de base différente. Cela peut être utile pour l'héritage d'une seule table, par exemple.
class VillageSearch < CitySearch
def base_query
Village . all
end
end
Ou vous pouvez utiliser super
pour obtenir la valeur base_query
de la superclasse et la modifier :
class SmallTownSearch < CitySearch
def base_query
super . where ( "`cities`.`population` < ?" , 1_000 )
end
end
Vous pouvez proposer une recherche Searchlight avec toutes les options de votre choix ; seuls ceux avec une méthode search_
correspondante détermineront quelles méthodes sont exécutées. Par exemple, si vous souhaitez effectuer AccountSearch.new("super_user" => true)
pour trouver des résultats restreints, assurez-vous simplement de cocher options["super_user"]
lors de la création de votre requête.
Searchlight fonctionne bien avec les formulaires Rails - incluez simplement l'adaptateur ActionView
comme suit :
require "searchlight/adapters/action_view"
class MySearch < Searchlight :: Search
include Searchlight :: Adapters :: ActionView
# ...etc
end
Cela permettra d'utiliser un Searchlight::Search
avec 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 }
Tant que votre formulaire soumet des options que votre recherche comprend, vous pouvez facilement le connecter à votre contrôleur :
# 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
Pour une version donnée, consultez .travis.yml
pour voir quelles versions de Ruby nous testons la compatibilité.
Ajoutez cette ligne au Gemfile de votre application :
gem 'searchlight'
Et puis exécutez :
$ bundle
Ou installez-le vous-même en tant que :
$ gem install searchlight
rake
exécute les tests ; rake mutant
exécute des tests de mutation en utilisant le mutant.
git checkout -b my-new-feature
)git commit -am 'Add some feature'
)git push origin my-new-feature
)