Skip to content
A rich Ruby API and DSL for the ElasticSearch search engine/database
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.




Slingshot is a Ruby client for the ElasticSearch search engine/database. It aims to provide rich and comfortable Ruby API in the form of a simple domain-specific language.

ElasticSearch is a scalable, distributed, cloud-ready, highly-available, RESTful database communicating by JSON over HTTP, based on Lucene, written in Java. It manages to be very simple to use and very powerful at the same time.


First, you need a running ElasticSearch server. Thankfully, it's easy. Let's define easy:

$ curl -k -L -o elasticsearch-0.15.2.tar.gz
$ tar -zxvf elasticsearch-0.15.2.tar.gz
$ ./elasticsearch-0.15.2/bin/elasticsearch -f

OK, easy. Now, install the gem via Rubygems:

$ gem install slingshot-rb

or from source:

$ git clone git://
$ cd slingshot
$ rake install


Currently, you can use Slingshot via the DSL (eg. by extending your class with it). Plans for full ActiveModel integration (and other convenience layers) are in progress (see the activemodel branch).

To kick the tires, require the gem in an IRB session or a Ruby script (note that you can just run the full example from examples/dsl.rb):

require 'rubygems'
require 'slingshot'

First, let's create an index named articles and store/index some documents:

Slingshot.index 'articles' do

  store :title => 'One',   :tags => ['ruby']
  store :title => 'Two',   :tags => ['ruby', 'python']
  store :title => 'Three', :tags => ['java']
  store :title => 'Four',  :tags => ['ruby', 'php']


We can also create the index with specific mapping:

Slingshot.index 'articles' do
  create :mappings => {
    :article => {
      :properties => {
        :id       => { :type => 'string', :index => 'not_analyzed', :include_in_all => false },
        :title    => { :type => 'string', :boost => 2.0,            :analyzer => 'snowball'  },
        :tags     => { :type => 'string', :analyzer => 'keyword'                             },
        :content  => { :type => 'string', :analyzer => 'snowball'                            }

Now, let's query the database.

We are searching for articles whose title begins with letter “T”, sorted by title in descending order, filtering them for ones tagged “ruby”, and also retrieving some facets from the database:

s = 'articles' do
  query do
    string 'title:T*'

  filter :terms, :tags => ['ruby']

  sort { title 'desc' }

  facet 'global-tags' do
    terms :tags, :global => true

  facet 'current-tags' do
    terms :tags

Let's display the results:

s.results.each do |document|
  puts "* #{ document.title } [tags: #{document.tags.join(', ')}]"

# * Two [tags: ruby, python]

Let's display the global facets (distribution of tags across the whole database):

s.results.facets['global-tags']['terms'].each do |f|
  puts "#{f['term'].ljust(10)} #{f['count']}"

# ruby       3
# python     1
# php        1
# java       1

Now, let's display the facets based on current query (notice that count for articles tagged with 'java' is included, even though it's not returned by our query; count for articles tagged 'php' is excluded, since they don't match the current query):

s.results.facets['current-tags']['terms'].each do |f|
  puts "#{f['term'].ljust(10)} #{f['count']}"

# ruby       1
# python     1
# java       1

We can display the full query JSON:

puts s.to_json
# {"facets":{"current-tags":{"terms":{"field":"tags"}},"global-tags":{"global":true,"terms":{"field":"tags"}}},"query":{"query_string":{"query":"title:T*"}},"filter":{"terms":{"tags":["ruby"]}},"sort":[{"title":"desc"}]}

Or, better, we can display the corresponding curl command for easy debugging:

puts s.to_curl
# curl -X POST "http://localhost:9200/articles/_search?pretty=true" -d '{"facets":{"current-tags":{"terms":{"field":"tags"}},"global-tags":{"global":true,"terms":{"field":"tags"}}},"query":{"query_string":{"query":"title:T*"}},"filter":{"terms":{"tags":["ruby"]}},"sort":[{"title":"desc"}]}'

Since curl is the crucial debugging tool in ElasticSearch land, we can log every search query in curl format:

Slingshot.configure { logger 'elasticsearch.log' }


Currently, Slingshot supports main features of the ElasticSearch Search API and it's Query DSL. In present, it allows you to:

  • Create, delete and refresh the index
  • Create the index with specific mapping
  • Store a document in the index
  • Query the index with the query_string, term, terms and match_all types of queries
  • Sort the results by fields
  • Filter the results
  • Retrieve the terms and date histogram types of facets (other types are high priority)
  • Highlight matching fields
  • Return just specific fields from documents
  • Page the results with from and size query options
  • Log the curl-equivalent of requests and response JSON

See the examples/slingshot-dsl.rb file for the full, working examples.

Slingshot wraps the results in a enumerable Results::Collection class, and every result in a Results::Item class, which looks like a child of Hash and Openstruct, for smooth iterating and displaying the results.

You may wrap the result items in your own class just by setting the Configuration.wrapper property, supposed your class takes a hash of attributes upon initialization, in ActiveModel/ActiveRecord manner. Please see the files test/models/article.rb and test/unit/results_collection_test.rb for details.

Todo, Plans & Ideas

Slingshot is already used in production by its authors. Nevertheless, it's not finished yet.

The todos and plans are vast, and the most important are listed below, in the order of importance:

  • Seamless ActiveModel compatibility for easy usage in Rails applications (this also means nearly full ActiveRecord compatibility). See the ongoing work in the activemodel branch
  • Seamless will_paginate compatibility for easy pagination. Already implemented on the activemodel branch
  • Mapping definition for models
  • Proper RDoc annotations for the source code
  • Dual interface: allow to simply pass queries/options for ElasticSearch as a Hash in any method
  • Histogram facets
  • Statistical facets
  • Geo Distance facets
  • Index aliases management
  • Analyze API support
  • Bulk API
  • Embedded webserver to display statistics and to allow easy searches
  • Seamless support for auto-updating river index for CouchDB _changes feed

The full ActiveModel integration is planned for the 1.0 release.

Other Clients

Check out other ElasticSearch clients.


You can send feedback via e-mail or via Github Issues.

Karel Minarik

Something went wrong with that request. Please try again.