elasticSearch之java客户端详细使用:文档搜索API

文章目录

一、SearchRequest:搜索API

官方文档:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.10/java-rest-high-search.html

1、基本使用

java 复制代码
// 用于创建SearchRequest。如果不提供参数,该命令会遍历所有索引进行操作
SearchRequest searchRequest = new SearchRequest(); 
//大多数搜索参数都会被添加到SearchSourceBuilder中。该接口为搜索请求正文中所需的所有内容提供了相应的设置选项
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); 
//将一个match_all查询添加到SearchSourceBuilder中
searchSourceBuilder.query(QueryBuilders.matchAllQuery()); 
//将SearchSourceBuilder添加到SearchRequest中
searchRequest.source(searchSourceBuilder); 
java 复制代码
import org.elasticsearch.index.query.BoolQueryBuilder;
// 组合查询
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// must:必须满足(类似AND,影响评分)
boolQuery.must(QueryBuilders.matchQuery("name", "张三"));
// filter:必须满足(不影响评分,性能更好)
boolQuery.filter(QueryBuilders.rangeQuery("age").gte(20).lte(30));
// should:满足其一即可(类似OR)
boolQuery.should(QueryBuilders.termQuery("gender", "男"));

sourceBuilder.query(boolQuery);

2、参数:构造方法

java 复制代码
// 将请求限制在特定索引范围内
SearchRequest searchRequest = new SearchRequest("posts"); 

3、参数:routing

java 复制代码
// 设置路由参数,通常为用户id、订单id等分片路由
searchRequest.routing("routing"); 

4、参数:indicesOptions(不常用)

java 复制代码
// 设置IndicesOptions用于决定如何处理无法使用的索引,以及如何解析通配符表达式
searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen()); 

IndicesOptions 提供了多个预定义的静态方法,用于快速配置不同场景下的索引处理策略。这些方法的核心区别主要体现在两个维度:

1.是否严格处理不存在的索引strict 严格模式 vs lenient 宽松模式);

2.是否包含关闭的索引expandOpen 仅开放索引 vs expandClosed 包含关闭索引)。

以下是常用的 IndicesOptions 方法及含义:

1.strictExpandOpen()
行为
strict(严格模式):如果请求中指定的索引不存在,会直接抛出异常(如 IndexNotFoundException),不允许忽略不存在的索引。
expandOpen(仅展开开放索引):通配符(如 log*)仅匹配已打开(open)的索引,关闭的索引会被忽略。
适用场景:需要确保所有指定的索引必须存在,且只搜索开放索引的场景(如核心业务数据查询,不允许索引缺失)。

2.strictExpandClosed()
行为
strict(严格模式):索引不存在时抛出异常。
expandClosed(展开所有匹配索引):通配符匹配所有符合条件的索引,包括已关闭(closed)的索引(关闭的索引默认不参与搜索,但此处会被纳入匹配范围)。
适用场景:需要严格检查索引存在性,且允许搜索关闭索引的场景(如需要强制查询历史关闭的归档索引时)。

3.lenientExpandClosed()
行为
lenient(宽松模式):忽略不存在的索引,不抛出异常。
expandClosed(展开所有匹配索引):通配符匹配所有符合条件的索引(包括关闭的索引)。
适用场景:需要搜索可能包含关闭索引的动态索引集合(如按日期归档的日志索引,部分可能已关闭),且允许忽略不存在的索引。

  1. fromOptions(...)
    行为 :自定义配置 IndicesOptions 的核心参数,灵活性更高。常用参数包括:
    ignoreUnavailable:是否忽略不存在的索引(true 对应 lenient,false 对应 strict)。
    allowNoIndices:当通配符匹配不到任何索引时,是否允许请求继续(true 则不报错,false 则抛出异常)。
    expandWildcardsOpen:是否展开开放索引(true 则包含)。
    expandWildcardsClosed:是否展开关闭索引(true 则包含)。

示例

java 复制代码
  IndicesOptions options = IndicesOptions.fromOptions(
      true,   // ignoreUnavailable:忽略不存在的索引(lenient)
      true,   // allowNoIndices:通配符匹配不到索引时不报错
      true,   // expandWildcardsOpen:包含开放索引
      false   // expandWildcardsClosed:不包含关闭索引
  );

适用场景:预定义方法无法满足需求时,自定义索引处理策略(如仅允许通配符匹配开放索引,且允许匹配不到索引时不报错)。

5.none()
行为 :最严格的模式,不展开任何通配符,且不允许索引不存在。即:

索引名必须是精确名称(不能用通配符)。

若索引不存在,直接抛出异常。
适用场景:仅允许查询指定的精确索引,不允许通配符,且必须确保索引存在(如单一固定索引的查询)。

5、参数:preference(不常用)

java 复制代码
// 可以使用"偏好参数"来指定在执行搜索时优先使用本地分片;默认情况下,系统会随机选择分片来进行搜索
searchRequest.preference("_local"); 

6、使用 SearchSourceBuilder

java 复制代码
// 使用默认选项创建一个SearchSourceBuilder
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); 
//设置查询条件。可以使用任何类型的 QueryBuilder
sourceBuilder.query(QueryBuilders.termQuery("user", "kimchy")); 
//请设置 from 选项,该选项用于指定开始搜索的结果索引。默认值为 0
sourceBuilder.from(0); 
//请设置size选项,以确定返回的搜索结果数量。默认值为10
sourceBuilder.size(5); 
//可以设置一个可选的超时时间,用于限制搜索操作的最大执行时间。
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS)); 

