ElasticSearch7.4.2:RestHighLevelClient應用


ElasticSearch7.4.2:RestHighLevelClient
這次項目應客戶要求使用7.4.2 highLevel-client版本,由於之前做的es搜索還是使用SpringData+Transport來操作,所以這次也是看了好久的官方api以及好多大神的筆記,但是由於是版本太高,使用的人可能或許太少或許大神是沒時間寫筆記記錄,所以做的過程中也遇到了好多的問題和踩了好多的坑,所以現在記錄一下自己做的過程,一方面希望能給大家提供一些幫助,一方面也算是對這方面的回顧,也希望能有大神提供一些建議和批評,互相討論,共同進步。

一:pom依賴

<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>

二:后台創建index,mapping和同步數據
一:controller層

@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;
    }
}

二:前台執行查詢、分頁、高亮
一:controller層

@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;
}




封裝的sourceBuilder

//用於查詢
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 = "<em class='titleColor' style='color: #f60'>";
    String postTags = "</em>";
    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;
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM