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文件內(nèi)容添加如下:
## ES config spring.elasticsearch.username= spring.elasticsearch.password= spring.elasticsearch.cluster_host=IP地址 或 阿里云內(nèi)網(wǎng)域名 或 公網(wǎng)域名 spring.elasticsearch.cluster_port=9200 spring.es.env.flag=sit
創(chuàng)建實體類:
package com.xx.xx.es.entity; import org.springframework.data.annotation.Id; import lombok.Data; /** * @author Jimmy Shan * @date 2020-11-26 * @desc 示例DEMO 數(shù)據(jù)存儲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; } }
創(chuàng)建工具類:
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(); //訪問用戶名和密碼,創(chuàng)建阿里云Elasticsearch實例時設(shè)置的用戶名和密碼,也是Kibana控制臺的登錄用戶名和密碼。 credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(this.username, this.password)); // 通過builder創(chuàng)建rest client,配置http client的HttpClientConfigCallback。 // 單擊所創(chuàng)建的Elasticsearch實例ID,在基本信息頁面獲取公網(wǎng)地址,即為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進行構(gòu)造。 RestHighLevelClient highClient = new RestHighLevelClient(builder); esClientMap.put(Constant.ES_CLIENT, highClient); } return esClientMap.get(Constant.ES_CLIENT); } /** * @desc 創(chuàng)建索引 */ 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 添加數(shù)據(jù) */ 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。 // 同步執(zhí)行,并使用自定義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("索引:{}, 數(shù)據(jù)添加, 返回碼:{}, id:{}", index, response.status().getStatus(), id); } catch (IOException e) { LOGGER.error("添加數(shù)據(jù)失敗, index:{}, id:{}", index, id); } return id; } /** * @desc 修改數(shù)據(jù) */ 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("數(shù)據(jù)更新, 返回碼:{}, id:{}", response.status().getStatus(), id); } catch (IOException e) { LOGGER.error("數(shù)據(jù)更新失敗, index:{}, id:{}", index, id); } return id; } /** * @desc 批量插入數(shù)據(jù) */ 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("索引:{}, 批量插入 {} 條數(shù)據(jù)成功!", index, list.size()); } catch (IOException e) { LOGGER.error(e.getMessage()); LOGGER.error("索引:{}, 批量插入數(shù)據(jù)失敗", index); return false; } return true; } /** * @desc 根據(jù)id刪除數(shù)據(jù) */ 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("索引:{}, 根據(jù)id {} 刪除數(shù)據(jù):{}", index, docId, JsonWare.beanToJson(response)); } catch (Exception e) { LOGGER.error("根據(jù)id刪除數(shù)據(jù)失敗, index:{}, id:{}", index, docId); return false; } return true; } }
看下Constant類內(nèi)容:
/** * @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查詢限制數(shù)量 public static final Integer ES_QUERY_PAGE_START = 0; public static final Integer ES_QUERY_COUNT = 20; }
以上只是一些基本操作方式,下面 重點記錄下 模糊高亮查詢方法
本人這里自定義一個service服務(wù),用來處理高亮查詢
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服務(wù) */ public interface EsService { /** * @desc 高連顯示查詢-DEMO */ List<CusDemoInfoDocument> searchHighLightDocForDemo(String contents, String index, String type); }
Service實現(xiàn)
/** * @desc 高連顯示查詢-DEMO */ @Override public List<CusDemoInfoDocument> searchHighLightDocForDemo(String contents, String index, String type) { try { // 獲取鏈接RestHighLevelClient對象 RestHighLevelClient highClient = esUtil.getEsClient(); // 查詢維度設(shè)置 BoolQueryBuilder queryBuilder = new BoolQueryBuilder(); // 查詢列,此處支持多列查詢 String[] field = {"id", "demoName", "demoCode", "demoValue"}; // 多列匹配規(guī)則 queryBuilder.must(QueryBuilders.multiMatchQuery(contents, field)); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); sourceBuilder.query(queryBuilder); sourceBuilder.from(Constant.ES_QUERY_PAGE_START);// 分頁起始位,下標(biāo)從0開始 sourceBuilder.size(Constant.ES_QUERY_COUNT);// 每次20條記錄 //設(shè)置高亮顯示 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)); // 遍歷結(jié)果 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; } }
好了,主要方法都已完成,現(xiàn)在讓我們寫個測試類試一試。
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 根據(jù)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 根據(jù)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環(huán)境中,用postman可以調(diào)試一下,以上方法,本人是經(jīng)過驗證的,可以和阿里云ES互通及各項操作。
PS:本人在阿里云ES管理端,開啟了自動創(chuàng)建及刪除索引功能,這個是個好東西,可以不用每次單獨去創(chuàng)建索引,直接使用addData添加數(shù)據(jù),自動
會創(chuàng)建對應(yīng)的索引。
還是那句老話,發(fā)出來供大家一起學(xué)習(xí)及討論,如覺得好可轉(zhuǎn)載,但請注明原創(chuàng)地址,萬分感謝。

浙公網(wǎng)安備 33010602011771號