2017.12.15 Friday
スポンサーサイト
一定期間更新がないため広告を表示しています
| スポンサードリンク | - | | - | - |
関口宏司のLuceneブログOSS検索ライブラリのLuceneおよびそのサブプロジェクト(Solr/Tika/Mahoutなど)について
2006.09.22 Friday
100万件の特許情報の登録
あるテストのために、一昨日から昨日にかけて100万件の特許情報をLuceneを使って索引付けしたので結果をメモしておく。1件あたりの特許情報のテキストの大きさは約20KBである。
<<SUMMARY>> 総実行時間:29時間19分 10万件あたり登録時間:2時間42分 optimize()実行時間:2時間31分 インデックスサイズ:29.6 GB (31,890,259,478 バイト) なお、MAX BUFFERED DOCSは1000とした。PCはWindows XP/P4 2.6GHz/2GB RAM 2006.09.12 Tuesday
第2回エンタープライズサーチカンファレンス
以下、「第2回エンタープライズサーチカンファレンス」に出席した雑感や備忘録。
<<概念検索<<==================================>>キーワード検索>> Autonomy < Justsystem < 住友電工情報システム < FAST > Accela > Google
第1回エンタープライズサーチカンファレンスの話題はこちら。 2006.09.11 Monday
自分の家から一番近いレストランを探す(地理検索、地図検索)
最近、自分の中では「プアマンズ何々(貧乏人の何々)」というのが流行っている。
たとえば先週、Flex User Group(FxUG)の会合に初めて出席したときの話だ。Flexはここでのテーマと外れるので詳しい説明は省くが、RIA(Rich Internet Application)のアプリを開発するためのツールでAdobe(旧Macromedia)の製品である。 この集会の最後に「今後の参考のために」ということでアンケートが配られた。そのアンケートのある質問項目に「今後ユーザグループで取り上げてもらいたいテーマがあれば教えてください」というのがあったので、私はすかさず、このように書いた: 「Poor man's FDS(FDSを使わずに、Flex SDKだけでどこまでがんばれるか)」 FDSというのはFlex Data Servicesのことで、JMSのようなメッセージングサービスのサポートであったり、サーバのオブジェクトをクライアントで簡単に扱えるようになっていたり、という機能が含まれるのだが、Flexユーザの間ではその高機能さよりもライセンスの高価さで有名な製品なのであった。まったく気の毒なことである。 一方、Flex SDKというのは無償で使える開発者キットである。つまり、「SDKを使ってこのくらいがんばって書けばFDSの機能をこのくらい模倣できる」、そんなテーマをFxUGで取り上げて欲しいと書いたわけである。 なんとずうずうしい要求だろうか。しかし、安く上がるのに越したことはない。SDKでがんばることでどこまでFDSに迫れるのか、FDSが必要な場面はどういう要求仕様のときに限るのか、こういうことが明らかになると開発者としてはありがたい。しかし、Adobeとしてはたまったものではないだろう。FxUGの会場と参加者への飲み物を無料で提供しているというのに。。。 ところで、ライセンスフリー(無料)のLuceneを担いでいる私のところには「商用の全文検索エンジンは高価なのでLuceneでできませんか」という話が割とある。 先週の訪問先もその向きの話で、私はこういった案件に愛着を感じつつ「Poor man's XXX」と内心ひそかに呼んでいる(XXXにはある商用全文検索エンジンの名称が入る)。 さて、この訪問先での話はすでにXXXを使って動いているシステムをLuceneを使うようにXXXを置き換えられないか、というものであった。そこで、現在XXXの機能を使って実現しているいろいろな検索機能をLuceneでできるかどうかという検討を行った。 その中で面白いと思ったものが「GEOサーチ」というものだ。GEOはローマ字読みしてはいけない。「ジオサーチ」と読む。Geography(地理)のGEOで、「自分の家から一番近いレストランを探す」というような検索である。 この場合、すべての文書(レストラン検索で言うと1つの文書は1つのレストラン情報に相当)には位置情報(緯度経度情報)が属性としてつけられている。そして、その情報を使って「ある地点を中心とした縦横10km以内の中華料理店」や「ある地点から半径5km以内のすし屋」を探し、近い順にソートして表示するのである。 このような地理検索、地図検索はLuceneを使い慣れている方なら簡単だと思うかもしれない。X座標、Y座標でANDの範囲検索をすれば「ある地点を中心とした縦横10km以内の中華料理店」は得られるからだ。また、LIAには地理検索の結果を「自分の家から近い順にソートして表示する」サンプルプログラムが掲載されている。 しかし、「縦横」ではなく「半径」だったらどうだろう。「縦横」と「半径」の関係をわかりやすく図にすると次のようになる。 「縦横」の場合斜線の部分も含むが、半径の場合は斜線の部分に位置するレストランを検索結果に含めてはいけない。先週の訪問先では、「半径」で(も)検索したいという要望であった。 今回はこの「半径○km以内」を範囲検索する方法を検討してみる。 FunctionQueryの検討 Lucene 2.0にもいまだ採用されていないが、JIRAにFunctionQueryというQueryクラスが提案されている。 http://issues.apache.org/jira/browse/LUCENE-446 このQueryはフィールドの値を計算してスコアに反映させることができるクラスであり、計算ロジックはValueSourceという抽象クラスをextendsしたクラスで実装し、それをFunctionQueryに与えるようになっている。このFunctionQueryを「半径○km以内」を範囲検索するQueryに使えないだろうか。 そこで試しに、ValueSourceをextendsしたDistanceFunctionクラスを実装してみる。
上記のように、getValues()メソッドにて基準点(自分の家の座標)からのレストランの距離をスコアに反映するように実装する。 このクラスを次のようにFunctionQueryに渡して使うと、文書(=レストラン)のX座標とY座標の値から、ある地点からの距離をスコアに反映させることができる(基準点(5,5)から距離5以内のすし屋を検索している。以下同じ)。
しかし、(たとえスコアを0にしようとも)円の外の座標を持つ文書を「拒否」することはできない。そのため実行結果は次のようになり、座標(9,1)のすし屋も検索されてしまう。
したがって、ConstantScoreRangeQueryをBooleanQueryで組み合わせても図の正方形の部分以外を検索しないようにできるだけで、図の斜線の部分は検索結果に含まれてしまい、FunctionQueryでは「半径○km以内」の範囲検索は実現できないことがわかった。 DistanceQueryの実装を検討 次に、FunctionQueryでの失敗の教訓を生かし、Queryのrewrite()をオーバーライドするDistanceQueryなるものを作成することを検討してみる。 このQueryには基準(レストラン検索で言えば、自分の家が基準)となる座標と半径を与える。そして、rewrite()にて可能性のある座標の文書をすべてインデックスから拾うための別のプリミティブなQueryに置き換えるよう展開する。スコアは置き換えられたQueryにて計算されるため、「自分の家から近い順に結果を得る」ためには別途Sortを使用する必要がある。 しかしそもそも無数に可能性のある座標(グラフの網の目が細かくなれば円に含まれる座標は増えていく)を拾って別のQueryにどのように展開すればいいのだろう。BooleanQueryはTooManyClauses例外が投げられてしまうのでもちろん使えない。 ・・・まったく思いつかないので、この方法は机上の検討だけでボツにした。 RadiusFilterの実装を検討 次に、図のようなRadiusFilterというFilterの実装を検討してみる。 このFilterは円形をしており、ボツになったDistanceQueryと同じように円の中心座標と半径を与える。そして、Filterを通過した文書をBitSetで返すようにする。 RadiusFilterはFilterを通過した文書をBitSetで返すためにFilterのbits()メソッドをオーバーライドしなければならない。そのようなbits()メソッドは実装可能だろうか。 一般のFilterでは、bits()の引数で渡されたIndexReaderを使ってTermEnumを取得し、そのうちのTermが条件を満たしていればBitSetをセットする、というように実装される。このとき、調べる属性は一種類であるのが通常である。TermEnumは属性ごとにソートされているので、TermEnumを順番に調べていく方法はbits()を実装するのに効率がよい。 しかし今回のテーマでは、調べる属性はX座標とY座標というように2つにまたがってしまっている。そのため、TermEnumをX座標(またはY座標)で絞ってもY座標(またはX座標)を取得するためにDocumentを読まなければならなくなってしまい、効率が悪い。 しかし、もし座標を1つの属性でまとめて持てば、この方法でも可能である。たとえば、(5,9)という座標は1つの属性で"0509"というように持たせるのである。日本列島のように南北に長いところで地理検索を行うのであれば、YX(緯度経度)の順番でまとめて持てば効率がよいだろう。 この方針でできそうのなで実際にプログラムを書いてみる。すると、RadiusFilterは次のようになる。
そして、このFilterを使ったプログラムは次のようになる。
実行結果は次のようになり、この方法はうまくいくことがわかる(ただし、「自分の家から近い順にソート」をするために、実際にはSortの機能をさらに組み合わせることになる。以下の実行結果はSortを使っていないのでソートされていない)。
DistanceHitCollectorの実装を検討 最後に、HitCollectorを拡張したDistanceHitCollectorを実装することを検討してみる。 HitCollectorを引数に取るsearch()メソッドでは、Queryの結果セットの文書すべてに対してHitCollectorのcollect()メソッドが呼ばれるため、「半径○km以内」の文書を確実に取得できることはプログラムを書くまでもなくわかる。しかも好きなようにソートも可能である(したがって、HitCollectorを引数に取るsearch()メソッドにはSortを引数に取るバージョンがない)。 DistanceHitCollectorのプログラムは次のようになる。
ただし、DistanceHitCollectorのcollect()メソッドが呼ばれる回数を最小限に抑えるために、使う場合は次のように ConstantScoreRangeQueryと組み合わせる必要がある。これにより、DistanceHitCollectorは(最初の図の)斜線の部分だけを はじくようになる。
実行結果(上記printResults()による検索結果の表示)は次のようになり、指定した距離(5)を超えるすし屋は含まれず、「自分の家(5,5)から近い順」にソートされていることがわかる。
まとめ 以上検討の結果、「半径○km以内」の文書を(他の検索条件とともに)検索するには、次の2つの方法で実現できることがわかった。 1.RadiusFilterを実装 2.DistanceHitCollectorを実装 1.の方法はFilterであるため、CachingWrapperFilterを使ってRadiusFilterを再利用できる場面であれば2.よりも有利であろう。しかし、無数のインターネットのWebサイト訪問者に同じ座標を中心とする「半径5km以内のレストラン」の情報を繰り返し提供する場面は考えにくいのでFilterのメリットは働かないのではないか。 2.の方法は距離を計算するために、collect()メソッド内でDocumentを読まなければならない点が1.と比べて不利である。ただし、距離の計算が一度で済み(1.ではFilterとSortで2回必要)、ソートまでできてしまう点と、Documentを読んでいるのでキャッシュできる点が魅力的である。検索結果の表示のことまで考えれば、円の中に含まれるレストランが数十軒のオーダーなら2.の方が速いかもしれない。 実際にどちらの方法を取るかは、案件ごとに計測するのが望ましい。考慮するパラメータには、次のようなものがあげられる。
|
+ Solrによるブログ内検索
+ PROFILE
+ LINKS
+ Lucene&Solrデモ
+ ThinkIT記事
+ RECOMMEND
+ RECOMMEND
Lucene in Action (JUGEMレビュー »)
Erik Hatcher,Otis Gospodnetic,Mike McCandless FastVectorHighlighterについて解説記事を寄稿しました。
+ RECOMMEND
+ SELECTED ENTRIES
+ RECENT COMMENTS
+ RECENT TRACKBACK
+ CATEGORIES
+ ARCHIVES
+ MOBILE
+ SPONSORED LINKS
|
(C) 2024 ブログ JUGEM Some Rights Reserved.
|
PAGE TOP |