我想出了使用以下代码以编程方式创建查询以搜索带有通配符的短语的解决方案:
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 开销限制超出异常。这种行为在某种程度上是意料之中的,因为“an*”本身匹配大量文档。我知道我可以在 30-40 秒(更快但仍然很慢)内查询“an? karanjin*”。
这就是我感到困惑的地方。如果我只查询“karenjin*”,它会在 1 秒内给出结果。因此,我尝试使用 WildcardQuery 和 QueryWrapperFilter 查询“an* karenjin*”并使用过滤器“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);
好的,我的问题是什么?
怎么查询:
Query query = new WildcardQuery(new Term("text", "karanjin*"));
很快,但使用上述过滤器仍然很慢?