我提出了解决方案,以编程方式创建查询以使用此代码搜索带有通配符的短语:
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", "ane karenjine", ...(克罗地亚语语法不同)。
这个查询非常慢,我没有等待足够长的时间来获得结果(超过 1 小时),有时会导致 GC overhead limit exceeded 异常。 这种行为在某种程度上是意料之中的,因为“an*”本身匹配大量文档。我知道我可以在 30-40 秒内查询“an? karanjin*”,给出结果(更快但仍然很慢)。
这就是我困惑的地方。 如果我只查询“karenjin*”,它会在 1 秒内给出结果。因此,我尝试使用 WildcardQuery 和 QueryWrapperFilter 查询“an* karenjin*”并使用过滤器“karenjin*”。而且它仍然是 Not Acceptable 慢(我在它返回任何东西之前杀死了进程)。
文档说 Filter 减少了 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*"));
速度很快,但是使用上面描述的Filter还是很慢?
最佳答案
是的,通配符可能会影响性能,尤其是当它们匹配很多术语时,但您所描述的情况确实令人惊讶。很难确定为什么会发生这种情况,但可以尝试一下。
我假设:
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));
关于带通配符的 Lucene 短语查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26101351/