Elasticsearch 之 ElasticsearchRestTemplate 嵌套聚合查询【嵌套文档聚合查询】

前言:

上一篇我们分享了使用 ElasticsearchRestTemplate 实现 Elasticsearch 的聚合查询,其中度量聚合和桶聚合我们都进行了案例分享,唯有嵌套聚合查询没有分享,本篇我们来分享一下使用 ElasticsearchRestTemplate 实现 Elasticsearch 的嵌套聚合查询。

Elasticsearch 系列文章传送门

Elasticsearch 基础篇【ES】

Elasticsearch Windows 环境安装

Elasticsearch 之 ElasticsearchRestTemplate 普通查询

Elasticsearch 之 ElasticsearchRestTemplate 聚合查询

业务场景

假设我们一台家用汽车除了有主要的颜色、型号、价格等主要参数之外,还有一组车载手机支架信息,多个手机支架可以方便各个作为上的乘客使用手机,如果我们想知道某个手机支架使用在哪些型号的车上,那该怎么去查询?如果我们还想知道某个车型下手机支架的平均费用,又该怎么去查询?

Nested 关键字

Nested 翻译过来就是嵌套的意思,如果需要实现上述业务场景,我们需要使用嵌套查询,也就是会用到 Nested 关键字,Nested 在 Elasticsearch 中主要用于处理对象数组的情况,嵌套对象是一种特殊的对象类型,它允许在文档中存储一组相关的对象,并且在查询时能够正确地处理这些对象之间的关系,但是在映射(mapping)时候,需要将对象数组标记为 Nested 类型。

简单来说就是想要实现嵌套查询就需要将对象数组标记为 Nested 类型,再使用查询语法即可实现嵌套查询。

案例对象

我们使用小汽车来做为案例演示对象,小汽车内部拥有一个手机支架数组对象,代码如下:

@Data
@Document(indexName = "car_7")
@Mapping(mappingPath="mapper/car.json")
public class CarDO {

    private Long id;

    //颜色
    @Field(store = true, type = FieldType.Keyword)
    private String color;

    //model --不同类型的车 例如 su 7  P7
    private String model;

    //价格
    private double price;

    //车载手机支架
    @Field(store = true, type = FieldType.Nested)
    private List<CarMobilePhoneHolderDO> carMobilePhoneHolder;

}

@Data
public class CarMobilePhoneHolderDO {

    //品牌
    private String brand;

    //价格
    private double price;

    //颜色
    private String color;

}

查询演示场景一

我们想要查询“厂家B”生产的手机支架都使用在哪些型号的车上,我们来剖析这个场景,查询条件是手机支架的生产厂家,对于这种情况,我们使用普通查询无法实现,使用桶查询也无法实现,但是刚好满足聚合查询场景,因为我写了如下代码:

public void nestedAggregationCarHolder() {
	NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
	queryBuilder.withQuery(QueryBuilders.nestedQuery("carMobilePhoneHolder",
			QueryBuilders.termQuery("carMobilePhoneHolder.brand.keyword", "厂家B"), ScoreMode.Total));
	SearchHits<CarDO> search = elasticsearchRestTemplate.search(queryBuilder.build(), CarDO.class);
	List<CarDO> carList = search.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());
	for (CarDO carDO : carList) {
		System.out.println(carDO.toString());
	}
}

上述代码非常简单,但是当我运行后,控制台报错如下:

