我认为探照灯“完成”了。它没有生产依赖性,因此没有理由它不应该无限期地工作。我也转向了其他事情。
如果您发现错误,请随时提出问题,以便其他人可以找到它并进行讨论,但我不太可能亲自回复。如果 Searchlight 不再满足您的需求,请放弃! :)
Searchlight 是一种使用 ORM 构建数据库搜索的低魔法方法。
Searchlight 可以使用任何可以使用链式方法调用构建查询的 ORM 或对象(例如,ActiveRecord 的.where(...).where(...).limit(...)
或 Sequel、Mongoid 的类似链, ETC)。
演示应用程序和该应用程序的代码可帮助您入门。
Searchlight 的主要用途是支持 Web 应用程序中的搜索表单。
Searchlight 不会为您编写查询。它的作用是:
form_for
)WHERE first_name =
部分)例如,如果您有一个名为YetiSearch
的 Searchlight 搜索类,并且您可以像这样实例化它:
search = YetiSearch . new (
# or params[:yeti_search]
"active" => true , "name" => "Jimmy" , "location_in" => %w[ NY LA ]
)
...调用search.results
将通过调用YetiSearch
上的方法search_active
、 search_name
和search_location_in
来构建搜索,假设您已经定义了它们。 (如果您再次执行此操作但省略"name"
,则它不会调用search_name
。)
然后, results
方法将返回最后一个搜索方法的返回值。如果您使用的是 ActiveRecord,这将是一个ActiveRecord::Relation
,然后您可以调用each
来循环结果, to_sql
来获取生成的查询等。
搜索类有两个主要部分: base_query
和一些search_
方法。例如:
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
调用PersonSearch.new("first_name" => "Gregor", "last_name" => "Mendel").results
将运行Person.all.where(first_name: "Gregor").where(last_name: "Mendel")
并返回生成的ActiveRecord::Relation
。如果您省略了last_name
选项,或提供了"last_name" => ""
,则不会添加第二个where
。
这是一个更完整的示例搜索类。请注意,由于 Searchlight 不会为您编写查询,因此您可以自由地执行 ORM 支持的任何操作。 (请参阅spec/support/book_search.rb
了解更多奇特之处。)
# 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
以下是一些搜索示例。
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
对于您定义的每个搜索方法,Searchlight 将定义相应的选项读取器方法。例如,如果您添加def search_first_name
,您的搜索类将获得一个.first_name
方法,该方法返回options["first_name"]
,或者,如果该键不存在, options[:first_name]
。这主要在构建表单时有用。
由于它认为键"first_name"
和:first_name
是可以互换的,因此如果您同时提供这两个键,Searchlight 将引发错误。
Searchlight 提供了一些方法来检查为您的搜索提供的选项。
raw_options
包含了它被实例化的确切内容options
包含所有非空的raw_options
empty?
。例如,如果raw_options
是categories: nil, tags: ["a", ""]
,则选项将是tags: ["a"]
。empty?(value)
对于nil
、纯空白字符串或从value.empty?
(例如,空数组)checked?(value)
返回一个布尔值,其工作原理与!!value
类似,但将0
、 "0"
和"false"
视为false
最后, explain
将告诉您 Searchlight 如何解释您的选项。例如, book_search.explain
可能输出:
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"]
有时,使用默认搜索选项很有用 - 例如,“尚未履行的订单”或“上个月列出的房屋”。
这可以通过覆盖options
来完成。例如:
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
您可以对现有搜索类进行子类化,并使用不同的基本查询支持所有相同的选项。例如,这对于单表继承可能很有用。
class VillageSearch < CitySearch
def base_query
Village . all
end
end
或者你可以使用super
来获取超类的base_query
值并修改它:
class SmallTownSearch < CitySearch
def base_query
super . where ( "`cities`.`population` < ?" , 1_000 )
end
end
您可以提供探照灯搜索任何您喜欢的选项;只有那些具有匹配的search_
方法的人才能确定运行哪些方法。例如,如果您想要执行AccountSearch.new("super_user" => true)
来查找受限结果,只需确保在构建查询时检查options["super_user"]
即可。
Searchlight 与 Rails 表单配合得很好 - 只需包含ActionView
适配器,如下所示:
require "searchlight/adapters/action_view"
class MySearch < Searchlight :: Search
include Searchlight :: Adapters :: ActionView
# ...etc
end
这将允许使用Searchlight::Search
和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 }
只要您的表单提交了您的搜索可以理解的选项,您就可以轻松地将其连接到控制器中:
# 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
对于任何给定版本,请检查.travis.yml
以了解我们正在测试兼容性的 Ruby 版本。
将此行添加到应用程序的 Gemfile 中:
gem 'searchlight'
然后执行:
$ bundle
或者自己安装:
$ gem install searchlight
rake
运行测试; rake mutant
使用突变体运行突变测试。
git checkout -b my-new-feature
)git commit -am 'Add some feature'
)git push origin my-new-feature
)