Elasticsearch不完全入门指北系列(四):组合搜索

1,288

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

本篇内容我们来演示一下 Elasticsearch 复杂搜索语句写法。拿 sql 语句来说,当我们的查询需求需要很多条件进行查询时,就得用 and 或者 or 拼接多个条件进行查询,有时候还需要带上排序条件以及分页参数。Elasticsearch 的搜索也是很类似的,当要使用多个条件进行搜索时,就也得拼接多个条件组成一个搜索语句(请求),我们可以称之为组合搜索。其中用来连接多个查询条件的便是 BoolQueryBuilder 类。

1 来认识一下各种查询(Query)

全文搜索

DSL Search QueryJava 类名使用方法解释说明
MatchMatchQueryBuilderQueryBuilders.matchQuery()匹配查询 是执行全文搜索的标准查询,包括用于模糊匹配的选项。返回与提供的文本、数字、日期或布尔值匹配的文档。 在匹配之前会分析提供的查询条件。
Match PhraseMatchPhraseQueryBuilderQueryBuilders.matchPhraseQuery()match_phrase 查询分析文本并根据分析的文本创建短语查询。
Match Phrase PrefixMatchPhrasePrefixQueryBuilderQueryBuilders.matchPhrasePrefixQuery()以与提供的相同的顺序返回包含提供的文本的单词的文档。 提供的文本的最后一个 term 被视为前缀,匹配以该术语开头的任何单词。
Query StringQueryStringQueryBuilderQueryBuilders.queryStringQuery()可以使用 query_string 查询来创建包含通配符、跨多个字段的搜索等的复杂搜索。 虽然用途广泛,但查询是严格的,如果查询字符串包含任何无效语法,则返回错误。

词语级别的搜索

DSL Search QueryJava 类名使用方法解释说明
TermTermQueryBuilderQueryBuilders.termQuery()返回在提供的字段中包含确切词语的文档。可以使用词语查询根据价格、产品 ID 或用户名等精确值查找文档。
TermsTermsQueryBuilderQueryBuilders.termsQuery()多个词语查询。
RangeRangeQueryBuilderQueryBuilders.rangeQuery()返回包含提供范围内的词语的文档。gtelte
ExistsExistsQueryBuilderQueryBuilders.existsQuery()返回包含字段索引值的文档。即文档中是否存在该 field。
WildcardWildcardQueryBuilderQueryBuilders.wildcardQuery()返回包含匹配通配符模式词语的文档。通配符运算符是匹配一个或多个字符的占位符。 例如,* 通配符运算符匹配零个或多个字符。 您可以将通配符运算符与其他字符组合以创建通配符模式。
IdsIdsQueryBuilderQueryBuilders.idsQuery()根据文档的 ID 返回文档。 此查询使用存储在 _id 字段中的文档 ID。

组合查询

DSL Search QueryJava 类名使用方法解释说明
BoolBoolQueryBuilderQueryBuilders.boolQuery()匹配与其他查询的布尔组合匹配的文档的查询。 bool 查询映射到 Lucene BooleanQuery。 它是使用一个或多个布尔子句构建的,每个子句都有一个查询类型的出现
关键字描述
must子句(查询)必须出现在匹配的文档中,并将有助于得分。
filter子句(查询)必须出现在匹配的文档中。 然而,与 must 不同的是,查询的分数将被忽略。 过滤器子句在过滤器上下文中执行,这意味着忽略评分并考虑缓存子句。
should子句(查询)应该出现在匹配的文档中。
must_not子句(查询)不得出现在匹配的文档中。 子句在过滤器上下文中执行,这意味着忽略评分并考虑将子句用于缓存。 由于评分被忽略,所有文档的评分为 0。

连接查询

DSL Search QueryJava 类名使用方法解释说明
NestedNestedQueryBuilderQueryBuilders.nestedQuery()包装另一个查询以搜索嵌套字段。嵌套查询搜索嵌套字段对象,就好像它们被索引为单独的文档一样。 如果对象与搜索匹配,则嵌套查询将返回根父文档。要使用嵌套查询,索引必须包含嵌套字段映射(mapping)。

常规的多条件搜索应该都会包含在上面表格中所列取的 query 中,具体的使用方法大家可以自行查阅文档

