ここ数日、Lucene ドキュメント検索プログラムに取り組んできましたが、今のところすべてが順調に進んでいます。Lucene.Net.Highlight.Highlighter
このクラスを使用して、検索結果に関連するスニペットを表示しようとしていますが、一貫して機能していません。ほとんどの場合、呼び出しHighlighter.GetBestFragments()
は期待どおりに実行されます (指定されたクエリ文字列を含む関連するテキスト スニペットが表示されます) が、空の文字列が返されることがあります。
入力を 3 回チェックしたところ、使用しているクエリ文字列が入力テキストに存在することを確認できましたが、ハイライターは勝手に空の文字列を返すことがあります。問題は再現可能です。空白のフラグメントが返されたドキュメントは、同じクエリを使用すると引き続き空白のフラグメントが返されますが、正当なフラグメントを含むドキュメントには引き続き正当なフラグメントが含まれます。
ただし、問題はドキュメント固有ではありません。ドキュメントに対して有効なフラグメントを返すクエリもあれば、同じドキュメントに対して空の文字列を返すクエリもあります。また、この問題はアナライザーに関連していないようです。StandardAnalyzer
を使用しても、を使用しても問題が発生しますSnowballAnalyzer
。
何時間もぶらぶらした後、クエリ/ドキュメントで失敗するパターンと機能するパターンを見つけることができませんでした。これは、まったく同じクエリを使用して Lucene インデックスから特別にプルバックされたドキュメントで発生していることに注意してください。つまり、Searcher
は対象ドキュメントで関連するクエリ文字列を見つけることができますが、Highlighter
はそうではありません。
これは Lucene のバグですか? もしそうなら、どうすれば回避できますか?
私のコード:
private static SimpleHTMLFormatter _formatter = new SimpleHTMLFormatter("<b>", "</b>");
private static SimpleFragmenter _fragmenter = new SimpleFragmenter(50);
...
{
using (var searcher = new IndexSearcher(analyzerInfo.Directory, false))
{
QueryParser parser = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "Text", analyzerInfo.Analyzer);
parser.SetMultiTermRewriteMethod(MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE);
//build query
BooleanQuery booleanQuery = new BooleanQuery();
booleanQuery.Add(new TermQuery(new Term("PageNum", "0")), BooleanClause.Occur.MUST);
booleanQuery.Add(parser.Parse(searchQuery), BooleanClause.Occur.MUST);
Query query = booleanQuery.Rewrite(searcher.GetIndexReader());
//get results from query
ScoreDoc[] hits = searcher.Search(query, 50).ScoreDocs;
List<DVDoc> results = hits.Select(hit => MapLuceneDocumentToData(searcher.Doc(hit.Doc))).ToList();
//add relevant fragments to search results (shows WHY a certain result was chosen)
QueryScorer scorer = new QueryScorer(query);
Highlighter highlighter = new Highlighter(_formatter, scorer);
highlighter.SetTextFragmenter(_fragmenter);
foreach (DVDoc result in results)
{
TokenStream stream = analyzerInfo.Analyzer.TokenStream("Text", new StringReader(result.Text));
result.RelevantFragments = highlighter.GetBestFragments(stream, result.Text, 3, "...");
}
//clean up
analyzerInfo.Analyzer.Close();
searcher.Close();
return results;
}
}
(注:DVDoc
基本的には、見つかったドキュメントに関する情報を格納する単なる構造体です。このメソッドMapLuceneDocumentToData
は、LuceneDocument
をカスタムDVDoc
クラスに変換します。魔法はありません。)
そして、誰もが入力と出力の例が好きなので:
Lucene.NET バージョン 2.9.4g を使用しています。