Herokuで動いているRailaアプリに全文検索を導入しようとしてElasticsearchを検討したけどローカルですら導入につまづいてしまい、昔ちょっと触ったことがあるSolrにしたら余裕で導入できました。という話です。慣れの問題かも?

(いやーElasticsearchむずかしかったと思うんだわ…)

ローカル

% brew install solr

でおk。

Herokuへの導入

heroku addons:create websolr:staging-20 -a yourappname

でおk。グレード?は必要に応じて。最安では20ドルです。

Railsへの導入

sunspot gemが大変便利です。

sunspot/sunspot
Solr-powered search for Ruby objects. Contribute to sunspot/sunspot development by creating an account on GitHub.

gemの追加とbundle installして、

gem 'sunspot_rails'
gem 'sunspot_solr'

generateして、

% rails generate sunspot_rails:install

あ、ついでにローカルはSolrもスタートさせておきましょう。終わったら落としたいのでrake sunspot:solr:runでフォアグラウンドで動かしておきます。

bundle exec rake sunspot:solr:run

モデルの設定

検索対象にしたいモデルに設定用のコードを書きます。どのカラムをどんなデータタイプとして検索対象にするかを指定できます。子レコードの内容も親の検索内容とすることも簡単です。

Rubyのコードで書けるのでとても柔軟です。

# ユーザーの全員検索とかないかもしれないけど
class User < ActiveRecord::Base
  searchable do
    text :name, :email
  end
end
class Diary < ActiveRecord::Base
  belongs_to :user

  searchable do
    text :content
    text :user_name do
      user.name
    end
  end
end

みたいな感じですね。

検索方法

Diary.search { fulltext 'こんにちは' }

searchにブロックを渡して検索します。重み付けなどありますが、シンプルにやるならfulltextに検索ワードを渡すだけでOKです。

Solrへの登録

インデックシングと呼ぶのかな?データの登録をしないと検索しても何も返ってきません。

初回の登録はモデルごとにreindexするか、sunspotのrakeタスクでまとめて登録しちゃいましょう。

User.reindex
Diary.reindex

みたいにrails consoleから実行したり、

% bundle exec rake sunspot:reindex

として、全体を対象にすればOKです。

また、日々データが登録・更新されるような場合にはそのタイミングでデータを再度インデックスする必要がありますので、after_saveなどのコールバックとdelayed_jobなどのジョブを活用して都度インデックスしてあげましょう。

class User < ActiveRecord::Base
  after_save :update_index

  def update_index
    Sunspot.index(self)
  end
end

すでにデータが存在する本番環境へのデプロイの場合には、デプロイ後にインデックス登録のrakeタスクを走らせたほうがいいでしょう。じゃないと何も検索にかかりません。ただし、データ量が多い場合は時間がそれなりにかかりますので利用者の少ない時間帯がよいかと。

データ量にもよりますが、再インデックスのrakeタスクは夜間バッチで勝手に動くようにしておいてもよいと思います。