org.elasticsearch.ElasticsearchStatusException: Elasticsearch exception [type=search_phase_execution_exception, reason=all shards failed]
	at org.elasticsearch.rest.BytesRestResponse.errorFromXContent(BytesRestResponse.java:187) ~[elasticsearch-7.9.3.jar:7.9.3]
	at org.elasticsearch.client.RestHighLevelClient.parseEntity(RestHighLevelClient.java:1892) ~[elasticsearch-rest-high-level-client-7.9.3.jar:7.9.3]
	at org.elasticsearch.client.RestHighLevelClient.parseResponseException(RestHighLevelClient.java:1869) ~[elasticsearch-rest-high-level-client-7.9.3.jar:7.9.3]
	at org.elasticsearch.client.RestHighLevelClient.internalPerformRequest(RestHighLevelClient.java:1626) ~[elasticsearch-rest-high-level-client-7.9.3.jar:7.9.3]
	at org.elasticsearch.client.RestHighLevelClient.performRequest(RestHighLevelClient.java:1583) ~[elasticsearch-rest-high-level-client-7.9.3.jar:7.9.3]
	at org.elasticsearch.client.RestHighLevelClient.performRequestAndParseEntity(RestHighLevelClient.java:1553) ~[elasticsearch-rest-high-level-client-7.9.3.jar:7.9.3]
	at org.elasticsearch.client.RestHighLevelClient.search(RestHighLevelClient.java:1069) ~[elasticsearch-rest-high-level-client-7.9.3.jar:7.9.3]
	at org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate.lambda$search$10(ElasticsearchRestTemplate.java:256) ~[spring-data-elasticsearch-4.1.8.jar:4.1.8]
	at org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate.execute(ElasticsearchRestTemplate.java:343) ~[spring-data-elasticsearch-4.1.8.jar:4.1.8]
	at org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate.search(ElasticsearchRestTemplate.java:256) ~[spring-data-elasticsearch-4.1.8.jar:4.1.8]
	at org.springframework.data.elasticsearch.core.AbstractElasticsearchTemplate.search(AbstractElasticsearchTemplate.java:446) ~[spring-data-elasticsearch-4.1.8.jar:4.1.8]
	at com.order.service.service.impl.CarEsServiceImpl.nestedAggregationCarHolder(CarEsServiceImpl.java:357) ~[classes/:na]
	at com.order.service.service.impl.CarEsServiceImpl.nestedAggregationCar(CarEsServiceImpl.java:331) ~[classes/:na]
	at com.order.service.controller.ElasticsearchController.nestedAggregationCar(ElasticsearchController.java:176) ~[classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_121]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_121]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197) ~[spring-web-5.3.6.jar:5.3.6]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141) ~[spring-web-5.3.6.jar:5.3.6]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.3.6.jar:5.3.6]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894) ~[spring-webmvc-5.3.6.jar:5.3.6]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.6.jar:5.3.6]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.6.jar:5.3.6]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060) ~[spring-webmvc-5.3.6.jar:5.3.6]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962) ~[spring-webmvc-5.3.6.jar:5.3.6]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.6.jar:5.3.6]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.6.jar:5.3.6]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:626) ~[tomcat-embed-core-9.0.45.jar:4.0.FR]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.6.jar:5.3.6]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.45.jar:4.0.FR]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.45.jar:9.0.45]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.6.jar:5.3.6]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.6.jar:5.3.6]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.6.jar:5.3.6]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.6.jar:5.3.6]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93) ~[spring-boot-actuator-2.4.5.jar:2.4.5]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.6.jar:5.3.6]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.6.jar:5.3.6]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.6.jar:5.3.6]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) [tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) [tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) [tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) [tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) [tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) [tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) [tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707) [tomcat-embed-core-9.0.45.jar:9.0.45]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.45.jar:9.0.45]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_121]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_121]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.45.jar:9.0.45]
	at java.lang.Thread.run(Thread.java:745) [na:1.8.0_121]
	Suppressed: org.elasticsearch.client.ResponseException: method [POST], host [http://localhost:9200], URI [/car_5/_search?typed_keys=true&max_concurrent_shard_requests=5&ignore_unavailable=false&expand_wildcards=open&allow_no_indices=true&ignore_throttled=true&search_type=dfs_query_then_fetch&batched_reduce_size=512&ccs_minimize_roundtrips=true], status line [HTTP/1.1 400 Bad Request]
{"error":{"root_cause":[{"type":"query_shard_exception","reason":"failed to create query: {\n  \"nested\" : {\n    \"query\" : {\n      \"term\" : {\n        \"carMobilePhoneHolder.brand.keyword\" : {\n          \"value\" : \"厂家B\",\n          \"boost\" : 1.0\n        }\n      }\n    },\n    \"path\" : \"carMobilePhoneHolder\",\n    \"ignore_unmapped\" : false,\n    \"score_mode\" : \"sum\",\n    \"boost\" : 1.0\n  }\n}","index_uuid":"TeDcH8dkR1S7m_cMGWNJRQ","index":"car_5"}],"type":"search_phase_execution_exception","reason":"all shards failed","phase":"query","grouped":true,"failed_shards":[{"shard":0,"index":"car_5","node":"H-EuAgweS6Cei9fs4WxlHg","reason":{"type":"query_shard_exception","reason":"failed to create query: {\n  \"nested\" : {\n    \"query\" : {\n      \"term\" : {\n        \"carMobilePhoneHolder.brand.keyword\" : {\n          \"value\" : \"厂家B\",\n          \"boost\" : 1.0\n        }\n      }\n    },\n    \"path\" : \"carMobilePhoneHolder\",\n    \"ignore_unmapped\" : false,\n    \"score_mode\" : \"sum\",\n    \"boost\" : 1.0\n  }\n}","index_uuid":"TeDcH8dkR1S7m_cMGWNJRQ","index":"car_5","caused_by":{"type":"illegal_state_exception","reason":"[nested] nested object under path [carMobilePhoneHolder] is not of nested type"}}}]},"status":400}
		at org.elasticsearch.client.RestClient.convertResponse(RestClient.java:302) ~[elasticsearch-rest-client-7.9.3.jar:7.9.3]
		at org.elasticsearch.client.RestClient.performRequest(RestClient.java:272) ~[elasticsearch-rest-client-7.9.3.jar:7.9.3]
		at org.elasticsearch.client.RestClient.performRequest(RestClient.java:246) ~[elasticsearch-rest-client-7.9.3.jar:7.9.3]
		at org.elasticsearch.client.RestHighLevelClient.internalPerformRequest(RestHighLevelClient.java:1613) ~[elasticsearch-rest-high-level-client-7.9.3.jar:7.9.3]
		... 64 common frames omitted

错误信息比较多,遇到这种情况我们要学会从一大堆错误信息中提取到关键的错误信息,从这一大堆错误信息中我提取到如下两点关键信息:

关键信息一:

如下,大概得意思是查询阶段报错,所有分配都失败,经过确认不是我这里报错的根本原因,我使用普通查询和写入文档都没有问题,因此不在留意这段错误信息。

Elasticsearch exception [type=search_phase_execution_exception, reason=all shards failed]

关键信息二:

如下这段信息就略微有一点多了,但是我们还是可以提取到一些很有价值的信息【“reason”:“[nested] nested object under path [carMobilePhoneHolder] is not of nested type”】,大致意思就说我们的 carMobilePhoneHolder 字段不是嵌套(nested )类型,这个错误信息看起来更像是造成查询报错的根本原因。

{"error":{"root_cause":[{"type":"query_shard_exception","reason":"failed to create query: {\n  \"nested\" : {\n    \"query\" : {\n      \"term\" : {\n        \"carMobilePhoneHolder.brand.keyword\" : {\n          \"value\" : \"厂家B\",\n          \"boost\" : 1.0\n        }\n      }\n    },\n    \"path\" : \"carMobilePhoneHolder\",\n    \"ignore_unmapped\" : false,\n    \"score_mode\" : \"sum\",\n    \"boost\" : 1.0\n  }\n}","index_uuid":"TeDcH8dkR1S7m_cMGWNJRQ","index":"car_5"}],"type":"search_phase_execution_exception","reason":"all shards failed","phase":"query","grouped":true,"failed_shards":[{"shard":0,"index":"car_5","node":"H-EuAgweS6Cei9fs4WxlHg","reason":{"type":"query_shard_exception","reason":"failed to create query: {\n  \"nested\" : {\n    \"query\" : {\n      \"term\" : {\n        \"carMobilePhoneHolder.brand.keyword\" : {\n          \"value\" : \"厂家B\",\n          \"boost\" : 1.0\n        }\n      }\n    },\n    \"path\" : \"carMobilePhoneHolder\",\n    \"ignore_unmapped\" : false,\n    \"score_mode\" : \"sum\",\n    \"boost\" : 1.0\n  }\n}","index_uuid":"TeDcH8dkR1S7m_cMGWNJRQ","index":"car_5","caused_by":{"type":"illegal_state_exception","reason":"[nested] nested object under path [carMobilePhoneHolder] is not of nested type"}}}]},"status":400}

“reason”:“[nested] nested object under path [carMobilePhoneHolder] is not of nested type”

控制台报错提示 carMobilePhoneHolder 不是嵌套(nested )类型,但是我确实在定义 Car 对象的时候将 carMobilePhoneHolder 的字段类型定义了 FieldType.Nested,代码如下:

@Field(store = true, type = FieldType.Nested)
private List<CarMobilePhoneHolderDO> carMobilePhoneHolder;

字段我们确实定义了为 FieldType.Nested 类型,控制台报错也不会骗人,因此可以大胆推测是【@Field(store = true, type = FieldType.Nested)】没有生效,因此我就去查询了相关资料来解决这个问题,我能查阅到的资料提供了一下几种解决方式:

  • 使用 @Mapping 注解引入自己写好的 json 文件,Json 文件中是 Mapping 信息 --【无效】
@Mapping(mappingPath="mapper/car.json")
  • 在启动类上增加 @EnableElasticsearchRepositories 注解 --【无效】
@EnableElasticsearchRepositories
@SpringBootApplication
public class OrderServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }

}
  • 在保存文档之前先创建索引,创建完成索引后并进行 Mapping --【有效】
