Skip to content

Instantly share code, notes, and snippets.

@tonyta
Last active August 29, 2015 14:05
Show Gist options
  • Save tonyta/c2272ecbe352849c3bf1 to your computer and use it in GitHub Desktop.
Save tonyta/c2272ecbe352849c3bf1 to your computer and use it in GitHub Desktop.
Sunspot Sandbox

Sunspot Sandbox

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.

Setting up an Example Object

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

Simple Searching

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

Building a Search

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

Blockless Searches

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...

Grabbing the private DSL instance

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

Using only public APIs

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment