全文検索エンジンLuceneのメモです。((φo(´・ω・`*)
バージョンはこの前リリースされたばかりの2.4を使います。
2.4では、Hitsクラスなどが非推奨になりました。Luceneのマイルストーンとしては、次は2.9。そして3.0になります。3.0の時点で現在非推奨となっているメソッドは全て削除されてしまいます。ですので、今回は非推奨のメソッドやクラスは使わないようにしました。
CJKAnalyzerが検索の取りこぼしをするのでまずはAnalyzerを作ります。
package at.orz.tools; import java.io.Reader; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.ngram.NGramTokenizer; public class MyAnalyzer extends Analyzer { private int minGram; private int maxGram; public MyAnalyzer(int minGram, int maxGram) { this.minGram = minGram; this.maxGram = maxGram; } @Override public TokenStream tokenStream(String fieldName, Reader reader) { return new NGramTokenizer(reader, minGram, maxGram); } }
INDEXの作成をします。
IndexWriter out = new IndexWriter("lucene-idx", new MyAnalyzer(1,3), true, MaxFieldLength.UNLIMITED); Document doc = new Document(); doc.add(new Field("msg", "新機能aabbccを使う", Store.YES, Index.ANALYZED)); out.addDocument(doc); out.close();
"lucene-index"ディレクトリにMyAnalyzer(1, 3)なので、1文字~3文字のNGramのIndexを新規作成します。
Documentの"msg"フィールドに適当に文字を入れて、Store.YESなので実体も入れます。Index.ANALYZEDなので解析した状態で値をつっこみます。ここら辺の違いは、実際に作成してツールでIndexの中身を見るのがいいと思います。(現時点でLucene2.4に対応しているツールがあるのかは不明ですが・・・。少なくともLukeは対応していませんでした)
検索をしてみる。
IndexSearcher s = new IndexSearcher("lucene-idx"); Query q = new QueryParser("msg", new MyAnalyzer(1,1)).parse("aa"); TopDocs td = s.search(q, 2); System.out.println(td.totalHits); System.out.println(s.doc(td.scoreDocs[0].doc));
検索する時は、MyAnalyzer(1,1) (Uni-gram)にします。これを1,3にしてしまうと取りこぼしが発生します。というのも、QueryParserがTokenの位置を使わないでPhraseQueryを作っているのが原因らしいです。(ソース見てないので知りませんが・・)
s.searchの第2引数は、先頭の何件を取得するかです。2なので、td.scoreDocs配列は最大2になります。totalHitsは何件ヒットしたか。100件ヒットしたら100が返ります。(ただし、2を指定しているのでデータは2件分のみ)
td.scoreDocs[x]がScoreDocクラスを返し、そのdocフィールドが何番のDocumentかを示しています。
なのでIndexSearcherからそのDocumentを取得する。
DBと連携をする時は、二重に文章データを持ちたくないのであれば、Sotre.NOで作成すればINDEXのみ作成する(という認識だけど、試してはいない)。フィールドにDBレコードのPKを入れておけば参照はできる。そしてRPCにしておけばINDEXデータを一元管理できるので、マルチプロセスで動作するアプリ側でデータの整合性を気にする必要はなくなるヽ( `・ω・)ノ
匿名
MyAnalyzer(1,1) で検索するなら、
インデクシングもMyAnalyzer(1, 3)ではなく、MyAnalyzer(1, 1)
でした方がインデックスのサイズを小さくできます。
なぜなら、MyAnalyzer(1,1) では
bi-gramや、tri-gramは一切使われないためです。
タムタム
あ、確かにそうですね!!
ありがとうございます。
個人的にはBigramでインデックスを生成して、ミスヒット無しにしたいんですけどSennaは上手い事やってるのにLuceneも出来るといいなぁと思いました。
まぁ自分で書けばいいんですが。