Apache Solrソースコードリーディング SearchHandler/SearchComponent
Solrのソースコードを読んでいる。
拡張ポイントが多いので、読むと結構工夫できそうなところがあって面白い。
記録に残すため、少しずつ書いてみようかと思う。
まず、エントリーポイントに近いSearchHandlerと、SearchComponent
SearchHandlerの前に、RequestHandlerとはなんぞやという話から。
solrconfig.xmlに設定されているDataImportHandlerとか、SearchHandlerなどは、/solr/以下のURLと対応づけられ、データを取り込むなり、検索するなりの機能が実装されている。基本的にRequestHandlerBaseというクラスを継承して実装される。
そのRequestHandlerBaseのサブクラスの一つに、検索を行う機能が実装されているo.a.s.handler.SearchHandlerがある。
(o.a.sというのはorg.apache.solrのパッケージ名のこと)
SearchHandlerは、検索結果を処理するために複数のSearchComponentを持っている。
その設定の部分がおそらく以下
public class SearchHandler extends RequestHandlerBase implements SolrCoreAware { static final String INIT_COMPONENTS = "components"; static final String INIT_FIRST_COMPONENTS = "first-components"; static final String INIT_LAST_COMPONENTS = "last-components";
中略
@SuppressWarnings("unchecked") public void inform(SolrCore core) { Object declaredComponents = initArgs.get(INIT_COMPONENTS); List<String> first = (List<String>) initArgs.get(INIT_FIRST_COMPONENTS); List<String> last = (List<String>) initArgs.get(INIT_LAST_COMPONENTS); List<String> list = null; boolean makeDebugLast = true; if( declaredComponents == null ) { // Use the default component list list = getDefaultComponents(); if( first != null ) { List<String> clist = first; clist.addAll( list ); list = clist; } if( last != null ) { list.addAll( last ); } } else { list = (List<String>)declaredComponents; if( first != null || last != null ) { throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, "First/Last components only valid if you do not declare 'components'"); } makeDebugLast = false; }
initArgsはRequestHandlerBaseのフィールドで、型はNamedList。おそらくここにsolrconfig.xmlで定義された内容が、XMLのname属性をキーにして格納される。だから、declaredComponentsにはname="components"以下の内容のList
クラス図チックに書くと、こんな感じ。
RequestHandlerBase---<|----SearchHandler----SearchComponent
RequestHandlerは、メイン処理をhandleRequestBodyメソッドに記述する。SearchHandlerは検索が役割だから当然検索をするが、実は検索処理自体はSearchHandlerには含まれない。SearchHandlerは定義されたSearchComponentを順に処理させるだけ。定義されたComponentの中に検索処理を行うQueryComponentが含まれることによって検索を行うことができる。
さっきのSearchHandlerのinformメソッドは、以下のように続く
// Build the component list components = new ArrayList<SearchComponent>( list.size() ); DebugComponent dbgCmp = null; for(String c : list){ SearchComponent comp = core.getSearchComponent( c ); if (comp instanceof DebugComponent && makeDebugLast == true){ dbgCmp = (DebugComponent) comp; } else { components.add(comp); log.info("Adding component:"+comp); } }
componentsにSearchComponentのリストを作っている
で、handleRequestBodyの中では、以下のように全コンポーネントの処理を順に行っている。
for( SearchComponent c : components ) {
c.prepare(rb);
}
for( SearchComponent c : components ) {
c.process(rb);
}
rbというのはResponseBuilderで、レスポンス内容を組み立てるために利用されるクラスだ。各ComponentはそこまでのResponseBuilderの内容を参考に処理を行い、結果を追加する。
たぶん、最後にhttpレスポンスを返すときにはこのクラスの内容をXMLなり、jsonなりにシリアライズして返しているのだろう。
component.processを呼び出すところは、rb.shards ==null,つまりdistributed searchをしているかどうかで処理を分岐しているんだけど、分散検索の方はちょっと呼んだだけじゃなにがどうなってんのか良くわかんなかったけど、componentのdistributedProcess,handleResponses,finishStageといったメソッドを呼び出してる。多分distributed searchに対応したsearchComponentを実装するにはこれらのメソッドを実装する必要があるんだと思う。
これを僕が調べたのは、特定キーワードの場合に特定のコンテンツを一番上に表示させたり検索結果から除外するために利用されるQueryElevationComponentを調べたかったから。これもSearchComponentの一種。
次はそのQueryElevationComponentを読んでみよう。
ラフィアン09年産出資馬
マイネアモーレ09に出資することにしました。
ラフィアンの紹介ページは以下。
http://www.ruffian.co.jp/pc/search?m=detail&type=1&code=109132
最初は1番のアイアイサクラ09に出資したかったのですが、抽選除外でこの馬をハズレ一位の形で買いました。
アグネスデジタル産駒は初めて持ちますけど、今まで持ったことのないタイプの馬ですね。ハーティーやモルゲンとも違うし、ミモーゼみたいな中距離馬とも違う。かといってダート馬かというとそんな感じもしないし、僕の経験からでは適性が良くわかんないですね。
ところで、出資馬追加でプロフィールの文言を変更したんですが、
http://d.hatena.ne.jp/Hayato/about
これで21頭目の出資なのね。10年以上やってればそんなに速いペースでもないですけども、もうそんなにやってたかなと言う気持ち。最近はクラッチが気を吐いてくれているくらいで3歳世代は惨憺たる状況なので、今年の2歳馬、1歳馬には本当に何とかして欲しいという勝手な期待をかけてます。