Springboot2.2.9接入阿里雲ES(帶高亮查詢)


最近比較忙,好久沒更新博客了,今天抽個空記錄一下使用springboot接入阿里雲ES並帶模糊高亮查詢功能,閑話不多說,上干活。

引入POM依賴:

        <!-- ES config 接入阿里雲ES -->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.4.0</version><!--$NO-MVN-MAN-VER$-->
            <exclusions>
                <exclusion>
                    <groupId>org.elasticsearch</groupId>
                    <artifactId>elasticsearch</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.elasticsearch.client</groupId>
                    <artifactId>elasticsearch-rest-client</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-client</artifactId>
            <version>7.4.0</version><!--$NO-MVN-MAN-VER$-->
        </dependency>
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>7.4.0</version><!--$NO-MVN-MAN-VER$-->
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.7</version><!--$NO-MVN-MAN-VER$-->
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.7</version><!--$NO-MVN-MAN-VER$-->
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.6.1</version><!--$NO-MVN-MAN-VER$-->
        </dependency>

properties文件內容添加如下:

## ES config
spring.elasticsearch.username=
spring.elasticsearch.password=
spring.elasticsearch.cluster_host=IP地址 或 阿里雲內網域名 或 公網域名
spring.elasticsearch.cluster_port=9200
spring.es.env.flag=sit

創建實體類:

package com.xx.xx.es.entity;

import org.springframework.data.annotation.Id;
import lombok.Data;

/**
 * @author Jimmy Shan
 * @date 2020-11-26
 * @desc 示例DEMO 數據存儲ES
 */
@Data
public class CusDemoInfoDocument {
    @Id
    private String id;// 主鍵ID
    private String demoName;// 名稱
    private String demoCode;// 編碼
    private String demoValue;//

    public CusDemoInfoDocument() {
    }

    public CusDemoInfoDocument(String id, String demoName, String demoCode, String demoValue) {
        this.id = id;
        this.demoName = demoName;
        this.demoCode = demoCode;
        this.demoValue = demoValue;
    }

    public CusDemoInfoDocument(String id) {
        this.id = id;
    }
}

創建工具類:

package com.xx.xx.common.util;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.HttpAsyncResponseConsumerFactory;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
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.XContentType;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.xx.xx.common.Constant;
import com.xx.xx.es.entity.CusDemoInfoDocument;

/**
 * @author Jimmy Shan
 * @date 2020-11-30
 * @desc aliyun ES tools
 */
