• 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添加数据,自动

    会创建对应的索引。

    还是那句老话,发出来供大家一起学习及讨论,如觉得好可转载,但请注明原创地址,万分感谢。

  • 相关阅读:
    安卓的sqlite增删改
    C#访问MySQL数据库(winform+EF)
    Sqlite在.NET下的使用和Sqlite数据库清理
    WPF小笔记-Popup拖动
    WPF自定义窗口最大化显示任务栏
    什么是Hash?什么是Hash算法或哈希函数?什么是map?什么是HashMap?HashMap的实现原理或者工作原理?HashMap是线程安全的吗?为什么?如何解决?
    字符串转换整数
    系统顺序图与顺序图区别,以及根据顺序图写代码
    设计领域模型有哪些难点?有哪些指导原则?
    斐波那契数列java实现
  • 原文地址:https://www.cnblogs.com/jimmyshan-study/p/14091496.html
Copyright © 2020-2023  润新知