sunspot(solr)の設定方法
Advanced Fulltext Search Configuration - GitHubの意訳。
デフォルトの設定だと、スペースか他の区切り文字で区切られている前提。区切りにはStandardTokenizerというのを使ってる。大文字小文字を区別させないようにするためにはLowerCaseFilterというのを使ってる。
初回に rake sunspot:solr:start を叩いたときに solr/conf/schema.xml が出来る。これをいじることで全文検索の設定が出来る。
デフォルトの設定に追加するには
stemming(例:"run"で"runnning"が引っかかる)と同義語の設定を追加するには schema.xml を下記のようにする
<fieldType name="text" class="solr.TextField" omitNorms="false"> <analyzer> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.StandardFilterFactory"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType>
上記の fieldType の属性は sunspot で定義したテキストフィールドで使われる。analyzer は下記のように index を作る時とクエリを投げるときとで分けることが出来る。
<fieldType name="text" class="solr.TextField" omitNorms="false"> <analyzer type="query"> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.StandardFilterFactory"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> <analyzer type="index"> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.StandardFilterFactory"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType>
ここまではインデックス作る時とクエリ投げるときで一緒の設定だし、デフォルトの設定と同じ。ここから、インデックスを作る時に同義語のサポートをするように修正する。
<fieldType name="text" class="solr.TextField" omitNorms="false"> <analyzer type="query"> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.StandardFilterFactory"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> <analyzer type="index"> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.StandardFilterFactory"/> <filter class="solr.LowerCaseFilterFactory"/> <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true" /> </analyzer> </fieldType>
このまま同義語のサポートを続けるなら synonyms.txt を作る必要がある。詳しくは AnalyzersTokenizersTokenFilters - Solr Wiki
さらに stemming の機能を追加する。
<fieldType name="text" class="solr.TextField" omitNorms="false"> <analyzer type="query"> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.StandardFilterFactory"/> <filter class="solr.LowerCaseFilterFactory"/> <filter class="solr.PorterStemFilterFactory"/> </analyzer> <analyzer type="index"> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.StandardFilterFactory"/> <filter class="solr.LowerCaseFilterFactory"/> <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true" /> <filter class="solr.PorterStemFilterFactory"/> </analyzer> </fieldType>
設定を変更したら、Solr を再起動してインデックスを作り直す必要がある。これ以上の設定を知りたければ wiki をみるべし。
sunspotのチュートリアル意訳
インストール
solrのインストールはmacなら
brew install solr
でOK。Rails側はまずGemfileに
gem 'sunspot_rails'
を書いて bundle install。それから
rails g sunspot_rails:install
で config/sunspot.yml を作成。
solr のインスタンスを起動
下記の rake タスクで起動できる
rake sunspot:solr:start
初回実行時に Rails.root/solr ディレクトリが作成される。ここには Solr の設定とインデックスのデータファイルが置かれる。solr/data は.gitignoreに追加しておいた方がいいかも。Solr の設定いじりたい場合は solr/conf 配下のファイルをほげる。
モデルの設定
下記みたいに searchable メソッドで設定する。
class Post < ActiveRecord::Base searchable do text :title, :default_boost => 2 text :body end end
Postが作られたり更新されたりしたときに自動でインデックスを更新してくれるみたい。searchable の中身を変更したときなどは、手動でインデックスの更新が必要
rake sunspot:reindex
検索
こんな感じで検索すると
class PostsController < ApplicationController def search @search = Post.search(:include => [:comments]) do keywords(params[:q]) end end end
Sunspot::Search オブジェクトが @search に入る。view は下記のように書く。
.results - @search.each_hit_with_result do |hit, post| .result %h2= h post.title %h6== (#{h hit.score}) %p= h truncate(post.body, :length => 100) .pagination = will_paginate(@search.results)
これだと検索したときと検索してないときの処理を分けなきゃいけなくて面倒くさい気がする。
thinking_sphinx の設定
基本
config/sphinx.yml に設定を書く。下記のように database.yml っぽく environment 毎に設定を分けることが出来る。
development: port: 9312 test: port: 9312 production: port: 9312
rake ts:conf などとすると、このファイルを元に設定ファイル(config/{ENVIRONMENT}.sphinx.conf)が作られる。
インデックスファイルの場所
searchd_file_path オプションで設定する。デフォルトは db/sphinx/ENVIRONMENT
configuration, pid, log ファイルの場所
それぞれ下記のように設定する。下記の設定はデフォルト値。
development: config_file: "RAILS_ROOT/config/ENVIRONMENT.sphinx.conf" searchd_log_file: "RAILS_ROOT/log/searchd.log" query_log_file: "RAILS_ROOT/log/searchd.query.log" pid_file: "RAILS_ROOT/log/searchd.ENVIRONMENT.pid" # ... repeat for other environments
searchd デーモンのアドレスとポート
別マシンで searchd を動かしている時に設定する。
production: address: 10.0.0.4 port: 3200 # ... repeat for other environments if necessary
indexer のメモリ容量
index を作る indexer コマンドが使うメモリのリミットはデフォルトで64MB。下記のように変更できる。容量多くするとその分 index を作るのが早くなる
development: mem_limit: 128M # ... repeat for other environments
word stemming / Morphology
think と thinking が同じ意味を示すという設定ができる。英語とロシア語用の設定がビルトインされてる。Snowballで他の言語との設定も入手できるらしいけど、当然日本語はないので使うことはなさげ
development: morphology: stem_en # ... repeat for other environments
wildcard / Star Syntax
アスタリスクをワイルドカードとして使うことの出来る設定。デフォルトはオフ。
development: enable_star: true # ... repeat for other environments
接頭字と接中字のインデックス化
上のワイルドカードをオンにしたときに使う設定。prefix か infix を一度に両方設定することは出来ない。
最低のinfixとprefixの長さを設定できる。その長さより大きい単語がインデックスに入る?
0 が設定されたらこの機能は無効になる。全部をインデックス化したい場合は min_infix_len を 1 にセットする。でもそうするとパフォーマンスが悪化する。
development: min_infix_len: 3 # OR min_prefix_len: 3 # ... repeat for other environments
文字コード
デフォルトUTF-8
development: charset_type: sbcs # ... repeat for other environments
charset_tableで文字のマッピングを設定出来る。UTF-8で、他の文字(ASCII以外のことかな?)を含めたい時にcharset_tableを設定する。デフォルトは英語とロシア語。
development: charset_table: "0..9, A..Z->a..z, _, a..z, \ U+410..U+42F->U+430..U+44F, U+430..U+44F" # ... repeat for other environments
これに日本語の文字コードを指定してやっても、基本分かち書き(スペースで単語が分かれている)をベースにしているので、ngramの設定をしない限り日本語の検索は多分満足に出来ないと思う。
検索結果の件数が多い場合
検索を素早くするために、デフォルトではページネーションで使えるレコード数は1000をリミットにしている。変更したい場合は下記のようにして rebuild して、、
development: max_matches: 10000 # ... repeat for other environments
さらに検索時にオプションを付けてやる必要がある。
Article.search 'pancakes', :max_matches => 10_000
上記のコードは 10000 レコードを一発で得られるコードではない。あくまでページネーションの結果が 10000 レコードまで得られるというコード。一回で得たい場合は下記のようにする。
Article.search 'pancakes', :max_matches => 10_000, :per_page => 10_000
ASCIIcasts - Template Inheritance
ASCIIcasts - “Episode 269 - Template Inheritance”を見て。
以前、Edge Rails.info :: Template Inheritanceを見て、3.1 から 「デフォルトのビューの位置にファイルがない場合は、親のコントローラのビューを見る」という仕様が追加されるのは知ってた。でもコントローラ間で継承ってそうそう使わないし意味あるんかなーとずっと思ってた。
RailcastsとASCIIcastsを見て疑問が氷解。全てのコントローラは ApplicationController を継承している!!!
というわけで、デフォルトで使いまわしたいビューを views/application 配下において、上書きしたい場合は各コントローラに直接書くことで、同一のビューファイルの使い回しが簡単にできるようになりました。
view_path
コントローラの継承以外でも、view_path をいじることで同じようなことが出来る。例えばASCIIcasts上では、サブドメインを見て、特定のパスに置いてあるビューファイルを先に見るような実装を紹介していた。これはどうやらRails2からできる模様。
/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base protect_from_forgery before_filter :subdomain_view_path def subdomain_view_path if request.subdomain.present? prepend_view_path "app/views/#{request.subdomain}_subdomain" end end end
3.1のストリーミング機能の話
Automatic Flushing: The Rails 3.1 Plan « Katz Got Your Tongue?を読んで。3.1のストリーミング機能の実装が大体どんな感じか、非常に詳しく書かれていてためになった。Fiberを使っている手前、1.9でしか使えないのね。
thinking_sphinx の rake コマンドまとめ
インデックスとコンフィグの作成
下記のうちのどれでもOK。
rake thinking_sphinx:index
rake ts:index
rake ts:in
デフォルトでは、インデックスの作成に加えてコンフィグファイルも生成される。インデックスの作成だけしたいときには
rake thinking_sphinx:reindex
とする。逆にコンフィグだけ作りたいときには下記のどれかを実行する
rake thinking_sphinx:configure rake ts:conf rake ts:config
sphinx の実行と停止
rake thinking_sphinx:start rake ts:start rake thinking_sphinx:stop rake ts:stop
インデックスの再構築
インデックスの元データを変更したときには再構築およびsphinxの再起動が必要。indexしてstopしてstartしてもいいけど、それらをひとまとめにした rake コマンドも用意されている。
rake thinking_sphinx:rebuild rake ts:rebuild
増分インデックス
sphinxはフィールドの一部だけ更新することができない。そのようなときは全部更新する必要がある。thinking_sphinxではこの問題に対応するために、通常のインデックスの他に増分インデックスを用意している。
増分インデックスを使うための方法は下記の三つ。
- 変更フラグを見る(デフォルト)
- 更新時間を見る
- 変更フラグを見つつ delayed_job を利用
delayed_job 使うなら下記のコマンドを実行する
rake thinking_sphinx:delayed_delta rake ts:dd
cron等で定期的に実行するなら下記のコマンドを実行する。
rake thinking_sphinx:index:delta rake ts:dd
増分インデックスの具体的なやりかたは別途まとめる予定。
railsがTimeに追加してる便利メソッド
ActiveSupport::TimeWithZoneに追加されてる。
- today?
- past?
- future?
- between?