mixiでも使われているって言う全文検索エンジンHyperEstraier。mixiの膨大な件数を処理するぐらいだから相当スケーラビリティもあるだろなーとおもいつつ、SQLだけで全文検索できちゃうMySQL+Sennaが便利なんで、そっちばっかり使ってました。
MySQL+Senna(Triton)も、PostgreSQL+Senna(Ludia)も、属性付きで検索した場合、Sennaで全文検索してから、その答えをSQLで絞り込みを行っているようで、たとえば、個人のメッセージを保存しているテーブルを検索した場合など、属性の値が多い場合には、かなり効率が悪くなる事が見えてました。
本当は、メッセージみたいなものは、個人ごとにインデックスをもって処理するべきなんだろうなと思っていて、SennaでやるかHyperEstraierでやるか迷いつつ、ぐぐっていたところ、HyperEstraierがどんぴしゃりな機能を持っているのを発見しました。
疑似ノードマスタという機能で、大量のインデックスを保持することが容易にできるようです。
この疑似ノードマスターの話は、おいおい調べるとして、まずはRailsからHyperEstraierを触ってみます。
というほどのものではなく、acts_as_searchableというプラグインを使えば、ほぼ意識することなく、HyperEstraierを扱えるようです。
このプラグインは単純な全文検索だけでなく、属性検索などもサポートしており、通常の用途であれば、これで事が足りそうです。
# estcmd create -tr -attr @user_id num messages class Message < ActiveRecord::Base acts_as_searchable :searchable_fields => [:subject, :body], :attributes => {:user_id => :user_id} end
と指定する事で、下記の様に特定ユーザのメッセージだけ検索するという事ができます。
Message.fulltext_search("masuidrive", :attributes => "user_id NUMEQ %d" % User.find_by_name('guest'))
このプラグインで、注意が必要なのは、検索対象として指定したカラムのsetterメソッドが定義されている点です。
この場合は、subject=, body=, user_id=が上書きされます。
プラグインの実行タイミングなどから、define_method_chainは使えないようなので、これらのメソッドを上書きするときは、そのメソッドの中から値を更新するときに、write_attributeの代わりに、write_changed_attributeを呼ぶ様にしてください。
以上、バットノウハウでした。
属性検索の演算子は、ユーザーズガイドの検索条件式を見てください。通常の=演算子などは使えません。
masuidrive
shidara君と、ActiveModelをベースにして、ActiveHEなんてあったらいいのにねと、言ってみる。
take-sa
HyperEstraierはデモサイトのWikipedia全文検索を
常用してるぐらいかな?
一度MacOSXに入れようと思ったのだが、敷居が高いのと
すぐにOS側で検索機能が強化されたからやめちゃった。
HyperEstraierの記事も楽しみにするで〜!
グニャラくん
TritonnもLudiaも属性付き前文検索のチューニングはなかなか大変ですよね…
Sennaの次回バージョンはデータベースがついて、
自前で属性検索と前文検索を組み合わせることができるようになるので、
その際にはまた試してみてください!
masuidrive
おおお!まじっすか!
それは楽しみにしています。
この手の用途は増えているんですが、なかなか使いやすいものが無くて。