2014-09-29 97 views
2

我想出解决programmaticlly创建查询来搜索短语与使用此代码通配符:Lucene的短语查询与通配符

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将在创建的术语之间执行集合交集。让我想要查询短语“an * karenjin *”(我将分解为[“an *”,“karenjin *”]并且使用createPhraseQuery方法创建查询),我希望它匹配包含以下内容的句子:“ana karenjina”,“ani karenjinoj”,“an karenjine”,...(由于克罗地亚语语法不同的情况)。

这个查询是非常慢,我没有等待足够长的时间来获得结果(超过1小时),有时会导致GC开销限制超出异常。 由于“an *”本身匹配大量文档,因此会出现此行为。我知道我可以查询“an?karanjin *”,它可以在30-40秒内获得结果(更快但仍然很慢)。

这是我困惑的地方。 如果我只是查询“karenjin *”,它会在1秒内给出结果。因此,我尝试使用WildcardQuery和QueryWrapperFilter查询“an * karenjin *”并使用Filter“karenjin *”。而且它仍然是不能接受的缓慢(我在任何返回任何东西之前杀死了进程)。

文档说过滤器减少了查询的搜索空间。于是,我就用过滤:

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); 

OK,什么是我的问题?

如何而来的是quering:

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

是快,但使用上述仍然缓慢过滤器?

回答

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)); 
+0

谢谢你的建议,我会尽量限制条款的数量,看看它是如何执行。我期待它会快很多。但结果可能不完整。它是在时间和结果之间权衡的。 – 2014-09-30 00:30:23

+0

我会试一试。根据Lucene的书中的内容,如果没有通配符,WildcardQuery将在内部识别并优化为PrefixQuery(如果以*结尾,或者甚至包含TermQuery)。 – 2014-09-30 00:37:19

+0

我认为这是正确的,但我更希望逻辑生活在解析中,而我没有看到它。尽管可能是重写的一部分。 – femtoRgon 2014-09-30 01:27:16