@Component
public class ElasticsearchUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(ElasticsearchUtil.class);
    private static final RequestOptions COMMON_OPTIONS;
    @Value("${spring.elasticsearch.cluster_host}")
    private String clusterHost;
    @Value("${spring.elasticsearch.cluster_port}")
    private Integer clusterPort;
    @Value("${spring.elasticsearch.username}")
    private String username;
    @Value("${spring.elasticsearch.password}")
    private String password;

    static {
        RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
        // 默認緩存限制為100MB,此處修改為30MB。
        builder.setHttpAsyncResponseConsumerFactory(
                new HttpAsyncResponseConsumerFactory
                        .HeapBufferedResponseConsumerFactory(30 * 1024 * 1024));
        COMMON_OPTIONS = builder.build();
    }

    /**
     * @desc 獲取ES client
     */
    public RestHighLevelClient getEsClient() {
        Map<String, RestHighLevelClient> esClientMap = Constant.esClientMap;
        if (esClientMap == null || esClientMap.isEmpty()) {
            // 阿里雲Elasticsearch集群需要basic auth驗證。
            final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            //訪問用戶名和密碼,創建阿里雲Elasticsearch實例時設置的用戶名和密碼,也是Kibana控制台的登錄用戶名和密碼。
            credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(this.username, this.password));
            // 通過builder創建rest client,配置http client的HttpClientConfigCallback。
            // 單擊所創建的Elasticsearch實例ID,在基本信息頁面獲取公網地址,即為ES集群地址。
            RestClientBuilder builder = RestClient.builder(new HttpHost(this.clusterHost, this.clusterPort))
                    .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
                        @Override
                        public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
                            return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                        }
                    });
            // RestHighLevelClient實例通過REST low-level client builder進行構造。
            RestHighLevelClient highClient = new RestHighLevelClient(builder);
            esClientMap.put(Constant.ES_CLIENT, highClient);
        }
        return esClientMap.get(Constant.ES_CLIENT);
    }

    /**
     * @desc 創建索引
     */
    public void createIndex(String index) throws IOException {
        if(!existsIndex(index)) {
            CreateIndexRequest request = new CreateIndexRequest(index);
            CreateIndexResponse createIndexResponse = getEsClient().indices().create(request, COMMON_OPTIONS);
            LOGGER.info("createIndex: {}", JsonWare.beanToJson(createIndexResponse));
        }       
    }

    /**
     * @desc 判斷索引是否存在
     */
    public boolean existsIndex(String index) throws IOException {
        GetIndexRequest request = new GetIndexRequest(index);
        boolean exists = getEsClient().indices().exists(request, COMMON_OPTIONS);
        LOGGER.info("existsIndex: {}", exists);
        return exists;
    }

    /**
     * @desc 判斷記錄是否存在
     */
    public boolean exists(String index, String type, CusDemoInfoDocument document) throws IOException {
        GetRequest getRequest = new GetRequest(index, type, document.getId());
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        getRequest.storedFields("_none_");
        boolean exists = getEsClient().exists(getRequest, COMMON_OPTIONS);
        LOGGER.info("exists: {}", exists);
        return exists;
    }

    /**
     * @desc 刪除索引
     */
    public boolean deleteIndex(String index) {
        try {
            DeleteIndexRequest request = new DeleteIndexRequest(index);
            request.indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN);
            AcknowledgedResponse deleteIndexResponse = getEsClient().indices().delete(request, RequestOptions.DEFAULT);
            return deleteIndexResponse.isAcknowledged();
        } catch (Exception e) {
            LOGGER.error("刪除索引失敗, index:{}", index);
            return false;
        }
    }

    /**
     * @desc 添加數據
     */
    public String addData(CusDemoInfoDocument document, String index, String type, String docId) {
        String id = null;
        try {
            //index_name為索引名稱;type_name為類型名稱,7.0及以上版本必須為_doc;doc_id為文檔的id。
            // 同步執行,並使用自定義RequestOptions(COMMON_OPTIONS)。
            RestHighLevelClient highClient = getEsClient();
            IndexRequest request = new IndexRequest(index, type)
                    .id(docId).source(JsonWare.beanToJson(document), XContentType.JSON);
            request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);// 立即刷新
            IndexResponse response = highClient.index(request, COMMON_OPTIONS);
            id = response.getId();
            //highClient.close();
            LOGGER.info("索引:{}, 數據添加, 返回碼:{}, id:{}", index, response.status().getStatus(), id);
        } catch (IOException e) {
            LOGGER.error("添加數據失敗, index:{}, id:{}", index, id);
        }

        return id;
    }

    /**
     * @desc 修改數據
     */
    public String updateData(CusDemoInfoDocument document, String index, String type, String docId) {
        String id = null;
        try {
            RestHighLevelClient highClient = getEsClient();
            UpdateRequest request = new UpdateRequest(index, type, docId)
                    .doc(JsonWare.beanToJson(document), XContentType.JSON);
            request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
            UpdateResponse response = highClient.update(request, COMMON_OPTIONS);
            id = response.getId();
            //highClient.close();
            LOGGER.info("數據更新, 返回碼:{}, id:{}", response.status().getStatus(), id);
        } catch (IOException e) {
            LOGGER.error("數據更新失敗, index:{}, id:{}", index, id);
        }
        return id;
    }

    /**
     * @desc 批量插入數據
     */
    public boolean insertBatch(String index, String type, List<CusDemoInfoDocument> list) {
        BulkRequest request = new BulkRequest();
        request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
        list.forEach(item -> request.add(new IndexRequest(index, type)
                .id(item.getId()).source(JsonWare.beanToJson(item), XContentType.JSON)));
        try {
            RestHighLevelClient highClient = getEsClient();
            BulkResponse bulk = highClient.bulk(request, COMMON_OPTIONS);
            bulk.status().getStatus();
            //highClient.close();
            LOGGER.info("索引:{}, 批量插入 {} 條數據成功!", index, list.size());
        } catch (IOException e) {
            LOGGER.error(e.getMessage());
            LOGGER.error("索引:{}, 批量插入數據失敗", index);
            return false;
        }

        return true;
    }

    /**
     * @desc 根據id刪除數據
     */
    public boolean deleteById(String index, String type, String docId) {
        DeleteRequest request = new DeleteRequest(index, type, docId);
        request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
        try {
            DeleteResponse response = getEsClient().delete(request, COMMON_OPTIONS);
            response.status().getStatus();
            LOGGER.info("索引:{}, 根據id {} 刪除數據:{}", index, docId, JsonWare.beanToJson(response));
        } catch (Exception e) {
            LOGGER.error("根據id刪除數據失敗, index:{}, id:{}", index, docId);
            return false;
        }

        return true;
    }
}