//之后,只需将SearchSourceBuilder添加到SearchRequest中即可
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("posts");
searchRequest.source(sourceBuilder);

7、构建查询语句

搜索查询是通过使用QueryBuilder对象来创建的。Elasticsearch的Query DSL支持多种类型的搜索查询,每种查询类型都对应一个相应的QueryBuilder对象。

java 复制代码
//可以使用其构造函数来创建一个QueryBuilder对象。
// 创建一个全文匹配查询,用于在"user"字段中查找包含"kimchy"这个字样的文本
MatchQueryBuilder matchQueryBuilder = new MatchQueryBuilder("user", "kimchy"); 

// 一旦创建完成,QueryBuilder对象就会提供相应的方法,用于配置其所生成的搜索查询的各项选项
// 模糊匹配启用
matchQueryBuilder.fuzziness(Fuzziness.AUTO); 
// 设置前缀长度
matchQueryBuilder.prefixLength(3); 
// 通过设置最大扩展选项来控制查询过程中的模糊处理过程
matchQueryBuilder.maxExpansions(10); 
java 复制代码
// QueryBuilder 对象也可以通过使用 QueryBuilders 辅助类来创建。该类提供了许多辅助方法,可以让我们以流畅的编程风格来创建 QueryBuilder 对象
QueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("user", "kimchy")
                                                .fuzziness(Fuzziness.AUTO)
                                                .prefixLength(3)
                                                .maxExpansions(10);
java 复制代码
//无论使用何种方法来创建它,都必须按照以下方式将QueryBuilder对象添加到SearchSourceBuilder中:
searchSourceBuilder.query(matchQueryBuilder);

8、指定排序方式

SearchSourceBuilder允许添加一个或多个SortBuilder实例。提供了丰富的排序方式,主要通过 addSort() 方法添加排序条件,核心依赖于不同的排序构建器(如 FieldSortBuilderScriptSortBuilder 等)。以下是常见的排序方式及对应的实现方式:

(1)字段排序(基础排序)

通过指定字段名和排序方向(升序/降序)排序,适用于大多数普通字段(如数值、字符串、日期等)。

核心类:FieldSortBuilder

关键参数:

字段名(fieldName):需排序的字段。

排序方向(SortOrder):SortOrder.ASC(升序)或 SortOrder.DESC(降序)。

排序类型(SortType):可选,指定字段的排序逻辑(如字符串按 string 类型,数值按 number 类型,默认自动推断)。

java 复制代码
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;

SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 按 "price" 字段降序排序
sourceBuilder.sort(new FieldSortBuilder("price").order(SortOrder.DESC));
// 按 "create_time" 字段升序排序(日期字段)
sourceBuilder.sort(new FieldSortBuilder("create_time").order(SortOrder.ASC));

(2)相关性分数排序(_score默认)

按文档与查询的相关性分数(_score)排序,是 Elasticsearch 的默认排序方式(默认降序,即分数越高越靠前)。

本质是对特殊字段 _score 的字段排序。

java 复制代码
// 显式按相关性分数降序排序(默认行为,可省略)
sourceBuilder.sort(new FieldSortBuilder("_score").order(SortOrder.DESC));

