Elastic Rails

Build Status

Elasticsearch + Ruby on Rails made easy.


  • Easy setup
  • Chainable query DSL
  • Easy to use results
  • Seamless rails integration
  • Multiple enviroment support
  • Zero downtime index migrations


Add this line to your application's Gemfile:

gem 'elastic-rails'

And then execute:

$ bundle

Or install it yourself as:

$ gem install elastic-rails

Finally execute the initialization script to generate configuration files:

$ rails g es:init

Usage overview

[For detailed usage reference check out the GUIDE]

Suppose that you already have a model called Bike, start by creating an index for it:

rails g es:index Bike

This will generate a new index definition file in app/indices/bike_index.rb

Add some fields to the index:

class BikeIndex < ElasticType
  # field types are extracted from the target model
  fields :brand_id, :model, :year, :price

  # you can also explicitly set the field type
  field :category, type: :term
  field :description, type: :string
  field :created_at, type: :time

  # you can also have nested documents, the following will require a nested PartIndex to be defined.
  nested :parts

  # you can override fields or create new ones
  def year

Every time you create or change and index you will neeed to synchronize the Elasticsearch index mappings:

 rake es:remap

If you already have some data that needs to be indexed then run the reindex task:

 rake es:reindex

To add additional data call the index import or the model's index_now or index_later methods:

some_bike.index_now # this will reindex only one record
some_bike.index_later # this will queue a reindexing job on the record
BikeIndex.import([bike_1, bike_2, bike_3]) # this will perform a bulk insertion

You can also setup automatic indexation/unindexation for a given model:

class Bike < ActiveRecord::Base
  index on: :save

After some data has been added you can start quering:

# List bikes of brand Trek or Cannondale, preferably 2015 or later models:
  .must(brand: ['Trek', 'Cannondale'])
  .should(year: { gte: 2015 })

# List bikes of brand Trek, preferably 2015 or 2016, give higher score to 2016 models:
  .must(brand: ['Trek', 'Cannondale'])
  .should(year: 2015)
  .boost(2.0) { should(year: 2016) }

# More score manipulation:
  .coord_similarity(false) # disable coord similarity (no score normalization)
  .boost(0.0) { must(brand: ['Trek', 'Cannondale']) } # no score
  .boost(fixed: 1.0) { should(year: 2015) } # fixed score
  .boost(fixed: 2.0) { should(year: 2016) }
  .each_with_score { |bike, score| puts "#{bike.name} got score: #{score}" }

# Get average bike price by year and category, for bikes newer than 2014
  .must(year: { gte: 2014 })
  .each { |keys, price| puts "#{keys[:year]}/#{keys[:category]} => #{price}" }

# Get average and maximum bike price for bikes newer than 2014
  .must(year: { gte: 2014 })
  .compose do |c|

# Search bikes ids that have shimano parts:
BikeIndex.must(parts: { brand: 'shimano' }).ids

Missing features

The following are some features that we plan to implement in the future:

  • Highlighting support
  • Suggesters support
  • Geo fields and queries support
  • Custom analizers support
  • More queries types support (multi-match, common-terms, wildcard, fuzzy, etc)


Bug reports and pull requests are welcome on GitHub at https://github.com/platanus/elastic-rails. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.


The gem is available as open source under the terms of the MIT License.