関口宏司のLuceneブログ

OSS検索ライブラリのLuceneおよびそのサブプロジェクト(Solr/Tika/Mahoutなど)について
<< 郵便番号のインクリメンタルサーチのデモ | main | Luceneアプリケーションの開発環境の紹介 >>
はじめてのLucene全文検索プログラム
子供のころ、日曜日の夕方になるとしばしば不安感に襲われたことを思い出す。
それは「月曜日からまた学校が始まる」ということのほかに、あるテレビ番組も原因のひとつだったように思う。
それは「サザエさん」である。
思えばフシギな番組であった(今でも続いているが・・・)。何がフシギかというと、カツオとタラちゃん、ワカメとタラちゃんは兄弟(姉弟)ではない、ということだ。
これは子供心にとても理解しがたい事実であった。あの三人はどうみても兄弟であろう。しかしどうだ。実際はサザエ・カツオ・ワカメが兄弟姉妹の関係なのだ。
カツオとワカメは小学生のようだが、彼らの姉は結婚して子供もいるのである。サザエさんとその下の二人はとても歳が離れているのだ。サザエさんが生まれた後、十年ほど経ってからカツオとワカメが相次いで生まれた計算だ。
そのとき波平と舟の間に何があったのだろう。小学生のころはそんなことは考えなかったと思うが、今はとても気になる。
・・・そういうことなので、タラちゃんにとってカツオはお兄さんではなく、ワカメはお姉さんではなく、おじさん、おばさんであり、カツオやワカメから見てタラちゃんは弟ではなく甥、ということを子供のころに学習した。
また、作者の「長谷川町子」という名前、子供のころはこの苗字も最初はなんと読むのかさっぱりわからなかった(ほかにも「東海林さだお」もよくわからなかった)。そんなことがあり、「サザエさん」は子供のころの教材であった。
もっとも、無垢な子供のころはなんでも教材になるものだ。

そういうわけで、Luceneの入門用プログラムにもサザエさんをコンテンツに選択する。サザエさんのコンテンツは、次のようなものである:



private static final String[] contents = {
"カツオはサザエの弟", "サザエはワカメの姉", "ワカメはカツオの妹",
"カツオは長男", "サザエは長女", "ワカメは次女",
"マスオはサザエの夫", "波平は舟の夫", "タラちゃんのパパはマスオ",
"サザエとマスオは夫婦", "波平はタラちゃんの祖父", "舟はカツオの母",
"マスオはカツオの義兄", "カツオはタラちゃんの叔父", "舟はワカメの母"
};



上記のように、contentsという文字列配列に、サザエさんファミリーの人間関係を表した短い文を設定している。ここではこのひとつひとつがドキュメントである。
ドキュメントは、全文検索の対象となるオブジェクトで、この例で言えばひとつの要素contents[i]がひとつのHTMLファイルなりPDFファイルの代わりである。
入門用プログラムなので、全文検索対象となるドキュメントは、HTMLやPDFではなくこのような簡単な文字列にしよう、ということだ。

上記のようなコンテンツに対し、検索質問の文字列も次のような文字列配列で用意する:



private static final String[] queries = {
"カツオ", "ワカメ", "サザエ ワカメ", "サザエ AND ワカメ", "三河屋さん"
};



プログラムでは、contentsのドキュメントでインデックスを作成し、次に検索質問語であるqueriesの分だけforループをまわし、インデックスを検索する。
ではプログラムを紹介する前に、実行結果を先に見ておこう。それは、次のようになる:



***** query = "カツオ" 6件ヒットしました。 *****
カツオは長男
カツオはサザエの弟
ワカメはカツオの妹
舟はカツオの母
マスオはカツオの義兄
カツオはタラちゃんの叔父

***** query = "ワカメ" 4件ヒットしました。 *****
ワカメは次女
サザエはワカメの姉
ワカメはカツオの妹
舟はワカメの母

***** query = "サザエ ワカメ" 8件ヒットしました。 *****
サザエはワカメの姉
ワカメは次女
ワカメはカツオの妹
舟はワカメの母
サザエは長女
カツオはサザエの弟
マスオはサザエの夫
サザエとマスオは夫婦

***** query = "サザエ AND ワカメ" 1件ヒットしました。 *****
サザエはワカメの姉

***** query = "三河屋さん" 0件ヒットしました。 *****



実行結果は、queries[0]の"カツオ"を検索したときにcontents[]のドキュメントのうち6件がヒットし、その内容を表示している。以下、queries[1]の検索結果の表示、queries[2]の検索結果の表示、・・・と続く。

プログラムの全体は、次のようである:



import java.io.IOException;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.ja.JapaneseAnalyzer;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.Query;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.queryParser.ParseException;

