
Indexers
Dsl to delegate searches to elasticsearch in rails.
Why
I did this gem to:
- Gain control of the queries without losing simplicity.
- Have out of the box integration with activerecord and pagers.
- Deal with the just in time nature of elasticsearch.
- Integrate activerecord includes on it.
- Have a convention of how to use suggestions.
Install
Put this line in your Gemfile:
gem 'indexers'
Then bundle:
$ bundle
To install Redis you can use homebrew:
$ brew install elasticsearch
NOTE: This gem is tested agains version 5.4.
Configuration
Generate the configuration file:
$ bundle exec rails g indexers:install
Set the global settings:
Indexers.configure do |config|
config.mappings do
name do
type 'string'
fields do
raw do
type 'keyword'
end
end
end
category type: 'string'
price type: 'long'
currency type: 'string'
product_suggestions do
type 'completion'
analyzer 'simple'
end
end
end
If you need to personalize the analysis:
Indexers.configure do |config|
config.analysis do
filter do
ngram do
type 'nGram'
min_gram 2
max_gram 20
end
end
analyzer do
ngram do
type 'custom'
tokenizer 'standard'
filter %w(lowercase ngram)
end
end
end
end
NOTE: You may want to personalize the generated config/elasticsearch.yml.
Definitions
Generate an index:
$ bundle exec rails g indexers:indexer products
Define the mappings, serialization and search in the index:
Indexers.define :product do
mappings do
properties :name, :category, :price, :product_suggestions
end
serialize do |record|
extract record, :name, :category, :price
product_suggestions do
input [record.name, transliterate(record.name)].uniq
end
end
search do |*args|
options = args.extract_options!
term = args.first
query do
if term.present?
multi_match do
query term
type 'phrase_prefix'
fields %w(name category)
end
else
match_all
end
end
end
end
Traits
You can dry complex searches or serializations using traits:
Indexers.define :product do
search do |*args|
options = args.extract_options!
shop = options[:shop]
term = args.first
query do
bool do
must do
if term.present?
multi_match do
query term
type 'phrase_prefix'
fields %w(name category)
end
else
match_all
end
end
traits :shop
end
end
end
trait :shop do
filter do
if shop
term do
_parent shop.id
end
end
end
end
end
NOTE: The binding is persisted, there is no need to redefine variables.
Indexing
The index will be updated every time a record is created, updated or destroyed:
product = Product.create(name: 'Les Paul', category: 'Gibson')
You can force this actions manually with:
product.index
product.reindex
product.unindex
Rake tasks
At any time you can build/rebuild your indexers using:
$ bundle exec rake indexers:index
$ bundle exec rake indexers:reindex
$ bundle exec rake indexers:unindex
Search
Use the included search method in the model:
products = Product.search('Les Paul')
The result can be used as a collection in views:
<%= render products %>
Includes
Similar to using activerecod:
Product.search.includes(:shop)
Works the same as pagers gem:
Products.search.page(1, padding: 4, length: 30)
You can force a record to be part of the results by id:
Products.search.page(1, with: 4)
Or the opposite:
Products.search.page(4, without: 4)
And you can send the collection directly to the view helper:
<%= paginate products %>
Order
Same as using activerecord:
Product.search.order(name: :asc)
You can use a computed sort by declare it in the configuration:
Indexers.configure do |config|
config.computed_sort :price do |direction|
type 'number'
script do
inline "if (params['_source']['currency'] == 'UYU') { doc['price'].value * 30 }"
end
order direction
end
end
Suggestions
You need to first define the logic in the configuration:
Indexers.configure do |config|
config.suggestions do |name, term, options={}|
type = name.to_s.singularize
text (term || '')
completion do
field "#{type}_suggestions"
end
end
end
Then you can get suggestions using the suggest method:
Indexers.suggest :product, 'gibson'
The result is an array of hashes with a text property:
[{ text: 'Les Paul' }, ...]
Contributing
Any issue, pull request, comment of any kind is more than welcome!
I will mainly ensure compatibility to Rails, AWS, PostgreSQL, Redis, Elasticsearch and FreeBSD.
Credits
This gem is maintained and funded by museways.
License
It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.