看下Constant類內容:

/**
 * @author Jimmy Shan
 * @date 2020-08-07
 * @desc 常量類
 */
public class Constant {
    public static final Map<String, RestHighLevelClient> esClientMap = new HashMap<>();
    public static final String ES_CLIENT = "esClient";
    // ES查詢限制數量
    public static final Integer ES_QUERY_PAGE_START = 0;
    public static final Integer ES_QUERY_COUNT = 20;

}

 

以上只是一些基本操作方式,下面 重點記錄下 模糊高亮查詢方法

本人這里自定義一個service服務,用來處理高亮查詢

Service

package com.xx.xx.service;

import java.util.List;
import com.xx.xx.es.entity.CusDemoInfoDocument;/**
 * @author Jimmy Shan
 * @date 2020-09-28
 * @desc ES服務
 */
public interface EsService {
    /**
     * @desc 高連顯示查詢-DEMO
     */
    List<CusDemoInfoDocument> searchHighLightDocForDemo(String contents, String index, String type);
}

Service實現

    /**
     * @desc 高連顯示查詢-DEMO
     */
    @Override
    public List<CusDemoInfoDocument> searchHighLightDocForDemo(String contents,
            String index, String type) {
        try {
            // 獲取鏈接RestHighLevelClient對象
            RestHighLevelClient highClient = esUtil.getEsClient();
            // 查詢維度設置
            BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
            // 查詢列,此處支持多列查詢
            String[] field = {"id", "demoName", "demoCode", "demoValue"};
            // 多列匹配規則
            queryBuilder.must(QueryBuilders.multiMatchQuery(contents, field));

            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            sourceBuilder.query(queryBuilder);
            sourceBuilder.from(Constant.ES_QUERY_PAGE_START);// 分頁起始位,下標從0開始
            sourceBuilder.size(Constant.ES_QUERY_COUNT);// 每次20條記錄

            //設置高亮顯示
            HighlightBuilder highlightBuilder = new HighlightBuilder()
                    .field("*").requireFieldMatch(false);
            highlightBuilder.preTags("<font color='#dd4b39'>");
            highlightBuilder.postTags("</font>");
            sourceBuilder.highlighter(highlightBuilder);

            SearchRequest searchRequest = new SearchRequest(index);
            searchRequest.types(type);
            searchRequest.source(sourceBuilder);
            SearchResponse response = highClient.search(searchRequest, RequestOptions.DEFAULT);
            LOGGER.info("highClient response : {}", JsonWare.beanToJson(response));

            // 遍歷結果
            List<CusDemoInfoDocument> list = new ArrayList<>();
            for(SearchHit hit : response.getHits()) {
                if (response.getHits().getHits().length <= 0) {
                    return null;
                }

                CusDemoInfoDocument cusDoc = JsonWare.jsonToBean(hit.getSourceAsString(),
                        CusDemoInfoDocument.class);
                // 處理高亮片段
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField nameField = highlightFields.get("demoValue");
                if(nameField != null){
                    Text[] fragments = nameField.fragments();
                    String fragmentString = fragments[0].string();
                    cusDoc.setDemoValue(fragmentString);
                }
                list.add(cusDoc);
            }

            if (!CollectionUtils.isEmpty(list)) {
                return list;
            }
            return null;
        } catch (Exception e) {
            LOGGER.error("searchHighLightDocForDemo happen exception: {}", e.getMessage(), e);
            return null;
        }
    }

