LoginSignup
18

More than 5 years have passed since last update.

ElasticSearch-Railsの導入手順・詰まった点など

Posted at

RailsからElasticSearchを使いたい

初めてElasticSearchを使ったので、その際の手順や詰まった点などまとめてみました。

導入手順

Install

gem install elasticsearch-model
gem install elasticsearch-rails

既存のModelに反映させる

class Line < ActiveRecord::Base
  include Elasticsearch::Model
  ...

設定の作成

/config/initializers/elasticsearch.rb
config = {
    host: "http://localhost:9200/",
}

if File.exists?("config/elasticsearch.yml")
  config.merge!(YAML.load_file("config/elasticsearch.yml")[Rails.env].symbolize_keys)
end
Elasticsearch::Model.client = Elasticsearch::Client.new(config)
/config/elasticsearch.yml
development: &default
  host: 'http://localhost:9200/'
test:
  <<: *default
staging:
  <<: *default
production:
  host: 'http://XXX/'

ref) https://medium.com/@thecolorfulcrayon/configuring-elasticsearch-on-rails-8bcbe973e9e7

Rails Consoleで動作確認

[1] pry(main)> Line.__elasticsearch__.create_index!
=> {"acknowledged"=>true, "shards_acknowledged"=>true}
[2] pry(main)> Line.__elasticsearch__.import
  Line Load (1.0ms)  SELECT  `lines`.* FROM `lines`  ORDER BY `lines`.`id` ASC LIMIT 1000
=> 0
[3] pry(main)> Line.__elasticsearch__.search('秋田新幹線').results.first
=> #<Elasticsearch::Model::Response::Result:0x007fbdeccf3080
 @result=
  {"_index"=>"lines",
   "_type"=>"line",
...

悩んだ点

ElasticSearchをPrimary Data Storeに使用することの是非

結局Primary Data Storeとして使用しませんでしたが、下記サイトを参考にしました。

ネストされたデータの作成

/app/models/concerns/line_searchable.rb
module LineSearchable
  extend ActiveSupport::Concern

  included do
    include Elasticsearch::Model

    index_name    "line_#{Rails.env}"

    settings do
      mappings dynamic: 'false' do
        indexes :name, type: 'text'

        indexes :stations, type: :nested do
          indexes :id
          indexes :name, type: 'text'
        end
      end
    end

    def as_indexed_json(option = {})
      category_attrs = {
          id: self.id,
          name: self.name
      }
      category_attrs[:stations] = self.stations.map do |station|
        {
            id: station.id
            name: station.name
        }
      end

      category_attrs.as_json
    end
  end
end

ネストされたデータの検索

[1] pry(main)> query = {:query=>
  {:bool=>
    {:must=>
      [{"term"=>{"name"=>"JR中央線(快速)"}},
       {"nested"=>{"path"=>"stations", "query"=>{"terms"=>{"stations.id"=>[1, 2, 3]}}}}]}}}
[2] pry(main)> Line.search(query).results.first
...

includesやwhereをしてからImportしたい

コードを読み込んで行ったらoptionsに色々な機能を持たせていたので、それによって対応しました。

/app/models/concerns/line_searchable.rb
def self.elasticsearch_import
  self.__elasticsearch__.import(query: -> { includes(:stations).where('XXX') })
end

ref)
https://github.com/elastic/elasticsearch-rails/blob/b6d485748c71a07d064ea2a46a6da82d64a04cd7/elasticsearch-model/lib/elasticsearch/model/adapters/active_record.rb#L96

同一モデルでもindexやtypeを動的に変えたい

結局やりませんでしたが、使用する際にoptionsで動的にindexやtypeを指定することで対応可能かと思われます。

ref)
https://github.com/elastic/elasticsearch-rails/blob/master/elasticsearch-model/lib/elasticsearch/model/importing.rb#L102

ElasticSearchとactiverecord-importでNamespaceが被っている

[1] pry(main)> Line.import
ArgumentError: Invalid arguments!

activerecord-importが先に導入されていたこともあってactiverecord-import側のメソッド名を変えることはリスキーだったので、ElasticSearchのFunctionを呼ぶ際は__elastic_search__を経由することで対応しました。

[1] pry(main) > Line.__elastic_search__.import

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18