kenny-codes
5/14/2014 - 4:01 PM

Rake task for collocating multiple models as types in a single index.

Rake task for collocating multiple models as types in a single index.

# A Rake tasks to facilitate importing data from your models into a common Elasticsearch index.
#
# All models should declare a common index_name, and a document_type: 
#
#  class Article
#    include Elasticsearch::Model
#
#    index_name 'app_scoped_index'
#    document_type 'articles'
#
#    mappings do
#       ...
#    end
#  end
#
#

STDOUT.sync = true
STDERR.sync = true

begin; require 'ansi/progressbar'; rescue LoadError; end

namespace :elasticsearch do

  task :import => 'import:model'

  namespace :import do
   
    desc <<-DESC.gsub(/    /, '')
      Import all mappings from `app/models` (or use DIR environment variable) into a single index.

      All classes should declare a common `index_name`:

      class Article
        include Elasticsearch::Model
      
        index_name 'app_scoped_index'
      
        mappings do
          ...
        end
      end

      Usage: 
        $ rake environment elasticsearch:import:combined DIR=app/models
    DESC
    task :combined do
      dir = ENV['DIR'].to_s != '' ? ENV['DIR'] : Rails.root.join("app/models")
      
      puts "[IMPORT] Loading models from: #{dir} into a single index."

      all_mappings = {}
      index_klass = nil

      Dir.glob(File.join("#{dir}/**/*.rb")).each do |path|
        model_filename = path[/#{Regexp.escape(dir.to_s)}\/([^\.]+).rb/, 1]

        next if model_filename.match(/^concerns\//i) # Skip concerns/ folder

        begin
          klass = model_filename.camelize.constantize
        rescue NameError
          require(path) ? retry : raise(RuntimeError, "Cannot load class '#{klass}'")
        end

        # Skip if the class doesn't have Elasticsearch integration
        next unless klass.respond_to?(:__elasticsearch__)
        next unless klass.respond_to?(:mappings)

        puts "[IMPORT] Processing mappings for: #{klass}..."

        index_klass = klass
        all_mappings.merge! klass.mappings.to_hash
      end

      
      ## Create the combined index
      index_klass.__elasticsearch__.client.indices.create(
        { 
          index: index_klass.index_name, 
          body: { 
            mappings: all_mappings 
          }  
        })    

      ## Import data into the newly created index
      Rake::Task["elasticsearch:import:all"].invoke

      puts
    end

  end

end