为什么这个Lucene查询没有返回任何命中?

时间:2016-01-05 02:14:04

标签: java lucene lexical-analysis

我很久以前就提到了Lucene的一个错误,寻找同一个问题的答案。但是很长一段时间过去了,甚至分析仪的开发人员似乎也不愿意回答我的问题,所以我想我会把它扔到地板上,看看是否有其他人可以解释这里发生了什么。

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.ja.JapaneseAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.MultiFields;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.queryparser.flexible.standard.StandardQueryParser;
import org.apache.lucene.queryparser.flexible.standard.config.StandardQueryConfigHandler;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.BytesRef;

public class LuceneMissingTerms {
    public static void main(String[] args) throws Exception {
        try (Directory directory = new RAMDirectory()) {
            Analyzer analyser = new JapaneseAnalyzer();

            try (IndexWriter writer = new IndexWriter(directory, new IndexWriterConfig(analyser))) {
                Document document = new Document();
                document.add(new TextField("content", "blah blah commercial blah blah \u79CB\u8449\u539F blah blah", Field.Store.NO));
                writer.addDocument(document);
            }

            try (IndexReader multiReader = DirectoryReader.open(directory)) {
                for (LeafReaderContext leaf : multiReader.leaves()) {
                    LeafReader reader = leaf.reader();

                    Terms terms = MultiFields.getFields(reader).terms("content");
                    TermsEnum termsEnum = terms.iterator();
                    BytesRef text;
                    //noinspection NestedAssignment
                    while ((text = termsEnum.next()) != null) {
                        System.out.println("Term in index: " + text.utf8ToString());
                    }
                }

                StandardQueryParser queryParser = new StandardQueryParser(analyser);
            queryParser.setDefaultOperator(StandardQueryConfigHandler.Operator.AND);
                String queryString = "\"\u79CB\u8449\u539F\"";
                // quoted to work around strange behaviour of StandardQueryParser treating this as a boolean query.
                Query query = queryParser.parse(queryString, "content");
                System.out.println("Performing query: " + queryString);

                TopDocs topDocs = new IndexSearcher(multiReader).search(query, 10);
                System.out.println("Hits count: " + topDocs.totalHits);
            }
        }
    }
}

运行时,输出如下:

Term in index: blah
Term in index: commercial
Term in index: 秋葉原
Performing query: "秋葉原"
Hit count: 0

因此,我们在索引中有一个术语,其中对该术语的查询没有找到它。通常在这种情况下,您在索引时不使用与查询相同的分析器,但在上面的示例中,两者都使用相同的分析器对象。

所以我假设分析器中存在一个错误,并且它与该词周围的上下文有关,因为这是两种情况下唯一不同的东西,但我不清楚这是什么实际问题是或我将如何解决它。

另一方面,也许这是预期的行为?如果是这种情况,那么我也可以关闭我的机票,最终用户可能只是有点恼火,我们在这个问题上坐了几年才解决这个问题。

1 个答案:

答案 0 :(得分:3)

这对我来说当然看起来像是预期的行为。我根本不了解日语语言分析的细节,但是Kuromoji分析器具有分割多个术语的功能,包括一个词干分析器,并且考虑到词性的分析。嵌入在一堆英文文本中的一系列日文字符与分析器本身没有相同的含义,或者与日文全文中的含义相同。

StandardAnalyzer对于你出现的特定情况会很好。如果您的实际用例是英语文本中的偶然日语序列,那么您应该使用它。这意味着要合理地处理多种语言。

kuromoji分析器似乎在实际日文全文中运行良好。我尝试将Akihabara (秋葉原) Japanese Wikipedia page中的一些内容编入索引,并且无论是否在查询中使用引号,它都能很好地工作。特定于语言的分析器包含许多专门针对该语言的其他智能,但作为交换,他们无法处理像StandardAnalyzer这样的多种语言。

我怀疑这是真正的问题,测试用例有点过于天真。您的测试文档主要是英语,因此EnglishAnalyzerStandardAnalyzer可能比JapaneseAnalyzer更适合搜索它。