带通配符的 Lucene 短语查询

标签 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 将在已创建的术语之间执行集合交集。

例子: 假设我想查询短语“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/

相关文章:

field - Elasticsearch :如何在跨字段多重匹配搜索中获得每个字段的精确匹配?

Java:匹配字符串中的短语

nlp - 如何在 Stanford CoreNLP 中获取短语标签?

java - Hibernate + Lucene - 通配符搜索返回空结果

java - html文件的lucene索引

linux - solr 性能随 ram 大小的变化

SQL查询性能统计信息多次返回

java - 如何将整个本地硬盘索引到 Apache Solr?

solr - 为什么必须在长查询字符串中的每个数字之间添加 OR?

sql - 分区表上的 Postgres 查询比非分区表慢 2 倍