用通配符查看Lucene短语

时间:2014-09-29 13:41:29

标签: lucene query-performance phrase

我提出了使用此代码编程创建查询以使用通配符搜索短语的解决方案:

public static Query createPhraseQuery(String[] phraseWords, String field) {
    SpanQuery[] queryParts = new SpanQuery[phraseWords.length];
    for (int i = 0; i < phraseWords.length; i++) {
        WildcardQuery wildQuery = new WildcardQuery(new Term(field, phraseWords[i]));
        queryParts[i] = new SpanMultiTermQueryWrapper<WildcardQuery>(wildQuery);
    }
    return new SpanNearQuery(queryParts,       //words
                             0,                //max distance
                             true              //exact order
    );
}

创建示例并调用toString()方法将输出:

String[] phraseWords = new String[]{"foo*", "b*r"};
Query phraseQuery = createPhraseQuery(phraseWords, "text");
System.out.println(phraseQuery.toString());

输出:

spanNear([SpanMultiTermQueryWrapper(text:foo*), SpanMultiTermQueryWrapper(text:b*r)], 0, true)

对于大多数情况来说,哪个效果很好,而且速度足够快。例如,如果我创建此类查询并使用它进行搜索,它将输出所需的结果,例如:

Sentence with foo bar.
Foolies beer drinkers.
...

而不是像:

Bar fooes.
Foo has bar.

我已经提到在大多数情况下查询工作足够快。目前我有一个大小为aprox的索引。 200GB,平均搜索时间在0.1到3秒之间。取决于许多因素,例如:缓存,短语中匹配单个单词的文档子集的大小,因为lucene将执行已建立的术语之间的集合交集。

实施例: 让我想要查询短语&#34; an * karenjin *&#34; (我将分成[&#34; an *&#34;,&#34; karenjin *&#34;]而不是使用createPhraseQuery方法创建查询)我希望它匹配包含以下内容的句子:&#34; ana karenjina&#34;,&#34; ani karenjinoj&#34;,&#34; an karenjine&#34;,...(不同的案例由于克罗地亚语法)。

这个查询非常慢,我没有等待足够长的时间来获得结果(超过1小时),有时会导致GC开销限制超出异常。 这种行为有点预期,因为&#34; an *&#34;本身匹配大量的文件。我知道我可以查询&#34;一个?水黄皮次素*&#34;哪个给予者产生30-40秒(更快但仍然慢)。

这是我感到困惑的地方。 如果我只查询&#34; karenjin *&#34;它以1秒的速度给出结果。因此,我试图查询&#34; an * karenjin *&#34;并使用过滤器&#34; karenjin *&#34;使用WildcardQuery和QueryWrapperFilter。它仍然是不可接受的缓慢(我在它返回任何东西之前杀死了进程)。

文档说过滤器会减少Query的搜索空间。所以我尝试使用过滤器:

Filter filter = new QueryWrapperFilter(new WildcardQuery(new Term("text", "karanjin*")));

并查询:

Query query = createPhraseQuery(new String[]{"an*", "karenjin*"}, "text");

比搜索,(经过多次热身查询后):

Sort sort = new Sort(new SortField("insertTime", SortField.Type.STRING, true));
TopDocs docs = searcher.search(query, filter, 100, sort);

好的,我的问题是什么?

怎么回事:

 Query query = new WildcardQuery(new Term("text", "karanjin*"));

速度很快,但使用上述过滤器仍然很慢?

1 个答案:

答案 0 :(得分:1)

是的,通配符可能会影响性能,特别是如果它们匹配很多术语,但你所描述的确如此令人惊讶。很难确定为什么会发生这种情况,但需要尝试。

我假设:

Query query = new WildcardQuery(new Term("text", "an*"));

如上所述,它自己的表现非常糟糕。由于您要查找的通配符都是前缀样式查询,因此最好使用PrefixQuery

Query query = new PrefixQuery(new Term("text", "an"));

虽然我不认为如果有的话会产生很大的影响。可能会有所不同的是改变你的重写方法。您可以尝试将查询重写为Terms的数量限制为:

Query query = new PrefixQuery(new Term("text", "an"));
//or
//Query query = new WildcardQuery(new Term("text", "an*"));
query.setRewriteMethod(new MultiTermQuery.RewriteMethod.TopTermsRewrite(10));