estella
Builds on elasticsearch-model to make your Ruby objects searchable with Elasticsearch. Provides fine-grained control of fields, analysis, filters, weightings and boosts.
Installation
gem 'estella'
The module will try to use Elasticsearch on localhost:9200
by default. You can configure your global ES client like so:
Elasticsearch::Model.client = Elasticsearch::Client.new host: 'foo.com', log: true
It is also configurable on a per model basis, see the doc.
Indexing
Just include the Estella::Searchable
module and add a searchable
block in your ActiveRecord or Mongoid model declaring the fields to be indexed like so:
class Artist < ActiveRecord::Base
include Estella::Searchable
searchable do
field :name, type: :string, analysis: Estella::Analysis::FULLTEXT_ANALYSIS, factor: 1.0
field :keywords, type: :string, analysis: ['snowball', 'shingle'], factor: 0.5
field :bio, using: :biography, type: :string, index: :not_analyzed
field :birth_date, type: :date
field :follows, type: :integer
field :published, type: :boolean, filter: true
boost :follows, modifier: 'log1p', factor: 1E-3
end
...
end
For a full understanding of the options available for field mappings, see the Elastic mapping documentation.
The filter
option allows the field to be used as a filter at search time.
You can optionally provide field weightings to be applied at search time using the factor
option. These are multipliers.
Document-level boosts can be applied with the boost
declaration, see the field_value_factor documentation for boost options.
While filter
, boost
and factor
are query options, Estella allows for their static declaration in the searchable
block for simplicity - they will be applied at query time by default when using #estella_search
.
You can now create your index mappings with this migration:
Artist.reload_index!
This uses a default index naming scheme based on your model name, which you can override simply by declaring the following in your model:
index_name 'my_index_name'
Start indexing documents simply by creating or saving them:
Artist.create(name: 'Frank Estella', keywords: ['art', 'minimalism'])
Estella adds after_save
and after_destroy
callbacks for inline indexing, override these callbacks if you'd like to do your indexing in a background process. For example:
class Artist < ActiveRecord::Base
include Estella::Searchable
# disable estella inline callbacks
skip_callback(:save, :after, :es_index)
skip_callback(:destroy, :after, :es_delete)
# declare your own
after_save :delay_es_index
after_destroy :delay_es_delete
...
end
A number of class methods are available for indexing.
# returns true if the index exists
Artist.index_exists?
# creates the index
Artist.create_index!
# delete and re-create the index without reindexing data
Artist.reload_index!
# recreate the index and reindex all data
Artist.recreate_index!
# deletes the index
Artist.delete_index!
# commit any outstanding writes
Artist.refresh_index!
Custom Analysis
Estella defines standard
, snowball
, ngram
and shingle
analysers by default. These cover most search contexts, including auto-suggest. In order to enable full-text search for a field, use:
analysis: Estella::Analysis::FULLTEXT_ANALYSIS
Or alternatively select your analysis by listing the analysers you want enabled for a given field:
es_field :keywords, type: :string, analysis: ['snowball', 'shingle']
The searchable block takes a settings
hash in case you require custom analysers or sharding (see doc):
my_analysis = {
tokenizer: {
...
},
filter: {
...
}
}
my_settings = {
analysis: my_analysis,
index: {
number_of_shards: 1,
number_of_replicas: 1
}
}
searchable my_settings do
...
end
It will otherwise use Estella defaults.
Searching
Finally perform full-text search:
Artist.estella_search(term: 'frank')
Artist.estella_search(term: 'minimalism')
Estella searches all analysed text fields by default, using a multi_match search. The search will return an array of database records in score order. If you'd like access to the raw Elasticsearch response data use the raw
option:
Artist.estella_search(term: 'frank', raw: true)
Estella supports filtering on filter
fields and pagination:
Artist.estella_search(term: 'frank', published: true)
Artist.estella_search(term: 'frank', size: 10, from: 5)
If you'd like to customize your query further, you can extend Estella::Query
and override the query_definition
:
class MyQuery < Estella::Query
def query_definition
{
multi_match: {
...
}
}
end
end
And then override class method estella_search_query
to direct Estella to use your query object:
class Artist < ActiveRecord::Base
include Estella::Searchable
searchable do
...
end
def self.estella_search_query
MyQuery
end
end
Artist.estella_search (term: 'frank')
For further search customization, see the elasticsearch dsl.
Estella works with any ActiveRecord or Mongoid compatible data models.
Contributing
Just fork the repo and submit a pull request.
License
Copyright (c) 2017 Artsy Inc., MIT License.