public void saveCar(CarDO carDO) {
	//创建索引
	elasticsearchRestTemplate.createIndex(CarDO.class);
	// Mapping 
	elasticsearchRestTemplate.putMapping(CarDO.class);
	elasticsearchRestTemplate.save(carDO);
	List<CarDO> list = new ArrayList<>();
	list.add(carDO);
	elasticsearchRestTemplate.save(list);
}

我这里使用的 Elasticsearch 是 7.4.0 版本的,供参考。

使用先创建索引后并进行 Mapping 后(需要将之前的索引删除,或者新建索引),查询结果如下:

CarDO(id=1, color=雅灰, model=小米su7, price=19.98, carMobilePhoneHolder=[CarMobilePhoneHolderDO(brand=厂家B, price=8.0, color=金色)])

查询结果符合预期。

查询演示场景二

我们想知道某个车型下手机支架的平均费用,又该怎么去查询?

对于这个业务场景我们可以拆分为两步:

  1. 查询某个车型,这个简单我们使用普通查询即可。
  2. 查询车型下的手机支架平均费用,这个就需要用到嵌套查询了。

我们先来实现查询车载手机支架的平均费用,代码如下:

public void nestedAvgAggregationCarHolder() {
	NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
	queryBuilder.withQuery(QueryBuilders.matchAllQuery());
	queryBuilder.addAggregation(AggregationBuilders
			.nested("carMobilePhoneHolder_agg", "carMobilePhoneHolder")
			.subAggregation(AggregationBuilders.avg("avg_carMobilePhoneHolder_price").field("carMobilePhoneHolder.price"))
	);
	SearchHits<CarDO> search = elasticsearchRestTemplate.search(queryBuilder.build(), CarDO.class);
	Aggregations aggregations = search.getAggregations();
	assert aggregations != null;
	ParsedNested parsedNested = aggregations.get("carMobilePhoneHolder_agg");
	Map<String, Aggregation> aggregationMap = parsedNested.getAggregations().asMap();
	Avg avg = (Avg) aggregationMap.get("avg_carMobilePhoneHolder_price");
	double avgPrice = avg.getValue();
	System.out.println("汽车手机支架平均价格: " + avgPrice);
}