好了,主要方法都已完成,現在讓我們寫個測試類試一試。

ControllerTest

@Controller
@RequestMapping("/api")
public class TestController extends AbstractController{

    @Autowired
    private EsService esService;
    @Autowired
    private ElasticsearchUtil esUtil;

    /**
     * @author Jimmy Shan
     * @date 2020-11-26
     * @desc 測試ES保存
     */
    @ResponseBody
    @RequestMapping(value = "/demoEsSaveTest", method = RequestMethod.POST)
    public String demoEsSaveTest(HttpServletRequest request, HttpServletResponse response) {
        String id = request.getParameter("id");
        String demoName = request.getParameter("demoName");
        String demoCode = request.getParameter("demoCode");
        String demoValue = request.getParameter("demoValue");
String index = "cusdemoinfoidx"; String type = "cusDemoInfo"; esUtil.addData(new CusDemoInfoDocument( id, demoName, demoCode, demoValue), index, type, id); LOGGER.info("TestController --> demoEsSaveTest"); return "The data of ES was save success."; } /** * @author Jimmy Shan * @date 2020-11-26 * @desc 測試ES讀取 */ @ResponseBody @RequestMapping(value = "/demoEsGetTest", method = RequestMethod.POST) public List<CusDemoInfoDocument> demoEsGetTest(HttpServletRequest request, HttpServletResponse response) { String demoValue = request.getParameter("demoValue"); String index = "cusdemoinfoidx"; String type = "cusDemoInfo"; List<CusDemoInfoDocument> demoList = esService.searchHighLightDocForDemo(demoValue, index, type); LOGGER.info("TestController --> demoEsGetTest result ==> {}", JsonWare.beanToJson(demoList)); return demoList; } /** * @author Jimmy Shan * @date 2020-11-26 * @desc 測試ES 根據ID刪除 */ @ResponseBody @RequestMapping(value = "/demoEsDelTest", method = RequestMethod.POST) public String demoEsDelTest(HttpServletRequest request, HttpServletResponse response) { String id = request.getParameter("id"); String index = "cusdemoinfoidx"; String type = "cusDemoInfo"; esUtil.deleteById(index, type, id); LOGGER.info("TestController --> demoEsDelTest"); return "Delete Success."; } /** * @author Jimmy Shan * @date 2020-11-30 * @desc 測試ES 根據ID更新 */ @ResponseBody @RequestMapping(value = "/demoEsUpdateByIdTest", method = RequestMethod.POST) public String demoEsUpdateByIdTest(HttpServletRequest request, HttpServletResponse response) { String id = request.getParameter("id"); String index = "cusdemoinfoidx"; String type = "cusDemoInfo"; esUtil.updateData(new CusDemoInfoDocument(id, "這里只是測試更新name", "這里只是測試更新code", "這里只是測試更新val"), index, type, id); LOGGER.info("TestController --> demoEsUpdateByIdTest"); return "Update Success."; } /** * @author Jimmy Shan * @date 2020-11-30 * @desc 測試ES 刪除索引 */ @ResponseBody @RequestMapping(value = "/demoEsDelIdxTest", method = RequestMethod.POST) public String demoEsDelIdxTest(HttpServletRequest request, HttpServletResponse response) { String index = "cusdemoinfoidx"; esUtil.deleteIndex(index); return "delete index Success."; } }

好了,我們將程序部署到阿里雲ECS環境中,用postman可以調試一下,以上方法,本人是經過驗證的,可以和阿里雲ES互通及各項操作。

PS:本人在阿里雲ES管理端,開啟了自動創建及刪除索引功能,這個是個好東西,可以不用每次單獨去創建索引,直接使用addData添加數據,自動

會創建對應的索引。

 

還是那句老話,發出來供大家一起學習及討論,如覺得好可轉載,但請注明原創地址,萬分感謝。

 


免責聲明!

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



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