public class HelloLucene {

private static final String FIELD_CONTENT = "content";
private static final Directory directory = new RAMDirectory();
private static final Analyzer analyzer = new JapaneseAnalyzer();
private static final QueryParser qp = new QueryParser( FIELD_CONTENT, analyzer );

private static final String[] contents = {
"カツオはサザエの弟", "サザエはワカメの姉", "ワカメはカツオの妹",
"カツオは長男", "サザエは長女", "ワカメは次女",
"マスオはサザエの夫", "波平は舟の夫", "タラちゃんのパパはマスオ",
"サザエとマスオは夫婦", "波平はタラちゃんの祖父", "舟はカツオの母",
"マスオはカツオの義兄", "カツオはタラちゃんの叔父", "舟はワカメの母"
};
private static final String[] queries = {
"カツオ", "ワカメ", "サザエ ワカメ", "サザエ AND ワカメ", "三河屋さん"
};

public static void main( String[] args ) throws IOException, ParseException {
makeIndex();
for( int i = 0; i < queries.length; i++ )
searchIndex( queries[i] );
if( directory != null )
directory.close();
}

private static void makeIndex() throws IOException {
IndexWriter writer = new IndexWriter( directory, analyzer, true );
for( int i = 0; i < contents.length; i++ ){
Document doc = new Document();
doc.add( new Field( FIELD_CONTENT, contents[i], Field.Store.YES, Field.Index.TOKENIZED ) );
writer.addDocument( doc );
}
writer.close();
}

private static void searchIndex( final String q ) throws IOException, ParseException {
IndexSearcher searcher = new IndexSearcher( directory );
Query query = qp.parse( q );
Hits hits = searcher.search( query );
int length = hits.length();
System.out.print( "¥n***** query = " + "¥"" + q + "¥" " );
System.out.println( Integer.toString( length ) + "件ヒットしました。 *****" );
for( int i = 0; i < length; i++ ){
Document doc = hits.doc( i );
System.out.println( "¥t" + doc.get( FIELD_CONTENT ) );
}
searcher.close();
}
}



プログラムのメインストリーム(main()の中)は、最初にmakeIndex()を呼んでインデックスを作成し、次にqueries[]の大きさだけforループをまわしてqueries[]要素を引数にしてsearchIndex()を呼んで検索を実行している。

makeIndex()では、IndexWriterのインスタンスを作成し、addDocument()でDocumentをインデックスに登録している。Documentはcontents[]の要素である。
FieldはDocumentを構成する要素で、ここでは次のようにDocumentにadd()している:



Document doc = new Document();
doc.add( new Field( FIELD_CONTENT, contents[i], Field.Store.YES, Field.Index.TOKENIZED ) );



ここで、FIELD_CONTENTはフィールド名、contents[i]はフィールドの文字列である。Field.Store.YESはフィールドの文字列をインデックスに登録することを示し、Field.Index.TOKENIZEDはフィールド文字列をアナライザーで分析しながら単語を索引付けすることを示している。

searchIndex()では、IndexSearcherインスタンスを作成し、search()メソッドで全文検索を実行している。検索結果はHitsオブジェクトで返るので、ヒット件数(hits.length())とその内容を表示している。
| 関口宏司 | Hello, Lucene (入門者向けプログラム例) | 11:32 | comments(0) | trackbacks(1) |









http://lucene.jugem.jp/trackback/67
java で全文検索 lucene の使い方?
lucene 基本的には解凍するだけです。 コンパイル時も、実行時も 、 lucene-core-2.0.0.jar,lucene-demos-2.0.0.jar,lucene-ja.jarにパスが通っている必要があるようです。 lucene/src/org/apache/lucene/demo/IndexJFilesでは、 ソース中で St
| 研究開発 | 2009/02/25 6:33 PM |
+ Solrによるブログ内検索
+ PROFILE
    123
45678910
11121314151617
18192021222324
252627282930 
<< June 2017 >>
+ LINKS
検索エンジン製品 - 比較のポイント
商用検索エンジンを購入した企業担当者は読まないでください。ショックを受けますから・・・
>>製品比較 10のポイント
+ Lucene&Solrデモ
+ ThinkIT記事
+ RECOMMEND
Apache Solr入門 ―オープンソース全文検索エンジン
Apache Solr入門 ―オープンソース全文検索エンジン (JUGEMレビュー »)
関口 宏司,三部 靖夫,武田 光平,中野 猛,大谷 純
+ RECOMMEND
Lucene in Action
Lucene in Action (JUGEMレビュー »)
Erik Hatcher,Otis Gospodnetic,Mike McCandless
FastVectorHighlighterについて解説記事を寄稿しました。
+ RECOMMEND
+ SELECTED ENTRIES
+ RECENT COMMENTS
+ RECENT TRACKBACK
+ CATEGORIES
+ ARCHIVES
+ MOBILE
qrcode
+ SPONSORED LINKS