本文已参与「新人创作礼」活动,一起开启掘金创作之路
1. 引入依赖
- 这里使用springboot 2.5.4 版本,es使用 7.14.0 版本
- 可以参考es 官方文档 ,在Java REST Client 下找到对应的版本
- 这里使用7.14.0的 Java High Level REST Client
- 右下角有详细的使用说明
- 需要同时使用elasticsearch和elasticsearch-rest-high-level-client
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- es-->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.14.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.14.0</version>
</dependency>
<!-- fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.57</version>
</dependency>
<!-- Swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2. 添加配置文件
#es
elasticsearch.host=192.168.42.111
elasticsearch.port=9200
#jackson
spring.jackson.default-property-inclusion=non_null
3. 创建ES配置类
package com.example.demo.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ESConfig {
@Value("${elasticsearch.host}")
private String host;
@Value("${elasticsearch.port}")
private int port;
@Bean
public RestHighLevelClient highLevelClient() {
HttpHost httpHost = new HttpHost(host, port, "http");
// 如果是集群模式,可以添加HttpHost数组
RestClientBuilder restClientBuilder = RestClient.builder(httpHost);
return new RestHighLevelClient(restClientBuilder);
}
}
4. swagger配置类
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
.apis(RequestHandlerSelectors.basePackage("com.example.demo")).build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().version("1.0").build();
}
}
5. 消息返回体
- 用来包装返回数据
package com.example.demo.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AjaxResult<T> {
private Integer code;
private String message;
private T data;
public static <T> AjaxResult<T> ok(T data) {
return new AjaxResult<>(0, "success", data);
}
public static <T> AjaxResult<T> error(T data) {
return new AjaxResult<>(1, "error", data);
}
}
6. 创建保存文档的实体
package com.example.demo.vo;
import lombok.Data;
@Data
public class User {
private String firstName;
private String secondName;
private String content;
private Integer age;
}
7. 索引操作service
- 这里对索引进行创建,判断索引是否存在和删除索引
package com.example.demo.service;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
@Service
public class OperateIndex {
@Resource
private RestHighLevelClient restHighLevelClient;
public boolean createIndex(String indexName) {
boolean acknowledged = false;
try {
/**
* 可以根据需要设置字段属性,如果不设置,es会根据添加文档时的字段类型自动推断
* put /{indexName}/_mapping
* {
* "properties": {
* "firstName": {
* "type": "keyword"
* },
* "secondName": {
* "type": "keyword"
* },
* "age": {
* "type": "integer"
* },
* "content": {
* "type": "text"
* }
* }
* }
*/
XContentBuilder xContentBuilder = XContentFactory.jsonBuilder()
.startObject()
.field("properties").startObject()
.field("firstName").startObject().field("type", "keyword").endObject()
.field("secondName").startObject().field("type", "keyword").endObject()
.field("age").startObject().field("type", "integer").endObject()
.field("content").startObject().field("type", "text").endObject()
.endObject()
.endObject();
CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName);
createIndexRequest.mapping(xContentBuilder);
CreateIndexResponse createIndexResponse =
restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
acknowledged = createIndexResponse.isAcknowledged();
} catch (IOException e) {
e.printStackTrace();
}
return acknowledged;
}
public boolean isIndexExists(String indexName) {
GetIndexRequest getIndexRequest = new GetIndexRequest(indexName);
getIndexRequest.humanReadable(true);
boolean exists = false;
try {
exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
return exists;
}
public boolean deleteIndex(String indexName) {
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexName);
// 忽略索引不存在的情况;如果不设置,索引不存在时,会报错
deleteIndexRequest.indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN);
boolean acknowledged = false;
try {
AcknowledgedResponse delete =
restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
acknowledged = delete.isAcknowledged();
} catch (IOException e) {
e.printStackTrace();
}
return acknowledged;
}
}
8. 创建索引controller
package com.example.demo.controller;
import com.example.demo.service.OperateIndex;
import com.example.demo.vo.AjaxResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/index")
public class IndexController {
@Resource
private OperateIndex operateIndex;
/*
* 创建索引
*/
@PostMapping("/create")
public AjaxResult<Boolean> createIndex(@RequestParam String indexName) {
return AjaxResult.ok(operateIndex.createIndex(indexName));
}
/**
* 索引是否存在
*/
@PostMapping("/exit")
public AjaxResult<Boolean> indexExit(@RequestParam String indexName) {
return AjaxResult.ok(operateIndex.isIndexExists(indexName));
}
/**
* 删除索引
*/
@PostMapping("/delete")
public AjaxResult<Boolean> deleteIndex(@RequestParam String indexName) {
return AjaxResult.ok(operateIndex.deleteIndex(indexName));
}
}
9. 测试索引操作
9.1 创建索引
- 查看是否创建成功
9.2 判断索引是否存在
9.3 删除索引
- 再次查看索引是否存在
10. 文档操作service
package com.example.demo.service;
import com.alibaba.fastjson.JSON;
import com.example.demo.vo.AjaxResult;
import com.example.demo.vo.User;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.get.GetResult;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.Objects;
@Service
public class OperateDoc {
@Resource
private RestHighLevelClient restHighLevelClient;
public AjaxResult<String> insertDoc(User user, String indexName, String docId) {
IndexRequest indexRequest = new IndexRequest(indexName);
//设置文档id,如果不设置id,es会自动生成全局唯一的id
indexRequest.id(docId);
//设置文档数据和格式
indexRequest.source(JSON.toJSONString(user), XContentType.JSON);
try {
//获取返回的response
IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
if (Objects.nonNull(indexResponse)) {
String id = indexResponse.getId();
DocWriteResponse.Result result = indexResponse.getResult();
// 如果设置的id不存在,则新增文档;如果id已存在,则覆盖文档
if (Objects.equals(result, DocWriteResponse.Result.CREATED)) {
return new AjaxResult<>(0, "新增文档成功!", id);
} else if (Objects.equals(result, DocWriteResponse.Result.UPDATED)) {
return new AjaxResult<>(0, "覆盖文档成功!", id);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public AjaxResult<String> getDoc(String indexName, String docId) {
GetRequest getRequest = new GetRequest(indexName, docId);
try {
GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
if (getResponse.isExists()) {
return AjaxResult.ok(getResponse.getSourceAsString());
} else {
return AjaxResult.error("文档不存在!");
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public AjaxResult<String> updateDoc(String indexName, String docId, String fieldName,String fieldValue) {
try {
XContentBuilder xContentBuilder = XContentFactory.jsonBuilder();
xContentBuilder.startObject();
xContentBuilder.field(fieldName, fieldValue);
xContentBuilder.endObject();
UpdateRequest updateRequest = new UpdateRequest(indexName, docId);
updateRequest.doc(xContentBuilder);
//id不存在则添加文档
updateRequest.docAsUpsert(true);
//在应答里包含当前文档的内容
updateRequest.fetchSource(true);
UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
GetResult getResult = updateResponse.getGetResult();
if (getResult.isExists()) {
return AjaxResult.ok(getResult.sourceAsString());
} else {
return AjaxResult.error("更新文档失败!");
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public AjaxResult<String> deleteDoc(String indexName, String docId) {
DeleteRequest deleteRequest = new DeleteRequest(indexName, docId);
try {
DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
if (Objects.equals(deleteResponse.getResult(), DocWriteResponse.Result.DELETED)) {
return AjaxResult.ok("文档删除成功!");
} else if (Objects.equals(deleteResponse.getResult(), DocWriteResponse.Result.NOT_FOUND)) {
return AjaxResult.error("文档不存在");
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
11. 创建文档controller
package com.example.demo.controller;
import com.example.demo.service.OperateDoc;
import com.example.demo.vo.AjaxResult;
import com.example.demo.vo.User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/doc")
public class DocController {
@Resource
private OperateDoc operateDoc;
/**
* 插入文档
*/
@PostMapping("/insert")
public AjaxResult<String> insertDoc(@RequestBody User user, @RequestParam String indexName,
@RequestParam String docId) {
return operateDoc.insertDoc(user, indexName, docId);
}
/**
* 查询文档
*/
@PostMapping("/query")
public AjaxResult<String> getDoc(@RequestParam String indexName, @RequestParam String docId) {
return operateDoc.getDoc(indexName, docId);
}
/**
* 更新文档
*/
@PostMapping("/updata")
public AjaxResult<String> updateDoc(@RequestParam String indexName, @RequestParam String docId,
@RequestParam String fieldName, @RequestParam String fieldValue) {
return operateDoc.updateDoc(indexName, docId, fieldName, fieldValue);
}
/**
* 删除文档
*/
@PostMapping("/delete")
public AjaxResult<String> deleteDoc(@RequestParam String indexName, @RequestParam String docId) {
return operateDoc.deleteDoc(indexName, docId);
}
}
12. 测试文档操作
12.1 插入文档
12.2 查询文档
12.3 更新文档
12.4 删除文档
13. search操作service
package com.example.demo.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.vo.AjaxResult;
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.BoolQueryBuilder;
import org.elasticsearch.index.query.FuzzyQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
@Service
public class NormalSearch {
@Resource
private RestHighLevelClient restHighLevelClient;
/**
* 处理返回结果中的hits部分
*/
public AjaxResult<JSONArray> send(String indexName, SearchSourceBuilder searchSourceBuilder) {
try {
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices(indexName);
searchRequest.source(searchSourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = search.getHits();
JSONArray jsonArray = new JSONArray();
for (SearchHit hit : hits) {
String src = hit.getSourceAsString();
JSONObject jsonObject = JSON.parseObject(src);
jsonArray.add(jsonObject);
}
return AjaxResult.ok(jsonArray);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* _search接口基本用法
*/
public AjaxResult<JSONArray> searchExample(String indexName) {
/**
* 拼接查询条件
* get kibana_sample_data_flights/_search
* {
* "from":0,
* "size":5,
* "query":{
* "match_all":{}
* },
* "_source":["Origin*","*Weather"],
* "sort":[{"DistanceKilometers":"asc"},{"FlightNum":"desc"}]
* }
*/
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(0);
searchSourceBuilder.size(5);
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
String[] includeFields = new String[]{"Origin*", "*Weather"};
searchSourceBuilder.fetchSource(includeFields, null);
searchSourceBuilder.sort(new FieldSortBuilder("DistanceKilometers").order(SortOrder.ASC));
searchSourceBuilder.sort(new FieldSortBuilder("FlightNum").order(SortOrder.DESC));
return send(indexName, searchSourceBuilder);
}
/**
* 基于词项的查询
*/
public AjaxResult<JSONArray> termsSearch(String indexName) {
/**
* get kibana_sample_data_flights/_search
* {
* "query":{
* "term":{
* "dayOfWeek":3
* }
* }
* }
*/
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("dayOfWeek", 3);
searchSourceBuilder.query(termQueryBuilder);
return send(indexName, searchSourceBuilder);
}
/**
* 基于全文的查询
*/
public AjaxResult<JSONArray> matchSearch(String indexName) {
/**
* POST /kibana_sample_data_flights/_search
* {
* "query": {
* "multi_match": {
* "query":"AT",
* "fields":["DestCountry", "OriginCountry"]
* }
* }
* }
*/
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.multiMatchQuery("AT", "DestCountry", "OriginCountry"));
return send(indexName, searchSourceBuilder);
}
/**
* 基于全文的模糊查询
*/
public AjaxResult<JSONArray> fuzzySearch(String indexName) {
/**
* get kibana_sample_data_logs/_search
* {
* "query": {
* "fuzzy": {
* "message": {
* "value": "firefix",
* "fuzziness": "1"
* }
* }
* }
* }
*/
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("message", "firefix");
fuzzyQueryBuilder.fuzziness(Fuzziness.ONE);
searchSourceBuilder.query(fuzzyQueryBuilder);
return send(indexName, searchSourceBuilder);
}
/**
* 组合查询范例
*/
public AjaxResult<JSONArray> boolSearch(String indexName) {
/**
* POST /kibana_sample_data_logs/_search
* {
* "query": {
* "bool": {
* "must":[
* {"match": { "message": "firefox"} }
* ],
* "should":[
* {"term": { "geo. src": "CN"}},
* {"term": { "geo. dest": "CN"}}
* ]
* }
* }
* }
*/
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.matchQuery("message", "firefox"))
.should(QueryBuilders.termQuery("geo.src", "CN"))
.should(QueryBuilders.termQuery("geo.dest", "CN"));
searchSourceBuilder.query(boolQueryBuilder);
return send(indexName, searchSourceBuilder);
}
}
14. search操作controller
package com.example.demo.controller;
import com.alibaba.fastjson.JSONArray;
import com.example.demo.service.NormalSearch;
import com.example.demo.vo.AjaxResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/search")
public class SearchController {
private final static String KIBANA_SAMPLE_DATA_FLIGHTS = "kibana_sample_data_flights";
private final static String KIBANA_SAMPLE_DATA_LOGS = "kibana_sample_data_logs";
@Resource
private NormalSearch normalSearch;
/**
* _search接口基本用法
*/
@PostMapping("/example")
public AjaxResult<JSONArray> searchExample() {
return normalSearch.searchExample(KIBANA_SAMPLE_DATA_FLIGHTS);
}
/**
* 基于词项的查询
*/
@PostMapping("/term")
public AjaxResult<JSONArray> termsSearch() {
return normalSearch.termsSearch(KIBANA_SAMPLE_DATA_FLIGHTS);
}
/**
* 基于全文的查询
*/
@PostMapping("/match")
public AjaxResult<JSONArray> matchSearch() {
return normalSearch.matchSearch(KIBANA_SAMPLE_DATA_FLIGHTS);
}
/**
* 基于全文的模糊查询
*/
@PostMapping("/fuzzy")
public AjaxResult<JSONArray> fuzzySearch() {
return normalSearch.fuzzySearch(KIBANA_SAMPLE_DATA_LOGS);
}
/**
* 组合查询范例
*/
@PostMapping("/combination")
public AjaxResult<JSONArray> combinationSearch() {
return normalSearch.boolSearch(KIBANA_SAMPLE_DATA_LOGS);
}
}
15. 测试search接口操作
- 测试之前需要提前导入es提供的样例数据kibana_sample_data_flights和kibana_sample_data_logs,参考数据检索和分析
15.1 _search接口基本用法
15.2 基于词项的查询
15.3 基于全文的查询
15.4 基于全文的模糊查询
15.5 组合查询
16. 聚集操作service
package com.example.demo.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.vo.AjaxResult;
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.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.metrics.ParsedAvg;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
@Service
public class AggsSearch {
@Resource
private RestHighLevelClient restHighLevelClient;
/**
* 处理返回结果中的hits和聚集
*/
public AjaxResult<JSONArray> send(String indexName, SearchSourceBuilder searchSourceBuilder) {
try {
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices(indexName);
searchRequest.source(searchSourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = search.getHits();
JSONArray jsonArray = new JSONArray();
//hits部分
for (SearchHit hit : hits) {
String src = hit.getSourceAsString();
JSONObject jsonObject = JSON.parseObject(src);
jsonArray.add(jsonObject);
}
//聚集部分
Aggregations aggregations = search.getAggregations();
for (Aggregation aggregation : aggregations) {
String jsonString = JSON.toJSONString(aggregation);
jsonArray.add(JSON.parseObject(jsonString));
//这里可以拿到具体的桶聚集,做特殊处理
List<? extends Histogram.Bucket> buckets = ((Histogram) aggregation).getBuckets();
for (Histogram.Bucket bucket : buckets) {
System.out.println("--------------------------------------");
System.out.println(bucket.getKeyAsString());
System.out.println(bucket.getDocCount());
ParsedAvg parsedAvg = (ParsedAvg) bucket.getAggregations().getAsMap().get("avg_price");
System.out.println(parsedAvg.getValueAsString());
}
}
return AjaxResult.ok(jsonArray);
} catch (IOException e) {
e.printStackTrace();
}
return AjaxResult.error(null);
}
/**
* 聚集查询
*/
public AjaxResult aggsExampleSearch(String indexName) {
/**
* high level client不能使用过滤条件filter_path,如果要使用,只能使用low level client
* POST /kibana_sample_data_flights/_search?filter_path=aggregations
* {
* "query": {
* "term": {"OriginCountry": "CN"}
* },
* "aggs":
* {
* "date_price_histogram": {
* "date_histogram": {
* "field": "timestamp",
* "fixed_interval": "30d"
* },
* "aggs": {
* "avg_price": {"avg": {"field": "FlightDelayMin"}}
* }
* }
* }
* }
*/
// 拼接query部分
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.termQuery("OriginCountry", "CN"));
//拼接聚集部分
DateHistogramAggregationBuilder date_price_histogram
= AggregationBuilders.dateHistogram("date_price_histogram");
date_price_histogram.field("timestamp").fixedInterval(DateHistogramInterval.days(30));
//嵌套聚集部分
date_price_histogram.subAggregation(AggregationBuilders.avg("avg_price").field("FlightDelayMin"));
searchSourceBuilder.aggregation(date_price_histogram);
return send(indexName, searchSourceBuilder);
}
}
17. 聚集查询controller
package com.example.demo.controller;
import com.alibaba.fastjson.JSONArray;
import com.example.demo.service.AggsSearch;
import com.example.demo.vo.AjaxResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/aggs")
public class AggsController {
private final static String KIBANA_SAMPLE_DATA_FLIGHTS = "kibana_sample_data_flights";
@Resource
private AggsSearch aggsSearch;
/**
* 聚集查询
*/
@PostMapping("/query")
public AjaxResult<JSONArray> aggsQuery() {
return aggsSearch.aggsExampleSearch(KIBANA_SAMPLE_DATA_FLIGHTS);
}
}
18. 测试聚集查询
- 直接使用kibana查询的结果
- 接口调用结果
- 控制台打印