This summarizes the things I learned about Sunspot searching while looking throught the source. I included links to specific parts of the source-code. Be sure to check that out if you are curious.
This Job
model will be what the examples below are searching.
class Job < ActiveRecord::Base
searchable do
boolean :requires_expertise
integer :state
time :deadline_at
end
end
Using the Object#search
method, we build a search and execute it one step.
searcher = Job.search do
with(:requires_expertise).equal_to(true)
with(:state).greater_than(2)
order_by(:deadline_at, :asc)
end
searcher.results #=> returns results
searcher.hits #=> returns hits
If we want to incrementally build up our search and delay execution until later, we can use the Sunspot::new_search
method instead. We create a new search instance, passing in the object class to be searched.
To build our query, the #build
method can be called with block of components.
We need to call #execute
on the search instance when we are ready to execute the search.
searcher = Sunspot.new_search(Job)
searcher.build do
with(:requires_expertise).equal_to(true)
end
searcher.results #=> throws error since search is unexecuted
searcher.execute
searcher.results #=> returns results
# the searcher can still be appended to after execution
searcher.build do
with(:state).greater_than(2)
end
# but will still return the old results until executed again
searcher.results #=> old results
searcher.execute
searcher.results #=> hawt new results with expertise AND state queries
Inside search blocks, Sunspot uses a DSL to build queries. The API is designed to keep this DSL isolated to those blocks, which made breaking up search logic difficult. Using #build
can help with that, but wouldn't it be great to get rid of blocks entirely to easily build a custom API layer for searches?
There are a few ways to do this...
The Sunspot search instance contains an instance of the DSL::Search
. This can be pulled out by sending private #dsl
method to the searcher. DSL methods can be called directly on this object.
searcher = Sunspot.new_search(Job)
context = searcher.send(:dsl)
context.with(:requires_expertise).equal_to(true)
context.with(:state).greater_than(2)
context.order_by(:deadline_at, :asc)
searcher.execute
searcher.results #=> returns results
If using of private methods feels a bit gross (and it should), we can create a new DSL::Search
instance instead. The only caveat is instantiating it requires a Sunspot::Setup
object as an argument.
searcher = Sunspot.new_search(Job)
setup = Sunspot::Setup.for(Job)
context = Sunspot::DSL::Search.new(searcher, setup)
context.with(:requires_expertise).equal_to(true)
context.with(:state).greater_than(2)
context.order_by(:deadline_at, :asc)
searcher.execute
searcher.results #=> returns results