带通配符的Lucene短语查询
我提出了一个解决方案,使用以下代码以编程方式创建查询来搜索带有通配符的短语:带通配符的Lucene短语查询,lucene,query-performance,phrase,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++) { WildcardQ
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
);
}
产出:
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”,“ane karenjine”。。。(由于克罗地亚语语法的不同情况)
这个查询非常慢,我没有等待足够长的时间来获得结果(超过1小时),有时会导致超出GC开销限制的异常。
这种行为是意料之中的,因为“an*”本身匹配大量文档。我知道我可以查询“an?karanjin*”,给出者的结果在30-40秒(更快,但仍然很慢)
这就是我困惑的地方。
如果我只查询“karenjin*”,它会在1秒内给出结果。因此,我尝试查询“an*karenjin*”并使用通配符查询和QueryWrapperFilter使用过滤器“karenjin*”。而且它仍然是不可接受的慢(我在它返回任何东西之前杀死了进程)
文档表明,过滤器减少了查询的搜索空间。所以我尝试使用过滤器:
Filter filter = new QueryWrapperFilter(new WildcardQuery(new Term("text", "karanjin*")));
和查询:
Query query = createPhraseQuery(new String[]{"an*", "karenjin*"}, "text");
然后进行搜索(在几个预热查询之后):
好的,我的问题是什么?
为什么是查询:
Query query = new WildcardQuery(new Term("text", "karanjin*"));
是的,通配符可能会影响性能,特别是当它们匹配很多术语时,但您所描述的似乎确实令人惊讶。很难说清楚为什么会发生这种情况,但这只是一次尝试 我假设:
Query Query=新的通配符查询(新术语(“文本”、“an*”));
就其自身而言,正如所描述的,它的表现非常糟糕。由于您要查找的通配符都是前缀样式的查询,因此最好使用PrefixQuery
Query Query=新前缀Query(新术语(“文本”、“安”));
虽然我不认为这会有什么不同,如果有的话。改变重写方法可能会使情况有所不同。您可以尝试限制将查询重写为以下内容的术语的数量:
Query Query=新前缀Query(新术语(“文本”、“安”));
//或
//查询查询=新的通配符查询(新术语(“文本”、“an*”));
setRewriteMethod(新的MultiTermQuery.RewriteMethod.TopTermsRewrite(10));
感谢您的建议,我将尝试限制条款的数量,并查看其性能。我希望它会快得多。但结果可能不完整。这是时间和结果之间的折衷。我会试试看。根据《Lucene in action》一书,如果通配符以*结尾,则会在内部识别并优化为PrefixQuery,如果没有通配符,则会优化为TermQuery。我相信这是正确的,但我更希望这种逻辑存在于解析中,而我在那里没有看到它。不过,这可能是重写本身的一部分。在Lucene的当前版本中,PrefixQuery和WildcardQuery都扩展了AutomatonQuery,它们生成的自动机是相同的,因此选择其中一个没有明显的好处。
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*"));