2. 组合查询

当我们要搜索数据时,一般我们需要构建搜索请求,然后通过 ES 的 Java 客户端调用搜索 API 进行搜索数据。再对搜索得到的结果进行解析和处理。

官方文档方法:

 // 创建一个搜索请求
SearchRequest searchRequest = new SearchRequest();
// 大多数搜索参数都添加到 SearchSourceBuilder。 它为进入搜索请求正文的所有内容提供设置。  
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); 
// 把匹配所有文档查询到 SearchSourceBuilder
searchSourceBuilder.query(QueryBuilders.matchAllQuery()); 
// 把 SearchSourceBuilder 添加到 SearchRequest.
searchRequest.source(searchSourceBuilder); 
​
// 同步返回查询结果
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
​
// 异步返回查询结果
ActionListener<SearchResponse> listener = new ActionListener<SearchResponse>() {
    @Override
    public void onResponse(SearchResponse searchResponse) {
        // 请求成功返回的结果
    }
​
    @Override
    public void onFailure(Exception e) {
        // 请求失败
    }
};
client.searchAsync(searchRequest, RequestOptions.DEFAULT, listener);
​

当我们使用多个条件进行搜索时,就需要使用 BoolQueryBuilder 将各个条件组合起来,其中有 mustmustNotshould 几个关键的链接词,分别表示,文档中必须出现匹配的内容,必须不出现匹配的内容,应该出现匹配的内容,对比我们常用的 sql 语法,可以简单类比为 andand !=or 这几个关键字。

比如,我们创建了一个 /book 索引,然后往索引里添加几个文档,接着,我们要开始搜索一部分图书,比如,名称中包含“编程”的,我们可以通过下面的方式构建一个DSL查询的方式:

POST /book/_search
​
{
    "query": {
        "bool": {
            "must": [
                {
                    "bool": {
                        "should": [
                            {
                                "match": {
                                    "bookName": {
                                        "query": "编程"
                                    }
                                }
                            },
                            {
                                "match_phrase": {
                                    "bookName": {
                                        "query": "编程"
                                    }
                                }
                            }
                        ]
                    }
                }
            ]
        }
    },
    "sort": [
        {
            "id": {
                "order": "asc"
            }
        }
    ]
}

意思就是说,我们查询的时候可以匹配出含有“编程”、“编”、“程”或者含有“编程”短语的文档,最后的搜索结果按照 id 生序排列。那么我们就可以按照这个逻辑构建一个Java的“DSL”语句:

// 创建一个 BoolQueryBuilder
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 模糊搜索,使用should连接两个匹配
BoolQueryBuilder bookNameQuery = QueryBuilders.boolQuery();
bookNameQuery.should(QueryBuilders.matchQuery("bookName", searchReq.getBookName()));
bookNameQuery.should(QueryBuilders.matchPhraseQuery("bookName", searchReq.getBookName()));
// 使用 must 组合这两个
boolQuery.must(bookNameQuery);
​
// 创建一个搜索请求,设置索引名称
SearchRequest searchRequest = new SearchRequest("book");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 设置组合索引到 SearchSourceBuilder
searchSourceBuilder.query(boolQuery);
// 默认按 id 正序排序
searchSourceBuilder.sort("id", SortOrder.ASC);
// 将 SearchSourceBuilder 添加到 SearchRquest
searchRequest.source(searchSourceBuilder);
// 执行搜索
SearchResponse searchResponse = client.search(searchRequest, COMMON_OPTIONS);

其他查询条件大家也可以自行进行设置和试验,像是 TermQueryBuilderWildcardQueryBuilderNestedQueryBuilder等等。除此之外,Search API还有一些其他的设置,比如高亮,搜索等等,后面的文章我们会逐一介绍到。

总结

本文我们先简单介绍了一下 Java Rest Client 的 Search API,当我们需要根据多个条件来搜索的时候,应该怎么把各个条件组合起来,以及哪些文档字段是需要使用精确匹配还是模糊匹配,还是相关度匹配。对于排序,我们是可以设置多个排序条件的,SearchSourceBuildersort 方法是可以设置多次的。后面的文章会继续对于各种搜索做介绍和实例演示,大家可以先参考一下链接中的项目,如有问题,欢迎叨扰。

链接