cmecab-javaとLuceneでMoreLikeThis


類似検索やりたくて試したんだけどなんかうまくいかねーです。 Lucene詳しいかた、教えてくだしあ><

2009/10/07: 解決したので後ろに追記しました。

package hoge;
import java.io.*;
import net.moraleboost.lucene.analysis.ja.StandardMeCabAnalyzer;
import org.apache.lucene.*;

public class MeCabTest {
    public static final String DIC_ENCODING = System.getProperty("net.moraleboost.mecab.encoding");
    private static StandardMeCabAnalyzer analyzer = null;
    private static FSDirectory directory = null;
    private IndexWriter writer  = null;
    private static final String DIR = "/path/to/index";

    public void setUp() throws Exception {
        writer  = new IndexWriter(directory, analyzer, new MaxFieldLength(4096));
        Document doc = new Document();
        addField(doc, "text", "ほげほげ");
        ....
        ....
        writer.commit();
        writer.optimize();
        writer.close();
    }
    private void addField(Document doc, String name, String value) throws Exception {
        Field field = new Field(name, value, Store.YES, Field.Index.ANALYZED, Field.TermVector.YES);
        doc.add(field);
        writer.addDocument(doc);
    }

    public static void main(String[] args) {
        MeCabTest t = new MeCabTest();
        IndexSearcher searcher = null;
        IndexReader reader = null;
        analyzer = new StandardMeCabAnalyzer(DIC_ENCODING, "");
        directory = FSDirectory.open(new File(DIR));
        t.setUp();
        reader = IndexReader.open(DIR, false);
        searcher = new IndexSearcher(directory, true);
        QueryParser parser = new QueryParser("text", analyzer);
        parser.setDefaultOperator(QueryParser.Operator.OR);
        MoreLikeThis mlt = new MoreLikeThis(reader);
        mlt.setAnalyzer(analyzer);
        mlt.setMinTermFreq(1);
        mlt.setFieldNames(new String[]{"text"});
        Query query = mlt.like(new ByteArrayInputStream("ほげ".getBytes()));
        TopDocs topDocs = searcher.search(query, 100);
        if (topDocs.totalHits > 0) {
            for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
                Document doc = searcher.doc(scoreDoc.doc);
                System.out.println(doc.get("text"));
            }
        }
    }
}

try〜catchとか省略。addField(…);のところ、実際にはニュースサイトから本文引っ張ってきて、いくつかいれた。7個くらい適当に選んで、3個くらいは同じニュース(台風18号関連)を違うサイトから引用した。

MoreLikeThis#like()には、台風18号関連のさらに別のニュースを入れてみたが、topDocs.totalHitsがindexに入れたニュース数と同数、つまり全部ヒットになっちゃった。しかも1件目に入れたやつしか表示されない…なんのこっちゃい??

明日以降もうちょっと調べて、わかったら追記しよう…

[追記] すげーポカやってるし…。単純にDocumentクラスを使い回しちゃダメってことみたい。そりゃそうだよね、別のDocumentとして扱いたいんだから、インスタンスも別だわな。というわけでaddFieldメソッドにDocument渡すのやめて、メソッド内でDocumentクラスのインスタンスを作るようにしたら、上手く動くようになりました。