执行结果如下:

汽车手机支架平均价格: 10.0

可以看到我们已经使用嵌套聚合查询查出了所有的车载手机支架的平均价格,接下来我们只需要加入普通查询即可,代码如下:

public void nestedAvgAggregationCarHolderByModel() {
	NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
	queryBuilder.withQuery(QueryBuilders.matchQuery("model","su7"));
	queryBuilder.addAggregation(AggregationBuilders
			.nested("carMobilePhoneHolder_agg", "carMobilePhoneHolder")
			.subAggregation(AggregationBuilders.avg("avg_carMobilePhoneHolder_price").field("carMobilePhoneHolder.price"))
	);
	SearchHits<CarDO> search = elasticsearchRestTemplate.search(queryBuilder.build(), CarDO.class);
	List<CarDO> carList = search.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());
	Aggregations aggregations = search.getAggregations();
	assert aggregations != null;
	ParsedNested parsedNested = aggregations.get("carMobilePhoneHolder_agg");
	Map<String, Aggregation> aggregationMap = parsedNested.getAggregations().asMap();
	Avg avg = (Avg) aggregationMap.get("avg_carMobilePhoneHolder_price");
	double avgPrice = avg.getValue();
	System.out.println("小米su7汽车手机支架平均价格: " + avgPrice);
}

