Elasticsearch with ruby on rails.

Elasticsearch is a full-text search engine with a RESTful web interface based on Lucene. Elasticsearch is developed in Java and is released as open source under the terms of the Apache License.

Elastic search installation is covered in my previous blog.

Elastic search in rails is split into three separate gems:

  • elasticsearch-model, which contains search integration for Ruby/Rails models such as ActiveRecord::Base and Mongoid
  • elasticsearch-persistence, which provides a standalone persistence layer for Ruby/Rails objects and models
  • elasticsearch-rails, which contains various features for Ruby on Rails applications

By default, our Rails app will connect to Elasticsearch at localhost:9200, but if you need to connect to a different server, uses the config/elasticsearch.yml file to overwrite the defaults.

config = {
  host: "http://localhost:9800/"
}

if File.exists?("config/elasticsearch.yml")
  config.merge!(YAML.load_file("config/elasticsearch.yml").symbolize_keys)
end

Elasticsearch::Model.client = Elasticsearch::Client.new(config)

First include elasticsearch in your model, so that elasticsearch methods and callbacks can be used in model.

class Merchant < ActiveRecord::Base
  include Elasticsearch::Model
  include Elasticsearch::Model::Callbacks
end

in console execute the code:

2.1.5 :001 > Merchant.search('ram').results.count
=> 2

Before searching we have to call a method import on the table we want to search, because initially our search index is empty. By importing all changes will be added to index automatically. So to populate data we need to execute:

Merchant.import

By deafult all columns are looked up in search. But these can be restricted by:

class Merchant < ActiveRecord::Base
 def as_indexed_json(options={})
    as_json(
        only: [:name, :about, :price, :gender]
     )
  end
end

When Elasticsearch indexes our Merchant object, we are telling it that we only want to search the name, about, price and gender attribute.

Their are two methods of getting the result.

Merchant.search('ram').results.count
Merchant.search('ram').records.count

records method will return collection of ActiveRecord objects and results will return collection Elasticsearch objects.

Now lets discuss the case of associations. Let’s have a model opening for merchants:

class Opening < ActiveRecord::Base
   belongs_to :merchant
end
class Merchant < ActiveRecord::Base
  include Elasticsearch::Model
  include Elasticsearch::Model::Callbacks

  has_many :openings
end

For adding openings in search index lets add that in as_indexed_json method of Merchant

class Merchant < ActiveRecord::Base
  include Elasticsearch::Model
  include Elasticsearch::Model::Callbacks

  has_many :openings

  def as_indexed_json(options={})
    as_json(
      only: [:name, :about, :price, :gender],
      include: [:openings]
    )
  end
end

Now all fields of opening models will also be added in index. To add specific column of association modify the method as:

def as_indexed_json(options={})
    as_json(
      only: [:name, :about, :price, :gender],
      include: {openings: {only: [:name, :section]}}
    )
  end

Now try fetch the method in console:

> rails c
2.1.5 :001 > Merchant.first.as_indexed_json
=> {"name"=>"Annu", "about"=>"Software engineer", "gender"=>1, "price"=>100.0, "openings"=>[{"name"=>"tution", "section"=>"C"}, {"name"=>"seminar", "section"=>"java"}]}

Now searching can be made for openings fields also. To make methods as searchable alter the as_indexed_json method as:

def as_indexed_json(options={})
    as_json(
      only: [:name, :about, :price, :gender],
      methods: [:availabilities, :specializations_id],
      include: {openings: {only: [:name, :section]}}
    )
  end
comments powered by Disqus