Today I Learned

Working with ElasticSearch and Rails; part 3

“You can pass any object which implements a to_hash method, which is called automatically, so you can use a custom class or your favourite JSON builder to build the search definition”

The aforementioned sentence lies somewhere in the middle of the lengthy Readme file for the elasticsearch-model gem and can be easily overlooked, however it let’s you to create abstraction for some standard elements of the user intefrace.

Imagine you have a listing displaying records, which can be filtered by the user. There are different types of filters depending if the actual field is a date, string, boolean and so on. User interacts with the filters and the request is being fired up to the backend.

Then on the backend, you translate the payload, matching the params contents against in the the info specified in your META for a given search class, generating an array of filtering directives as the one below:

module Search
 module Filters
   class Range
     def initialize(name:, min:, max:)
       raise name) if min.blank? && max.blank?

       @name = name
       @min  = min
       @max  = max

     def to_hash
       {}.tap do |range|
         range[:range] = Hash[name, {}]
         range[:range][name][:gte] = min if min.present?
         range[:range][name][:lte] = max if max.present?


     attr_accessor :name, :min, :max

Which can be easily passed as an input for the as:

def search_query
  { query: query, sort: sort, aggs: aggregations }

As long as we organized it with the following interface:

def query
  filter.presence || { match_all: {} }

def filter
  return if filter_options.blank?

  filter = { bool: { filter: [] } } do |f|


where the filter_options contains already mangled instances of various filter classes abstracitons.