执行结果如下:

小米su7汽车手机支架平均价格: 9.0

执行结果符合预期。

以上我们就完成了某个车型下手机支架的平均费用的查询。

总结:本篇分享了使用 ElasticsearchRestTemplate 实现 Elasticsearch 的嵌套文档查询的方法,在这个过程中我也踩坑无数,慢慢摸索出来的,在此分享希望可以帮助到有需要的朋友,也希望朋友们在遇到程序报错的时候,能够不急不躁、胆大心细的去分析,一定可以找到错误的原因,并解决错误。

如有不正确的地方欢迎各位指出纠正。

要实现 Elasticsearch嵌套聚合查询,你可以使用 Elasticsearch 的 AggregationBuilders 类来创建嵌套聚合查询。具体实现步骤如下: 1. 创建一个嵌套聚合查询,用于组合多个子聚合查询。 2. 在嵌套聚合查询中添加多个子聚合查询,分别对应不同的聚合方式。 3. 如果你需要对子聚合查询进行分组,可以在子聚合查询中添加 terms 聚合查询。 4. 如果你需要对子聚合查询进行计数,可以在子聚合查询中添加 count 聚合查询。 5. 执行查询并处理结果。 以下是一个示例 Java 代码,用于实现 Elasticsearch嵌套聚合查询: ``` SearchResponse response = client.prepareSearch("index_name") .addAggregation( AggregationBuilders.nested("nested_agg", "nested_field") .subAggregation(AggregationBuilders.terms("term_agg") .field("term_field")) .subAggregation(AggregationBuilders.count("count_agg") .field("count_field"))) .execute() .actionGet(); Nested nestedAgg = response.getAggregations().get("nested_agg"); Terms termAgg = nestedAgg.getAggregations().get("term_agg"); long totalCount = nestedAgg.getAggregations().get("count_agg").getDocCount(); ``` 其中,"index_name" 是你要查询的索引名称,"nested_field" 是你要进行嵌套聚合查询的字段名称,"term_field" 和 "count_field" 分别是你要进行分组和计数的字段名称。你可以根据实际情况进行修改。执行完查询后,你可以从查询结果中获取嵌套聚合对象,并进一步获取子聚合对象的结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值