这次项目应客户要求使用7.4.2 highLevel-client版本,由于之前做的es搜索还是使用SpringData+Transport来操作,所以这次也是看了好久的官方api以及好多大神的笔记,但是由于是版本太高,使用的人可能或许太少或许大神是没时间写笔记记录,所以做的过程中也遇到了好多的问题和踩了好多的坑,所以现在记录一下自己做的过程,一方面希望能给大家提供一些帮助,一方面也算是对这方面的回顾,也希望能有大神提供一些建议和批评,互相讨论,共同进步。
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.4.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.10</version>
</dependency>
@RestController
public class EsController {
@Autowired
EsSearchService esSearchService;
@PostMapping("checkIndex")
//检查该index是否存在
public boolean checkIndex(String index){
return esSearchService.checkIndex(index);
}
@PostMapping("createIndex")
//创建index,可以自己手动输入创建,也可以写在代码中,我是直接写在代码中里,一方面是懒,也方便管理
public String createIndex(){
return esSearchService.createIndex();
}
@PostMapping("createMapping")
//创建mapping,可以用dts也可以自己写,我是自己用API写的,API创建的需要注意数据类型
public String createMapping(){
return esSearchService.createMapping();
}
@PostMapping("saveById")
//通过id新增
public int synchroEsDatas(@RequestBody List<EsSearchDTO> es){
return esSearchService.synchroEsDatas(es);
}
@PostMapping("saveAll")
//批量新增
public int save(){
return esSearchService.synchroEsDatasAll();
}
public class EsSearchServiceImp implements EsSearchService{
//如果是用的阿里云的Es服务器,可以在这里注入
@Value("${aliyun.es.username}")
private String username;
@Value("${aliyun.es.password}")
private String password;
@Value("${aliyun.es.hostname}")
private String hostname;
@Value("${aliyun.es.port}")
private int port;
@Value("${aliyun.es.protocol}")
private String protocol;
//获取es连接(不需要密码可以直接连接)
//private RestHighLevelClient restHighLevelClient = new RestHighLevelClient(RestClient
// .builder(new HttpHost(hostname, port, protocol)));
//使用密码连接(阿里云给的API)
public RestHighLevelClient getRestClient(){
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,new UsernamePasswordCredentials(username, password));
RestClientBuilder builder = RestClient.builder(new HttpHost(hostname, port, protocol))
.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
@Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpAsyncClientBuilder) {
return httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
}
}).setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
@Override
public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestBuilder) {
requestBuilder.setConnectTimeout(5000);
requestBuilder.setSocketTimeout(40000);
requestBuilder.setConnectionRequestTimeout(1000);
return requestBuilder;
}
});
RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder);
return restHighLevelClient;
}
@Resource
private EsSearchMapper esSearchMapper;
// @Async("taskExecutor") 加线程池自动同步
//异步同步所有数据
@Override
public Integer synchroEsDatasAll(){
int syncount = 0;
try {
//异步执行延迟5秒
Thread.sleep(5000);
//获取需要同步的记录
List<EsSearchEntity> list = esSearchMapper.selectESObject(null);
if(list != null && list.size() >0 && !list.isEmpty()){
log.info("ES同步开始");
//同步ES
syncount = this.saveDocumentByIndex(list);
log.info("ES同步完成");
} else {
log.info("未获取到ES同步记录集");
}
} catch (Exception e){
e.printStackTrace();
log.error("ES同步数据异常");
}
return syncount;
}
//删除数据
public void deleteUtil(String id) throws IOException {
Map<String,Object> _Map=new HashMap<String,Object>();
_Map.put("id", id);
DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(IndexData.indexName);
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
for (Map.Entry<String,Object> id:_Map.entrySet()){
boolQueryBuilder.must(QueryBuilders.termQuery(id.getKey(),id.getValue()));
}
deleteByQueryRequest.setQuery(boolQueryBuilder);
BulkByScrollResponse bulkByScrollResponse = getRestClient().deleteByQuery(deleteByQueryRequest,RequestOptions.DEFAULT);
bulkByScrollResponse.getDeleted();
}
@Override
//判断index是否存在
public boolean checkIndex(String index) {
try {
GetIndexRequest request = new GetIndexRequest(index);
boolean result = getRestClient().indices().exists(request, RequestOptions.DEFAULT);
return result;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
@Override
//创建index
public String createIndex() {
String result = "创建成功";
CreateIndexRequest createIndexRequest = new CreateIndexRequest(IndexData.indexName);
try {
CreateIndexResponse createIndexResponse = getRestClient().indices().create(createIndexRequest,RequestOptions.DEFAULT);
if (!createIndexResponse.isAcknowledged()){
result = "创建失败";
}else{
result = "索引已经存在";
}
} catch (IOException e) {
e.printStackTrace();
result = "接口异常";
}
return result;
}
@Override
//创建mapping
//注意数据格式,此版本已经取去除String格式,改为text和keyword格式,其中text格式支持分词和建立索引,支持模糊查询和精确查询,不支持聚合,keyword不支持分词,支持模糊查询和精确查询,支持聚合查询,排序
public String createMapping() {
String result = "mapping创建成功";
PutMappingRequest putMappingRequest = new PutMappingRequest(IndexData.indexName);
XContentBuilder builder = null;
try {
builder = XContentFactory.jsonBuilder()
.startObject()
.startObject("properties")
.startObject("id")
.field("type","keyword")
.field("index",true)
.endObject()
.startObject("pics")
.field("type","text")
.field("index",false)
.endObject()
.startObject("name")
.field("type","text")
.field("index",true)
//分词器采用ik_smart分词器
.field("analyzer","ik_smart")
.endObject()
.startObject("prices")
.field("type","double")
.field("index",true)
.endObject()
//可以按照城市排序,需要在其中再套一层,并且格式为keyword
.startObject("city")
.field("type","text")
.startObject("fields")
.startObject("raw")
.field("type","keyword")
.endObject()
.endObject()
.endObject()
//支持指定时间格式
.startObject("createTime")
.field("type","date")
.field("format","yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis")
.endObject()
.endObject()
.endObject();
} catch (IOException e) {
e.printStackTrace();
}
putMappingRequest.source(builder);
try {
AcknowledgedResponse putMappingResponse = getRestClient().indices().putMapping(putMappingRequest, RequestOptions.DEFAULT);
System.out.println(putMappingResponse);
if (!putMappingResponse.isAcknowledged()){
result = "接口执行失败";
}else {
result = "mapping“”已经存在";
}
} catch (IOException e) {
result = "mapping创建接口异常";
}
return result;
}
@Override
//将查询出的数据通过实体类映射到es中
//注意:字段映射必须和es中的一致,可以直接映射,也可以转换为json映射
public int saveDocumentByIndex(List<EsSearchEntity> esSearchList) {
BulkRequest bulkRequest = new BulkRequest();
Map<String,Object> map = new HashMap<>();
int count = 0;
for (EsSearchEntity esSearch:esSearchList){
String json = JSON.toJSONString(esSearch);
JSONObject jsonObject = JSONObject.parseObject(json);
Date date = new Date();
map.put("id",jsonObject.getString("id"));
map.put("pics",jsonObject.getString("pics"));
map.put("name",jsonObject.getString("name"));
map.put("prices",jsonObject.getBigDecimal("prices"));
map.put("city",jsonObject.getString("city"));
//时间采用当前时间
map.put("createTime",date);
bulkRequest.add(new IndexRequest(IndexData.indexName,IndexData.type,esSearch.getBid()).source(map));
bulkRequest.timeout("10m");
try {
BulkResponse bulkResponse = getRestClient().bulk(bulkRequest, RequestOptions.DEFAULT);
count ++;
} catch (IOException e) {
log.info(esSearch.getBid() + "同步失败");
e.printStackTrace();
}
}
log.info("es同步完成,更新总记录数:" + count);
return count;
}
}
@RestController
@RequestMapping("es")
@Api(tags = "ES搜索")
public class ESController {
@Autowired
EsSearchService esSearchService;
@PostMapping("search")
//queryData中为查询的字段、排序字段
//pageNum,pageSize是分页
public Map<String,List<Map<String, Object>>> search( QueryData queryData,int pageNum,int pageSize){
return esSearchService.searchProductByQuery(queryData,pageNum,pageSize);
}
}
@Override
public Map<String, List<Map<String, Object>>> searchProductByQuery(QueryData queryData,int pageNum,int pageSize) {
//searchRequest 是用于查询
SearchRequest searchRequest = new SearchRequest();
//searchRequest1 用于统计查询的总数,由于Es中设置了from和size之后就无法查询出总数,所以就加了一次查询,如果大家有好的办法请务必告知于我,非常感谢!!!!
SearchRequest searchRequest1 = new SearchRequest();
//现在的版本已经不支持指定type啦,可以去掉,因为现在的type都是_doc
searchRequest.indices(IndexData.indexName).types(IndexData.type);
//将searchSourceBuilder加入到searchRequest中
//由于好多的查询都用到了查询条件,所以我i直接将sourceBuilder进行了封装,用的时候可以直接调用,避免代码冗杂
searchRequest.source(sourceBuilder(queryData,pageNum,pageSize));
searchRequest1.source(sourceBuilderTotal(queryData));
//现在的Es的查询返回值都是map集合,需要用map来接
List<Map<String, Object>> listTotal = searchResult(searchRequest1);
同步执行
List<Map<String, Object>> list = searchResult(searchRequest);
Map<String, List<Map<String, Object>>> listMap = new HashMap<>();
//查询出来的值
listMap.put("List", List);
//查询出的总数
listMap.put("total",listTotal.size);
return listMap;
}
//用于查询
public SearchSourceBuilder sourceBuilder(QueryData queryData,int pageNum,int pageSize) {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//开始搜索的位置
searchSourceBuilder.from((pageNum-1)*pageSize);
//搜索结果的数量大小
searchSourceBuilder.size(pageSize);
//设置超时时间
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//按照名称查询
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", queryData.getQueryName());
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//嵌套查询,满足其一即可
boolQueryBuilder.should(matchQueryBuilder);
//按照价格区间排序
if (null != queryData.getMinPrices() && null != queryData.getMaxPrices()) {
RangeQueryBuilder queryBuilder = QueryBuilders
.rangeQuery("prices")
.from(queryData.getMinPrices())
.to(queryData.getMaxPrices());
searchSourceBuilder.query(queryBuilder);
}
if (StringUtils.equals("asc", queryData.getAsc())) {
//按照价格升序
searchSourceBuilder.sort(new FieldSortBuilder("prices")
.order(SortOrder.ASC));
} else if (StringUtils.equals("desc", queryData.getDesc())) {
//按照价格降序
searchSourceBuilder.sort(new FieldSortBuilder("prices")
.order(SortOrder.DESC));
} else if (StringUtils.isNotBlank(queryData.getSite())) {
//按照地域排序
searchSourceBuilder.sort(new FieldSortBuilder("city.raw")
.order(SortOrder.ASC));
}
//此为高亮字段,作为封装类方法在这里被引用
searchSourceBuilder.highlighter(highlight());
//需要查询出来的字段
String[] includeFields = new String[]{"id", "name", "city", "pics", "createTime"};
//不需要的字段
String[] excludeFields = new String[]{""};
searchSourceBuilder.fetchSource(includeFields, excludeFields);
searchSourceBuilder.query(boolQueryBuilder);
return searchSourceBuilder;
}
//获取总页数
public SearchSourceBuilder sourceBuilderTotal(QueryData queryData) {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//开始搜索的位置
searchSourceBuilder.from(0);
//搜索结果的数量大小
searchSourceBuilder.size(10000);
//按照商品/服务/解决方案名称查询
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("pname", queryData.getQueryName());
uilder boolQueryBuilder = QueryBuilders.boolQuery();
//嵌套查询,满足其一即可
boolQueryBuilder.should(matchQueryBuilder);
searchSourceBuilder.query(boolQueryBuilder);
return searchSourceBuilder;
}
public HighlightBuilder highlight() {
//设置高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
//设置高亮的前缀和后缀
String preTags = "";
String postTags = "";
highlightBuilder.preTags(preTags);
highlightBuilder.postTags(postTags);
HighlightBuilder.Field pname = new HighlightBuilder.Field("name");
highlightBuilder.field(name);
return highlightBuilder;
}
public List<Map<String, Object>> searchResult(SearchRequest searchRequest) {
List<Map<String, Object>> list = new ArrayList<>();
List<String> fieldList = new ArrayList();
//需要高亮的字段
fieldList.add("pname");
try {
SearchResponse searchResponse = getRestClient().search(searchRequest, RequestOptions.DEFAULT);
RestStatus status = searchResponse.status();
log.info("状态:" + status);
TimeValue took = searchResponse.getTook();
log.info("执行时间:" + took);
Boolean terminatedEarly = searchResponse.isTerminatedEarly();
log.info("请求终止:" + terminatedEarly);
boolean timedOut = searchResponse.isTimedOut();
log.info("超时时间:" + timedOut);
//获得响应的文档
SearchHits hits = searchResponse.getHits();
//迭代取出数据
for (SearchHit hit : hits) {
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
for (String field : fieldList()) {
String keyValue = (String) sourceAsMap.get(field);
HighlightField highlightFieldValue = hit.getHighlightFields().get(field);
if (highlightFieldValue == null) {
sourceAsMap.put(field, keyValue);
} else {
String highLightContent = highlightFieldValue.getFragments()[0].toString();
sourceAsMap.put(field, highLightContent);
}
}
list.add(sourceAsMap);
}
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
个人觉得自己看着API写虽然慢但是可以避免好多的版本冲突(因为现在网上大神分享的博客很多都是老版本的),而且还可以提升英语能力!
以上就是自己写的es查询,希望能对大家有所帮助!!!有好的建议或者批评,也欢迎大家留言或者与我联系!!!