ElasticSearch and Spree integration

I. What is ElasticSearch? and Why do we need ElasticSearch ?

Elasticsearch is a open source full-text search and analysis engine. It allows us to store, search, and analyze large volumes of data very quickly and nearly real-time. It is often used to make a powers applications with complex search features and requirements. Elasticsearch provides a distributed system on top of Lucene StandardAnalyzer for automatic indexing and type guessing, and uses a JSON-based REST API to reference Lucene features.

It is easy to set up right out of the box as it comes with reasonable defaults and hides the complexity for beginners. It has a short learning curve to grasp the basics, so anyone with a little effort can become productive very quickly. It has no schema, uses some defaults to index the data.

For Spree Commerce store, it can help your application powerful, flexible, and reliable searching , especially when large amounts of data are involved. The easier and faster customers are able to find what they are looking for, the more likely they are to purchase something in your store.

Speed It is able to execute complex queries extremely fast. It also caches almost all of the structured queries commonly used as a filter for the result set and executes them only once. For every other request which contains a cached filter, it checks the result from the cache. This saves the time parsing and executing the query improving the speed.

Search options It implements a lot of features when it comes to search such as customized splitting text into words, customized stemming, faceted search, full-text search, auto-completion, and instant search.

Scalability As it has a distributed architecture it enables to scale up to thousands of servers and accommodate petabytes of data.

Flexibility All data types are accepted as numbers, text, geo, structured, unstructured...

How to integrate ElasticSearch with Spree

Now in Spree, it hase a gem call spree_elasticsearch that we use it in our app. Let's install it:

gem 'spree_elasticsearch', github: 'javereec/spree_elasticsearch', branch: '3-0-stable'

Run bundle and setup generator

touch config/elasticsearch.yml
bundle exec rails g spree_elasticsearch:install

Now you can see and edit file config_elasticserach.rb

 defaults: &defaults
    hosts: [""]
    bootstrap: true
    <<: *defaults
    index: development
    <<: *defaults
    index: test
    <<: *defaults
    index: production

you can see class that use for searching was changed:

Spree.config do |config|
  config.searcher_class = Spree::Search::Elasticsearch

In app/models/spree/product_decorator.rb we need to add callbacks to the model

module Spree
  Product.class_eval do
    include Elasticsearch::Model
    include Elasticsearch::Model::Callbacks

and in product_decorator.rb you will see something like this:

module Spree
  Product.class_eval do
    include Elasticsearch::Model
    include Elasticsearch::Model::Callbacks
    index_name Spree::ElasticsearchSettings.index
    document_type 'spree_product'
    mapping _all: {"index_analyzer" => "nGram_analyzer", "search_analyzer" => "whitespace_analyzer"} do
      indexes :name, type: 'multi_field' do
        indexes :name, type: 'string', analyzer: 'nGram_analyzer', boost: 100
        indexes :untouched, type: 'string', include_in_all: false, index: 'not_analyzed'
      indexes :description, analyzer: 'snowball'
      indexes :available_on, type: 'date', format: 'dateOptionalTime', include_in_all: false
      indexes :price, type: 'double'
      indexes :sku, type: 'string', index: 'not_analyzed'
      indexes :taxon_ids, type: 'string', index: 'not_analyzed'
      indexes :properties, type: 'string', index: 'not_analyzed'
    def as_indexed_json(options={})
      result = as_json({
        methods: [:price, :sku],
        only: [:available_on, :description, :name],
        include: {
          variants: {
            only: [:sku],
            include: {
              option_values: {
                only: [:name, :presentation]
      result[:properties] = property_list unless property_list.empty?
      result[:taxon_ids] = taxons.map(&:self_and_ancestors).flatten.uniq.map(&:id) unless taxons.empty?
    def self.get(product_id)
      Elasticsearch::Model::Response::Result.new(__elasticsearch__.client.get index: index_name, type: document_type, id: product_id)

Now let's start to make a query to Elasticsearch. We need to create a searcher like below:

@searcher = build_searcher(
     taxon: taxon_ids,
     include_images: true,
     page: 1,
     per_page: per_page_size

and we can get the result by code below:

products = @searcher.retrieve_products

For create and recreate index, you can use this rake task:

bundle exec rake spree_elasticsearch:load_products

Source: https://github.com/javereec/spree_elasticsearch

All Rights Reserved

Let's register a Viblo Account to get more interesting posts.