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 ::Search::Error::RangeFilterMissing.new(filter: name) if min.blank? && max.blank?
@name = name
@min = min
@max = max
end
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?
end
end
private
attr_accessor :name, :min, :max
end
end
end
Which can be easily passed as an input for the Model.search as:
def search_query
{ query: query, sort: sort, aggs: aggregations }
end
As long as we organized it with the following interface:
def query
filter.presence || { match_all: {} }
end
def filter
return if filter_options.blank?
filter = { bool: { filter: [] } }
filter_options.map do |f|
filter[:bool][:filter].push(f)
end
filter
end
where the filter_options
contains already mangled instances of various filter classes abstracitons.