Create index with new searchkick options in rails console
I am looking for a way to seamlessly update my Elasticsearch index.
I am moving from:
class Product < ApplicationRecord
searchkick
end
To:
class Product < ApplicationRecord
searchkick text_middle: [:name, :item_volume]
end
My current idea is to
- open a rails console on target environment
- update searchkick options of my Product class as above
- create new index with
Product.reindex(async: true)
- promote the new index once my app is deployed with
Product.search_index.promote(index_name)
.
Unfortunately, when doing that, searchkick won't let me change the options and raises:
RuntimeError (Only call searchkick once per model)
Any idea?
Traceback (most recent call last):
10: from bin/rails:9:in `<main>'
9: from bin/rails:9:in `require'
8: from /app/vendor/bundle/ruby/2.5.0/gems/railties-4.2.11/lib/rails/commands.rb:17:in `<top (required)>'
7: from /app/vendor/bundle/ruby/2.5.0/gems/railties-4.2.11/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
6: from /app/vendor/bundle/ruby/2.5.0/gems/railties-4.2.11/lib/rails/commands/commands_tasks.rb:68:in `console'
5: from /app/vendor/bundle/ruby/2.5.0/gems/railties-4.2.11/lib/rails/commands/console.rb:9:in `start'
4: from /app/vendor/bundle/ruby/2.5.0/gems/railties-4.2.11/lib/rails/commands/console.rb:110:in `start'
3: from (irb):1
2: from (irb):8:in `<class:Product>'
1: from /app/vendor/bundle/ruby/2.5.0/gems/searchkick-2.5.0/lib/searchkick/model.rb:11:in `searchkick'
Solution 1:
I would suggest doing this in a worker and you can use perhaps a Config model variable or Redis cache to set up your model to be ready to use the new index once it's promoted.
class UpdateProductIndexWorker
include Sidekiq::Worker
sidekiq_options retry: 0
def peform
#patch your product model for this job
class Product
searchkick text_middle: [:name, :item_volume]
end
# track status of reindex
Searchkick.redis = Redis.new
new_index = Product.reindex(async: true)
index_name = new_index['index_name']
loop do
break if Searchkick.reindex_status(index_name)[:completed]
sleep 60 # or however long you want to interval check
end
# You'll need to tell your app somehow to use new index
# You could use perhaps a Config model which could be something
# you could store in the database for exmaple:
# Config.create(name: 'use_new_products_index', state: true)
# or use redis assuming it's your Rails cache
Rails.cache.write('use_new_product_index', true, expires_in: 1.year)
# now you can just use the new index
Product.searchkick_index.promote(index_name)
end
end
Update your model to use new index when it's ready.
class Product < ApplicationRecord
def self.use_new_index?
# Config.find_by(name: 'use_new_products_index').try(:state)
# or use Rails/redis cachea
Rails.cache.read('use_new_product_index')
end
# use the config data to make the model backward compatable until
# the new index is ready. Later you'll remove this in a cleanup deploy.
if Product.use_new_index?
searchkick text_middle: [:name, :item_volume]
else
searchkick
end
end