Ich halte Searchlight für „erledigt“ . Es gibt keine Produktionsabhängigkeiten, daher gibt es keinen Grund, warum es nicht auf unbestimmte Zeit funktionieren sollte. Ich bin auch zu anderen Dingen übergegangen.
Wenn Sie einen Fehler finden, können Sie gerne ein Problem eröffnen, damit andere ihn finden und diskutieren können. Es ist jedoch unwahrscheinlich, dass ich persönlich antworte. Wenn Searchlight Ihre Anforderungen nicht mehr erfüllt, geben Sie auf! :) :)
Searchlight ist eine einfache Möglichkeit, Datenbanksuchen mithilfe eines ORM zu erstellen.
Searchlight kann mit jedem ORM oder Objekt arbeiten, das eine Abfrage mithilfe verketteter Methodenaufrufe erstellen kann (z. B. ActiveRecords .where(...).where(...).limit(...)
oder ähnliche Ketten mit Sequel, Mongoid , usw).
Um Ihnen den Einstieg zu erleichtern, stehen eine Demo-App und der Code für diese App zur Verfügung.
Der Hauptzweck von Searchlight ist die Unterstützung von Suchformularen in Webanwendungen.
Searchlight schreibt keine Anfragen für Sie. Was es tut, ist:
form_for
in Rails).WHERE first_name =
-Teil nicht aus). Wenn Sie beispielsweise eine Searchlight-Suchklasse namens YetiSearch
haben und diese wie folgt instanziieren:
search = YetiSearch . new (
# or params[:yeti_search]
"active" => true , "name" => "Jimmy" , "location_in" => %w[ NY LA ]
)
... der Aufruf von search.results
erstellt eine Suche, indem die Methoden search_active
, search_name
und search_location_in
auf Ihrem YetiSearch
aufgerufen werden, vorausgesetzt, Sie haben sie definiert. (Wenn Sie es erneut tun, aber "name"
weglassen, wird search_name
nicht aufgerufen.)
Die results
gibt dann den Rückgabewert der letzten Suchmethode zurück. Wenn Sie ActiveRecord verwenden, wäre dies ein ActiveRecord::Relation
, und Sie können dann each
aufrufen, um die Ergebnisse zu durchlaufen, to_sql
, um die generierte Abfrage abzurufen usw.
Eine Suchklasse besteht aus zwei Hauptteilen: einer base_query
und einigen search_
. Zum Beispiel:
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
Der Aufruf von PersonSearch.new("first_name" => "Gregor", "last_name" => "Mendel").results
würde Person.all.where(first_name: "Gregor").where(last_name: "Mendel")
ausführen und zurückkehren die resultierende ActiveRecord::Relation
. Wenn Sie die Option last_name
weggelassen oder "last_name" => ""
angegeben haben, wird das zweite „ where
“ nicht hinzugefügt.
Hier ist ein ausführlicheres Beispiel für eine Suchklasse. Da Searchlight keine Abfragen für Sie schreibt, können Sie alles tun, was Ihr ORM unterstützt . (Siehe spec/support/book_search.rb
für noch mehr Fantasie.)
# 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
Hier sind einige Beispielsuchen.
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
Für jede von Ihnen definierte Suchmethode definiert Searchlight eine entsprechende Option-Reader-Methode. Wenn Sie beispielsweise def search_first_name
hinzufügen, erhält Ihre Suchklasse eine .first_name
Methode, die options["first_name"]
oder, wenn dieser Schlüssel nicht existiert, options[:first_name]
zurückgibt. Dies ist vor allem beim Erstellen von Formularen nützlich.
Da die Schlüssel "first_name"
und :first_name
als austauschbar betrachtet werden, gibt Searchlight einen Fehler aus, wenn Sie beide angeben.
Searchlight bietet einige Methoden zum Untersuchen der für Ihre Suche bereitgestellten Optionen.
raw_options
enthält genau das, womit es instanziiert wurdeoptions
enthält alle raw_options
, die nicht empty?
. Wenn beispielsweise raw_options
categories: nil, tags: ["a", ""]
entspricht, sind die Optionen tags: ["a"]
.empty?(value)
gibt „true“ für nil
, reine Leerzeichenzeichenfolgen oder irgendetwas anderes zurück, das „true“ von value.empty?
(z. B. leere Arrays)checked?(value)
gibt einen booleschen Wert zurück, der meistens wie !!value
funktioniert, aber 0
, "0"
und "false"
als false
betrachtet Abschließend explain
Sie, wie Searchlight Ihre Optionen interpretiert hat. Beispielsweise könnte book_search.explain
Folgendes ausgeben:
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"]
Manchmal ist es nützlich, Standardsuchoptionen zu haben, z. B. „Bestellungen, die nicht ausgeführt wurden“ oder „Häuser im letzten Monat aufgelistet“.
Dies kann durch Überschreiben von options
erfolgen. Z.B:
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
Sie können eine Unterklasse einer vorhandenen Suchklasse erstellen und dieselben Optionen mit einer anderen Basisabfrage unterstützen. Dies kann beispielsweise für die Vererbung einzelner Tabellen nützlich sein.
class VillageSearch < CitySearch
def base_query
Village . all
end
end
Oder Sie können super
verwenden, um den base_query
Wert der Superklasse abzurufen und ihn zu ändern:
class SmallTownSearch < CitySearch
def base_query
super . where ( "`cities`.`population` < ?" , 1_000 )
end
end
Sie können einer Searchlight-Suche beliebige Optionen zur Verfügung stellen; Nur diejenigen mit einer passenden search_
Methode bestimmen, welche Methoden ausgeführt werden. Wenn Sie beispielsweise AccountSearch.new("super_user" => true)
ausführen möchten, um eingeschränkte Ergebnisse zu finden, stellen Sie einfach sicher, dass Sie beim Erstellen Ihrer Abfrage options["super_user"]
aktivieren.
Searchlight funktioniert gut mit Rails-Formularen – schließen Sie einfach den ActionView
Adapter wie folgt ein:
require "searchlight/adapters/action_view"
class MySearch < Searchlight :: Search
include Searchlight :: Adapters :: ActionView
# ...etc
end
Dadurch wird die Verwendung eines Searchlight::Search
mit form_for
ermöglicht:
# 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 }
Solange Ihr Formular Optionen übermittelt, die Ihre Suche versteht, können Sie es problemlos in Ihren Controller einbinden:
# 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
Überprüfen Sie für jede Version .travis.yml
, um zu sehen, welche Ruby-Versionen wir auf Kompatibilität testen.
Fügen Sie diese Zeile zur Gemfile Ihrer Anwendung hinzu:
gem 'searchlight'
Und dann ausführen:
$ bundle
Oder installieren Sie es selbst als:
$ gem install searchlight
rake
führt die Tests durch; rake mutant
führt Mutationstests mit Mutant durch.
git checkout -b my-new-feature
)git commit -am 'Add some feature'
)git push origin my-new-feature
)