(3)文档存储顺序排序(_doc

按文档在索引中的存储顺序排序,不计算相关性分数,是效率最高的排序方式(适合无需相关性、仅需快速返回结果的场景)。

基于特殊字段 _doc 的排序。

java 复制代码
// 按文档存储顺序升序排序(最快)
sourceBuilder.sort(new FieldSortBuilder("_doc").order(SortOrder.ASC));

(4)脚本排序(自定义逻辑)

通过脚本(Script)动态计算排序值,支持复杂的自定义排序逻辑(如多字段组合计算、条件判断等)。

核心类:ScriptSortBuilder

关键参数:

脚本(Script):定义排序值的计算逻辑。

排序类型(ScriptSortType):NUMBER(数值型)或 STRING(字符串型),需与脚本返回值类型匹配。

java 复制代码
import org.elasticsearch.script.Script;
import org.elasticsearch.search.sort.ScriptSortBuilder;
import org.elasticsearch.search.sort.ScriptSortType;

// 脚本逻辑:按 (price * 0.8 + score * 0.2) 计算排序值(数值型)
Script script = new Script("doc['price'].value * 0.8 + doc['score'].value * 0.2");
sourceBuilder.sort(new ScriptSortBuilder(script, ScriptSortType.NUMBER).order(SortOrder.DESC));

(5)地理位置距离排序(geo_point 字段)

针对 geo_point 类型的字段,按文档地理位置与目标点的距离排序(如"附近的人"功能)。

核心类:GeoDistanceSortBuilder

关键参数:

字段名(fieldName):geo_point 类型的字段。

目标点(points):参考点的经纬度(如 new GeoPoint(lat, lon))。

距离单位(unit):如 DistanceUnit.KILOMETERS(千米)。

java 复制代码
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.search.sort.GeoDistanceSortBuilder;

// 按与 (30.0, 120.0) 点的距离升序排序(由近及远)
sourceBuilder.sort(
    new GeoDistanceSortBuilder("location", new GeoPoint(30.0, 120.0))
        .order(SortOrder.ASC)
        .unit(DistanceUnit.KILOMETERS)
);

(6)嵌套对象排序(nested 类型字段)

当排序字段位于 nested 类型的嵌套对象中时,需通过 NestedSortBuilder 指定嵌套路径,确保排序值正确关联嵌套对象。

核心类:NestedSortBuilder(配合 FieldSortBuilder 使用)

java 复制代码
import org.elasticsearch.search.sort.NestedSortBuilder;

// 假设 "comments" 是 nested 类型,需按 "comments.rating" 字段排序
FieldSortBuilder fieldSort = new FieldSortBuilder("comments.rating").order(SortOrder.DESC);
// 指定嵌套路径
NestedSortBuilder nestedSort = new NestedSortBuilder("comments");
fieldSort.setNestedSort(nestedSort);

sourceBuilder.sort(fieldSort);

(7)其他排序配置

排序模式(SortMode):当字段有多个值(如数组)时,指定取哪个值作为排序依据(MIN/MAX/SUM/AVG/MEDIAN)。

java 复制代码
  // 对数组字段 "tags" 取最大值排序
  new FieldSortBuilder("tags").sortMode(SortMode.MAX);

缺失值处理(missing)**:指定字段值缺失时的排序位置(_first 排在最前,_last 排在最后,或自定义值)。

java 复制代码
  // 缺失 "price" 字段的文档排在最后
  new FieldSortBuilder("price").missing("_last");

9、源过滤

默认情况下,搜索请求会返回文档_source的内容;但与Rest API一样,你也可以更改这一行为。

java 复制代码
// 例如,你可以完全禁用对文档_source的检索功能。
sourceBuilder.fetchSource(false);

//该方法还允许使用一个或多个通配符模式来更精细地控制哪些字段应该被包含在结果中,哪些字段应该被排除在外。
String[] includeFields = new String[] {"title", "innerObject.*"};
String[] excludeFields = new String[] {"user"};
sourceBuilder.fetchSource(includeFields, excludeFields);

10、请求高亮显示

可以通过在HighlightBuilder上添加SearchSourceBuilder来突出显示搜索结果。对于每个字段,都可以通过添加一个或多个HighlightBuilder.Field来定义不同的突出显示方式。

高亮显示的文本片段可以稍后从SearchResponse中检索出来

java 复制代码
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
HighlightBuilder highlightBuilder = new HighlightBuilder(); //创建一个新的HighlightBuilder
HighlightBuilder.Field highlightTitle =
        new HighlightBuilder.Field("title"); //为title字段创建一个字段高亮显示功能
highlightTitle.highlighterType("unified");  //设置字段高亮显示类型
highlightBuilder.field(highlightTitle);  //将字段高亮显示功能添加到高亮显示工具中。
HighlightBuilder.Field highlightUser = new HighlightBuilder.Field("user");
highlightBuilder.field(highlightUser);
searchSourceBuilder.highlighter(highlightBuilder);

(1)示例

java 复制代码
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import java.io.IOException;
import java.util.Map;

public class EsHighlightExample {
    public static void main(String[] args) {
        // 1. 创建RestHighLevelClient客户端
        RestClientBuilder builder = RestClient.builder(
            new HttpHost("localhost", 9200, "http") // ES地址和端口
        );
        RestHighLevelClient client = new RestHighLevelClient(builder);

        try {
            // 2. 创建查询请求(指定索引)
            SearchRequest searchRequest = new SearchRequest("your_index"); // 替换为实际索引名

            // 3. 构建查询条件(示例:match查询,搜索"content"字段中包含"关键词"的文档)
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            sourceBuilder.query(QueryBuilders.matchQuery("content", "关键词") // 字段名和搜索关键词
                .fuzziness(Fuzziness.AUTO)); // 可选:模糊匹配

            // 4. 配置高亮规则
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            // 4.1 配置需要高亮的字段(需与查询字段一致)
            HighlightBuilder.Field highlightField = new HighlightBuilder.Field("content");
            // 4.2 设置高亮前后缀标签(如<em>包裹高亮内容)
            highlightField.preTags("<em>");
            highlightField.postTags("</em>");
            // 4.3 可选:设置高亮类型(默认unified,支持plain、fvh等)
            highlightField.highlighterType("unified");
            // 4.4 可选:限制高亮片段长度和数量
            highlightField.fragmentSize(200); // 每个片段最大长度
            highlightField.numberOfFragments(3); // 返回最多3个片段
            // 将字段添加到高亮配置
            highlightBuilder.field(highlightField);

            // 5. 将高亮配置关联到查询
            sourceBuilder.highlighter(highlightBuilder);
            searchRequest.source(sourceBuilder);

            // 6. 执行查询
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

            // 7. 解析结果,提取高亮内容
            for (SearchHit hit : response.getHits().getHits()) {
                // 原始文档内容
                Map<String, Object> sourceMap = hit.getSourceAsMap();
                System.out.println("原始文档: " + sourceMap);

                // 高亮内容(键为字段名,值为高亮片段)
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField contentHighlight = highlightFields.get("content");
                if (contentHighlight != null) {
                    // 高亮片段可能有多个(按numberOfFragments配置)
                    StringBuilder highlightedText = new StringBuilder();
                    for (Text fragment : contentHighlight.fragments()) {
                        highlightedText.append(fragment.string());
                    }
                    System.out.println("高亮结果: " + highlightedText.toString());
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭客户端
            try {
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

11、请求数据聚合

要向搜索结果中添加聚合数据,首先需要创建相应的聚合项,然后将其应用于相关数据。

java 复制代码
// 在以下示例中,我们创建了一个关于公司名称的聚合项,并进一步添加了一个关于该公司员工平均年龄的子聚合项。
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
TermsAggregationBuilder aggregation = AggregationBuilders.terms("by_company")
        .field("company.keyword");
aggregation.subAggregation(AggregationBuilders.avg("average_age")
        .field("age"));
searchSourceBuilder.aggregation(aggregation);

12、建议器

若要向搜索请求中添加建议内容,可以使用那些可以从 SuggestionBuilder 工厂类轻松获取的实现方式。建议内容的生成器需要被添加到最顶层的 SuggestBuilders 中,而 SuggestBuilder 可以在 SearchSourceBuilder 上进行设置。

java 复制代码
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
SuggestionBuilder termSuggestionBuilder =
    SuggestBuilders.termSuggestion("user").text("kmichy"); //为TermSuggestionBuilder字段创建一个新的user,并显示文本kmichy
SuggestBuilder suggestBuilder = new SuggestBuilder();
suggestBuilder.addSuggestion("suggest_user", termSuggestionBuilder); //添加了建议生成器,并为其命名为suggest_user
searchSourceBuilder.suggest(suggestBuilder);

13、查询分析

searchSourceBuilder.profile(true); 用于启用查询剖析(Query Profiling)功能

当通过 SearchSourceBuilder 构建搜索请求时,调用 profile(true) 会告诉 Elasticsearch:在执行该搜索请求时,额外收集并返回详细的查询执行过程信息,包括查询在每个分片上的执行时间、各个子查询的耗时、Lucene 底层的执行细节(如匹配文档数、评分计算时间等)。

作用:

查询剖析功能主要用于调试和优化查询性能 。当搜索请求执行缓慢时,通过分析剖析结果,可以定位性能瓶颈,例如:

哪些子查询耗时最长;

是否存在低效的查询逻辑(如大范围的全表扫描);

分片之间的执行时间是否均衡等。

启用后的效果:

profile(true) 生效后,搜索响应结果(SearchResponse)中会包含一个 profile 字段,该字段包含以下核心信息:

每个分片的剖析数据(shards);

每个分片上的查询阶段(query)和获取阶段(fetch)的耗时;

具体查询子句的执行细节(如 type 表示查询类型,time_in_nanos 表示耗时,breakdown 表示更细粒度的时间拆分)。

注意事项:

剖析功能会带来额外的性能开销(需要收集和计算详细的执行数据),不建议在生产环境的高频查询中启用 ,仅用于调试和优化阶段。

若要关闭剖析功能,可调用 profile(false)(默认即为关闭)。

示例:启用剖析后,可通过响应获取剖析数据:

java 复制代码
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
ProfileResponse profileResponse = response.getProfileResults();
// 解析 profileResponse 中的详细信息

通过分析这些信息,可以针对性地优化查询语句(如调整查询类型、添加合理的过滤条件、优化索引结构等),提升搜索性能。

13、执行与响应

(1)详细解释

java 复制代码
// 同步执行
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); 
java 复制代码
// 1、包含了有关请求执行过程的重要信息,例如HTTP状态码、执行时间,以及请求是提前终止还是超时结束等
RestStatus status = searchResponse.status();
TimeValue took = searchResponse.getTook();
Boolean terminatedEarly = searchResponse.isTerminatedEarly();
boolean timedOut = searchResponse.isTimedOut();

// 2、该响应还提供了有关分片级别执行情况的信息,包括受到搜索影响的分片总数,以及成功完成搜索的分片与未能完成搜索的分片的数量。对于可能出现的故障,也可以通过遍历数组来进行处理,如下例所示:
int totalShards = searchResponse.getTotalShards();
int successfulShards = searchResponse.getSuccessfulShards();
int failedShards = searchResponse.getFailedShards();
for (ShardSearchFailure failure : searchResponse.getShardFailures()) {
    // failures should be handled here
}

// 3、要获取返回的文档,我们首先需要获取响应中包含的SearchHits内容
SearchHits hits = searchResponse.getHits();
 // 总数
TotalHits totalHits = hits.getTotalHits();
long numHits = totalHits.value;
// 判断命中次数是否准确(是否与实际值相等),或者是否至少等于总次数的下限。
TotalHits.Relation relation = totalHits.relation;
float maxScore = hits.getMaxScore();
// 获取搜索结果
SearchHit[] searchHits = hits.getHits();
for (SearchHit hit : searchHits) {
    // 在SearchHits内部,包含着可以依次遍历的各个搜索结果 索引号、文档ID以及评分等
    String index = hit.getIndex();
	String id = hit.getId();
	float score = hit.getScore();
	String sourceAsString = hit.getSourceAsString();// 获取json数据
	Map<String, Object> sourceAsMap = hit.getSourceAsMap(); //获取map数据
	String documentTitle = (String) sourceAsMap.get("title");
	List<Object> users = (List<Object>) sourceAsMap.get("user");
	Map<String, Object> innerObject =
        (Map<String, Object>) sourceAsMap.get("innerObject");
}
// 可以从结果中的每个SearchHit中获取被高亮显示的文本片段。该功能允许用户访问一个字段名称与HighlightField实例之间的映射关系;每个undefined实例都包含一个或多个被高亮显示的文本片段。
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits.getHits()) {
    Map<String, HighlightField> highlightFields = hit.getHighlightFields();
    HighlightField highlight = highlightFields.get("title"); //获取title字段的高亮显示效果
    Text[] fragments = highlight.fragments(); //获取一个或多个包含所高亮显示的字段内容的片段 
    String fragmentString = fragments[0].string();
}

// 4、检索聚合数据
// 要获取这些聚合数据,首先需要找到聚合树的根节点,即SearchResponse对象,然后才能根据名称来获取相应的聚合数据。
Aggregations aggregations = searchResponse.getAggregations();
Terms byCompanyAggregation = aggregations.get("by_company"); //获取by_company项的聚合结果
Bucket elasticBucket = byCompanyAggregation.getBucketByKey("Elastic"); //获取以Elastic为键的桶。
Avg averageAge = elasticBucket.getAggregations().get("average_age"); //从该数据桶中获取average_age级别的子聚合数据
double avg = averageAge.getValue();

//请注意,如果您通过名称来访问聚合数据,就需要根据所请求的聚合类型来指定相应的聚合接口;否则将会出现错误提示ClassCastException。
//这会引发异常,因为"by_company"属于terms类型的聚合数据,而我们却试图将其作为range类型的聚合数据来获取。
Range range = aggregations.get("by_company"); 

//也可以将所有聚合结果以映射的形式进行查看,这些映射是按照聚合名称来排序的。在这种情况下,需要明确地将数据转换为相应的聚合接口类型才能进行后续处理。
Map<String, Aggregation> aggregationMap = aggregations.getAsMap();
Terms companyAggregation = (Terms) aggregationMap.get("by_company");

//还有一些获取器可以将所有顶级聚合结果以列表的形式返回。
List<Aggregation> aggregationList = aggregations.asList();

//最后但同样重要的是,你可以遍历所有的聚合结果,然后根据它们的类型来决定如何进一步处理它们。
for (Aggregation agg : aggregations) {
    String type = agg.getType();
    if (type.equals(TermsAggregationBuilder.NAME)) {
        Bucket elasticBucket = ((Terms) agg).getBucketByKey("Elastic");
        long numberOfDocs = elasticBucket.getDocCount();
    }
}

// 5、获取建议
//要获取来自编号为SearchResponse的建议内容,可以使用编号为Suggest的对象作为入口点,然后从中提取嵌套在内的建议对象。
Suggest suggest = searchResponse.getSuggest(); //可以使用Suggest类来获取建议
TermSuggestion termSuggestion = suggest.getSuggestion("suggest_user"); //可以通过名称来查找建议。您需要将这些建议分配到相应的建议类中(例如TermSuggestion类型);否则将会抛出ClassCastException错误
for (TermSuggestion.Entry entry : termSuggestion.getEntries()) { //遍历这些建议条目。
    for (TermSuggestion.Entry.Option option : entry) { //遍历某个条目中的所有选项。
        String suggestText = option.getText().string();
    }
}

// 6、检索分析结果
//配置文件分析结果是通过SearchResponse方法从getProfileResults()中获取的。该方法会返回一个Map,其中包含了每个参与ProfileShardResult执行过程的分片所对应的SearchRequest对象。这些ProfileShardResult数据会使用能够唯一标识相应分片的键被存储在Map中。

//以下是一个示例代码,展示了如何遍历每个分片的所有性能分析结果:
Map<String, ProfileShardResult> profilingResults =
        searchResponse.getProfileResults(); //从Map中检索ProfileShardResult中的SearchResponse
for (Map.Entry<String, ProfileShardResult> profilingResult : profilingResults.entrySet()) { //如果已知分片的键,就可以通过该键来获取分析结果;否则,遍历所有分析结果可能更为简单。
    String key = profilingResult.getKey(); //获取用于识别ProfileShardResult所属分片的键
    ProfileShardResult profileShardResult = profilingResult.getValue(); //检索给定分片的ProfileShardResult内容
}

//ProfileShardResult对象本身包含一个或多个查询结果;每个结果对应于针对底层Lucene索引执行的一次查询。
List<QueryProfileShardResult> queryProfileShardResults =
        profileShardResult.getQueryProfileResults(); 
for (QueryProfileShardResult queryProfileResult : queryProfileShardResults) { 

}

//每个QueryProfileShardResult对象都可以用来查看详细的查询树执行过程,其返回结果以ProfileResult对象的列表形式呈现。
for (ProfileResult profileResult : queryProfileResult.getQueryResults()) { //遍历这些分析结果
    String queryName = profileResult.getQueryName(); //获取Lucene查询的名称
    long queryTimeInMillis = profileResult.getTime(); //获取执行Lucene查询所花费的时间(以毫秒为单位)
    List<ProfileResult> profiledChildren = profileResult.getProfiledChildren(); //检索子查询的结果(如果有的话)。
}

//QueryProfileShardResult还可以帮助用户查看Lucene收集器的相关配置信息。
CollectorResult collectorResult = queryProfileResult.getCollectorResult();  //获取Lucene收集器的分析结果
String collectorName = collectorResult.getName();  //获取Lucene收集器的名称
Long collectorTimeInMillis = collectorResult.getTime(); //获取执行Lucene收集器所花费的时间(以毫秒为单位)
List<CollectorResult> profiledChildren = collectorResult.getProfiledChildren(); //检索子收集器的配置信息结果(如果有的话)。

//与查询树的执行方式非常相似,QueryProfileShardResult对象也可以用于查看详细的聚合数据计算过程。
AggregationProfileShardResult aggsProfileResults =
        profileShardResult.getAggregationProfileResults(); 
for (ProfileResult profileResult : aggsProfileResults.getProfileResults()) { 
    String aggName = profileResult.getQueryName(); 
    long aggTimeInMillis = profileResult.getTime(); 
    List<ProfileResult> profiledChildren = profileResult.getProfiledChildren(); 
}

(2)实战示例

java 复制代码
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.hits.Hit;
import java.util.Map;

private static void parseResponse(SearchResponse response) {
    // 1. 获取总命中数
    long totalHits = response.getHits().getTotalHits().value;
    float maxScore = response.getHits().getMaxScore();
    System.out.println("总命中数:" + totalHits + ",最高评分:" + maxScore);

    // 2. 遍历命中的文档
    SearchHits hits = response.getHits();
    for (Hit hit : hits) {
        // 文档ID
        String docId = hit.getId();
        // 文档评分
        float score = hit.getScore();
        // 文档内容(Map形式,key为字段名,value为字段值)
        Map<String, Object> sourceMap = hit.getSourceAsMap();
        String name = (String) sourceMap.get("name");
        Integer age = (Integer) sourceMap.get("age");

        System.out.println("文档ID:" + docId + ",评分:" + score + ",内容:" + sourceMap);

        // 3. 解析高亮(如果配置了高亮)
        Map<String, org.elasticsearch.search.fetch.subphase.highlight.HighlightField> highlightFields = hit.getHighlightFields();
        if (highlightFields.containsKey("name")) {
            String highlightedName = highlightFields.get("name").getFragments()[0].toString();
            System.out.println("高亮name:" + highlightedName); // 例如:<em>张三</em>
        }
    }
}

二、QueryBuilders的静态方法详解

在Elasticsearch中,QueryBuilders是构建查询条件的核心工具类,提供了大量静态方法,对应ES支持的各种查询类型。这些查询可分为全文查询term级查询复合查询特殊查询等类别。

1、全文查询(Full-text Queries)

全文查询用于对文本字段(text类型)进行分词后的匹配,会根据分词器对查询文本和字段内容进行分词后再匹配,适用于模糊搜索场景。

(1)matchQuery():单字段全文匹配

最常用的全文查询,对查询文本分词后,在指定字段中匹配包含任意分词结果的文档(类似"OR"逻辑)。

示例:在title字段中匹配包含"elasticsearch 入门"分词结果的文档(如包含"elasticsearch"或"入门")。

java 复制代码
// 匹配title字段中包含"elasticsearch"或"入门"的文档
QueryBuilders.matchQuery("title", "elasticsearch 入门");

(2)matchPhraseQuery():短语匹配

要求查询文本的分词结果在字段中连续出现且顺序一致(类似"短语精确匹配")。

示例:在content字段中匹配包含"分布式搜索"完整短语的文档(分词后"分布式"和"搜索"必须连续且顺序不变)。

java 复制代码
// 匹配content字段中包含"分布式搜索"完整短语的文档
QueryBuilders.matchPhraseQuery("content", "分布式搜索");

(3)matchPhrasePrefixQuery():短语前缀匹配

类似matchPhraseQuery,但允许最后一个分词是前缀匹配(适用于"搜索提示"场景)。

示例:在name字段中匹配"张三"短语,或"张"开头的短语(如"张三""张伟"等)。

java 复制代码
// 匹配name字段中"张"开头且后续分词连续的短语(如"张三"、"张伟")
QueryBuilders.matchPhrasePrefixQuery("name", "张");

(4)multiMatchQuery():多字段全文匹配

在多个字段中执行matchQuery,可指定字段权重(boost)。

示例:在title(权重2.0)和content(默认权重1.0)字段中匹配"java 教程"。

java 复制代码
// 在title(权重更高)和content字段中匹配"java 教程"
QueryBuilders.multiMatchQuery("java 教程", "title", "content")
    .field("title", 2.0f); // 提升title字段的权重

(5)commonTermsQuery():常见词查询

降低"常见词"(如"的""是")的权重,避免其影响评分(优化全文查询性能)。

示例:在description字段中匹配"好用的工具",降低"的"的权重。

java 复制代码
// 对"好用的工具"进行查询,自动降低常见词"的"的权重
QueryBuilders.commonTermsQuery("description", "好用的工具");

(6)queryStringQuery():查询字符串查询

支持类似Lucene的查询语法(如title:elasticsearch AND content:入门),可在多个字段中匹配。

示例:通过查询字符串匹配title包含"es"且author为"张三"的文档。

java 复制代码
// 支持 Lucene 风格的查询语法
QueryBuilders.queryStringQuery("title:es AND author:张三");

(7)simpleQueryStringQuery():简化的查询字符串查询

类似queryStringQuery,但忽略无效语法(更容错),支持+(AND)、|(OR)、-(NOT)等符号。

示例:匹配content中包含"java"或"python"但不包含"php"的文档。

java 复制代码
// 容错性更好的查询字符串,支持+、|、-符号
QueryBuilders.simpleQueryStringQuery("java | python -php")
    .field("content"); // 限制在content字段

2、Term级查询(Term-level Queries)

Term级查询用于对非文本字段(如数字、日期、keyword类型)或文本字段的"分词结果"进行精确匹配,不进行分词处理,适用于精确查询场景。

(1)termQuery():单值精确匹配

匹配字段中完全等于指定值的文档(适用于keyword、数字、日期等类型)。

注意:文本字段(text)会被分词,若用termQuery匹配text字段,需匹配其分词后的某个词(而非原始文本),建议对text字段使用matchQuery,对精确匹配用keyword子字段。

示例:匹配status(keyword类型)为"active"的文档。

java 复制代码
// 精确匹配status字段为"active"的文档(status应为keyword类型)
QueryBuilders.termQuery("status", "active");

(2)termsQuery():多值精确匹配

匹配字段值等于多个值中的任意一个的文档(类似"IN"逻辑)。

示例:匹配tags(keyword数组)包含"java"或"elasticsearch"的文档。

java 复制代码
// 匹配tags字段包含"java"或"elasticsearch"的文档
QueryBuilders.termsQuery("tags", "java", "elasticsearch");

(3)rangeQuery():范围查询

匹配字段值在指定范围内的文档(支持数字、日期、字符串)。

常用方法:gte()(>=)、gt()(>)、lte()(<=)、lt()(<)、format()(日期格式)。

示例1(数字范围):匹配age在20到30之间(包含20和30)的文档。

java 复制代码
// age >=20 且 age <=30
QueryBuilders.rangeQuery("age").gte(20).lte(30);

示例2(日期范围):匹配create_time在2023-01-01到2023-12-31之间的文档。

java 复制代码
// create_time >= "2023-01-01" 且 <= "2023-12-31"
QueryBuilders.rangeQuery("create_time")
    .gte("2023-01-01")
    .lte("2023-12-31")
    .format("yyyy-MM-dd"); // 指定日期格式

(4)existsQuery():存在查询

匹配指定字段有值(非null)的文档(适用于判断字段是否存在)。

示例:匹配email字段存在的文档(排除没有邮箱的记录)。

java 复制代码
// 匹配email字段存在的文档
QueryBuilders.existsQuery("email");

(5)missingQuery():不存在查询(7.10已废弃,建议用bool + mustNot + exists替代)

匹配指定字段无值 (null或不存在)的文档。7.10中已废弃,推荐用boolQuery().mustNot(existsQuery())

替代示例:匹配phone字段不存在的文档。

java 复制代码
// 替代missingQuery:匹配phone字段不存在的文档
QueryBuilders.boolQuery()
    .mustNot(QueryBuilders.existsQuery("phone"));

(6)prefixQuery():前缀匹配

匹配字段值以指定前缀开头的文档(适用于keyword或不分词字段)。

示例:匹配username(keyword)以"zhangs"开头的文档(如"zhangsan")。

java 复制代码
// 匹配username以"zhangs"开头的文档
QueryBuilders.prefixQuery("username", "zhangs");

(7)wildcardQuery():通配符查询

支持*(匹配任意字符序列,包括空)和?(匹配单个字符)的通配符匹配(适用于keyword字段)。

注意:性能较差,避免用*开头。

示例:匹配name(keyword)以"张"开头且长度为3的文档(如"张三三")。

java 复制代码
// 张?:张开头,后面1个字符(总长度2);张??:总长度3
QueryBuilders.wildcardQuery("name", "张??");

(8)regexpQuery():正则表达式查询

通过正则表达式匹配字段值(适用于复杂模式匹配,性能较差)。

示例:匹配phone(keyword)以"138"开头且后面8位为数字的文档。

java 复制代码
// 正则:138开头,后跟8位数字
QueryBuilders.regexpQuery("phone", "138[0-9]{8}");

(9)fuzzyQuery():模糊匹配

允许查询文本与字段值有一定编辑距离(默认1-2,即插入、删除、替换字符的次数)的匹配(适用于拼写纠错场景)。

示例:匹配name字段与"zhangsan"编辑距离≤2的文档(如"zhangsa""zhangsaan")。

java 复制代码
// 允许2次编辑距离的模糊匹配
QueryBuilders.fuzzyQuery("name", "zhangsan").fuzziness(Fuzziness.TWO);

3、复合查询(Compound Queries)

复合查询用于组合多个子查询,实现复杂逻辑(如AND、OR、NOT等)。

(1)boolQuery():布尔查询(最常用)

通过must(必须满足,影响评分)、should(满足其一即可,影响评分)、mustNot(必须不满足,不影响评分)、filter(必须满足,不影响评分,性能更好)组合子查询。

示例:查询age在20-30之间(filter)、name包含"张"(must)、且gender不为"女"(mustNot)的文档。

java 复制代码
QueryBuilders.boolQuery()
    // 必须满足(影响评分)
    .must(QueryBuilders.matchQuery("name", "张"))
    // 必须不满足
    .mustNot(QueryBuilders.termQuery("gender", "女"))
    // 必须满足(不影响评分,性能更好)
    .filter(QueryBuilders.rangeQuery("age").gte(20).lte(30));

(2)constantScoreQuery():常量评分查询

对匹配的文档赋予固定评分 (忽略子查询的原始评分),通常与filter结合使用(性能优)。

示例:匹配status为"active"的文档,所有匹配文档评分均为1.0。

java 复制代码
// 所有匹配的文档评分固定为1.0
QueryBuilders.constantScoreQuery(
    QueryBuilders.termQuery("status", "active")
).boost(1.0f); // 固定评分

(3)boostingQuery():提升/降低评分查询

通过positive(提升匹配文档的评分)和negative(降低匹配文档的评分)调整结果排序。

示例:优先匹配content包含"elasticsearch"的文档(positive),若同时包含"旧版本"则降低其评分(negative)。

java 复制代码
QueryBuilders.boostingQuery(
    // 正向查询:匹配的文档评分提升
    QueryBuilders.matchQuery("content", "elasticsearch"),
    // 负向查询:匹配的文档评分降低
    QueryBuilders.matchQuery("content", "旧版本")
).negativeBoost(0.5f); // 负向匹配的文档评分乘以0.5

(4)disMaxQuery():离散最大评分查询

对多个子查询的结果取最大评分(而非求和),适用于"多个字段中只要一个匹配度高即可"的场景。

示例:在titlecontent中匹配"java",取两个字段中评分最高的作为文档评分。

java 复制代码
QueryBuilders.disMaxQuery()
    .add(QueryBuilders.matchQuery("title", "java")) // 子查询1
    .add(QueryBuilders.matchQuery("content", "java")) // 子查询2
    .tieBreaker(0.3f); // 其他子查询评分乘以0.3后与最大评分求和(可选)

4、特殊查询

(1)idsQuery():根据文档ID查询

匹配指定ID的文档(类似"根据主键查询")。

示例:查询ID为"1""2""3"的文档。

java 复制代码
// 匹配ID为1、2、3的文档
QueryBuilders.idsQuery().addIds("1", "2", "3");

(2)functionScoreQuery():函数评分查询

通过自定义函数(如字段值、随机、脚本等)调整文档评分,实现个性化排序。

示例:对status为"active"的文档,根据view_count(浏览量)越高评分越高。

java 复制代码
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.common.lucene.search.function.FieldValueFactorFunction;

// 基础查询:匹配status为active的文档
QueryBuilder baseQuery = QueryBuilders.termQuery("status", "active");

// 函数:根据view_count提升评分(值越高评分越高)
FieldValueFactorFunction function = new FieldValueFactorFunction(
    "view_count", // 基于view_count字段
    FieldValueFactorFunction.Modifier.LOG1P, // 计算方式:log(1 + value)
    1.0f // 权重
);

// 组合:用function调整baseQuery的评分
QueryBuilders.functionScoreQuery(
    baseQuery,
    new FunctionScoreQuery.ScoreMode(), // 评分模式(默认SUM)
    new FunctionScoreQuery.FilterFunctionBuilder(function)
);

(3)nestedQuery():嵌套查询

用于查询nested类型字段(嵌套对象),需指定嵌套路径。

示例:查询users(nested类型)中age>25且name包含"张三"的嵌套对象。

java 复制代码
// 嵌套字段路径为"users"
QueryBuilders.nestedQuery(
    "users", // 嵌套字段路径
    // 嵌套对象的查询条件
    QueryBuilders.boolQuery()
        .must(QueryBuilders.matchQuery("users.name", "张三"))
        .must(QueryBuilders.rangeQuery("users.age").gt(25)),
    ScoreMode.None // 评分模式(不影响主文档评分)
);

(4)hasChildQuery()/`hasParentQuery():父子查询

hasChildQuery:查询包含符合条件的子文档的父文档;
hasParentQuery:查询包含符合条件的父文档的子文档。

示例(hasChildQuery):查询包含age>18的child_type子文档的父文档。

java 复制代码
// 父文档中存在子类型为child_type且age>18的子文档
QueryBuilders.hasChildQuery(
    "child_type", // 子文档类型
    QueryBuilders.rangeQuery("age").gt(18), // 子文档查询条件
    ScoreMode.Avg // 父文档评分取子文档评分的平均值
);

三、SearchTemplateRequest:搜索模板API

1、内联模板

java 复制代码
// 1、使用
SearchTemplateRequest request = new SearchTemplateRequest();
request.setRequest(new SearchRequest("posts")); // 索引

request.setScriptType(ScriptType.INLINE);
request.setScript( //该模板用于定义搜索源的结构。由于"mustache模板"并不总是符合JSON格式的,因此需要以字符串的形式传递。
    "{" +
    "  \"query\": { \"match\" : { \"{{field}}\" : \"{{value}}\" } }," +
    "  \"size\" : \"{{size}}\"" +
    "}");

Map<String, Object> scriptParams = new HashMap<>();
scriptParams.put("field", "title");
scriptParams.put("value", "elasticsearch");
scriptParams.put("size", 5);
request.setScriptParams(scriptParams); //参数

// 2、执行与响应
SearchTemplateResponse response = client.searchTemplate(request, RequestOptions.DEFAULT);
SearchResponse searchResponse = response.getResponse();
SearchTemplateResponse renderResponse = client.searchTemplate(request, RequestOptions.DEFAULT);
BytesReference source = renderResponse.getSource(); // 二进制的 json结果
相关推荐
yumgpkpm2 小时前
Hadoop在AI时代如何实现生态协同? CMP 7.13(或类 Cloudera CDP7.3 的 CMP 7.13 平台,如华为鲲鹏 ARM 版)
大数据·hadoop·elasticsearch·zookeeper·kafka·hbase·cloudera
java1234_小锋2 小时前
MyBatis如何处理懒加载和预加载?
java·开发语言·mybatis
hdsoft_huge2 小时前
小程序弱网 / 无网场景下 CacheManager 离线表单与拍照上传解决方案
java·小程序·uni-app
麦嘟学编程3 小时前
开发环境搭建之JDK11+maven3.9.8+tomcat9安装
java
小坏讲微服务3 小时前
使用 Spring Cloud Gateway 实现集群
java·spring boot·分布式·后端·spring cloud·中间件·gateway
wa的一声哭了3 小时前
hf中transformers库中generate的greedy_search
android·java·javascript·pytorch·深度学习·语言模型·transformer
.格子衫.3 小时前
Maven的下载与安装
java·maven
Override笑看人生3 小时前
gitlab中maven私有库使用备忘
java·gitlab·maven
不知几秋3 小时前
配置JDK和MAVEN
java·开发语言·maven