• 【Spring Boot】Spring Boot之使用 Java High Level REST Client 整合elasticsearch


    一、相关介绍

    1)版本信息:

    Java High Level REST Client 的版本为:7.4.2

    elasticsearch: 7.4.2

    注意:Java High Level REST Client的版本必须小于等于你的elasticsearch版本,建议版本一致

    2)整合思路

    1.通过注解在实体类上定义对应的index和mapping信息

    2.通过spring事件监听器实现项目启动后,自动建立index和mapping

    3.通过抽取base dao类,实现通用的es增删改查逻辑

    4.提供部分常用的查询demo

    3)GitHub地址

    https://github.com/zhangboqing/spring-boot-demo-elasticsearch-rest-high-level-client

    二、整合步骤

    1)maven坐标

    <dependency>
                <groupId>org.elasticsearch.client</groupId>
                <artifactId>elasticsearch-rest-high-level-client</artifactId>
                <version>7.4.2</version>
                <exclusions>
                    <exclusion>
                        <artifactId>elasticsearch</artifactId>
                        <groupId>org.elasticsearch</groupId>
                    </exclusion>
                    <exclusion>
                        <artifactId>elasticsearch-rest-client</artifactId>
                        <groupId>org.elasticsearch.client</groupId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.elasticsearch</groupId>
                <artifactId>elasticsearch</artifactId>
                <version>7.4.2</version>
                <scope>compile</scope>
            </dependency>
            <dependency>
                <groupId>org.elasticsearch.client</groupId>
                <artifactId>elasticsearch-rest-client</artifactId>
                <version>7.4.2</version>
                <scope>compile</scope>
            </dependency>
    View Code

    2)自定义注解

    1.@ESDocument

    import java.lang.annotation.*;
    
    /**
     * @author zhangboqing
     * @date 2019/12/12
     */
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE })
    public @interface ESDocument {
    
        /**
         * Name of the Elasticsearch index.
         * <ul>
         * <li>Lowercase only</li>
         * <li><Cannot include \, /, *, ?, ", <, >, |, ` ` (space character), ,, #/li>
         * <li>Cannot start with -, _, +</li>
         * <li>Cannot be . or ..</li>
         * <li>Cannot be longer than 255 bytes (note it is bytes, so multi-byte characters will count towards the 255 limit
         * faster)</li>
         * </ul>
         */
        String indexName();
    
        /**
         * Number of shards for the index {@link #indexName()}. Used for index creation.
         */
        int shards() default 0;
    
        /**
         * Number of replicas for the index {@link #indexName()}. Used for index creation.
         */
        int replicas() default 0;
    }
    View Code

    2.@ESField

    /**
     * @author zhangboqing
     * @date 2019/12/12
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    @Documented
    @Inherited
    public @interface ESField {
    
        @AliasFor("name")
        String value() default "";
    
        @AliasFor("value")
        String name() default "";
    
        ESFieldType type();
    
        String analyzer() default "";
    }
    View Code

    3.@ESId

    /**
     * @author zhangboqing
     * @date 2019/12/12
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = { FIELD, METHOD, ANNOTATION_TYPE })
    public @interface ESId {
    }
    View Code

    3)定义的base类

    1.BaseElasticsearchDao

    /**
     * @author zhangboqing
     * @date 2019/12/10
     */
    @Slf4j
    public abstract class BaseElasticsearchDao<T> {
    
        @Autowired
        protected ElasticsearchUtils elasticsearchUtils;
        @Autowired
        protected RestHighLevelClient client;
    
        /**
         * 索引名称
         */
        protected String indexName;
        /**
         * ID字段
         */
        protected Field idField;
        /**
         * T对应的类型Class
         */
        protected Class<T> genericClass;
    
        public BaseElasticsearchDao() {
            Class<T> beanClass = (Class<T>) GenericTypeResolver.resolveTypeArgument(this.getClass(), BaseElasticsearchDao.class);
            this.genericClass = beanClass;
    
            ESDocument esDocument = AnnotationUtils.findAnnotation(beanClass, ESDocument.class);
            this.indexName = esDocument.indexName();
    
            Field[] declaredFields = beanClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                ESId esId = declaredField.getAnnotation(ESId.class);
                if (esId != null) {
                    this.idField = declaredField;
                    idField.setAccessible(true);
                    break;
                }
            }
        }
    
    
        /**
         * 保存或更新文档数据
         *
         * @param list 文档数据集合
         */
        public void saveOrUpdate(List<T> list) {
    
            list.forEach(genericInstance -> {
                IndexRequest request = ElasticsearchUtils.buildIndexRequest(indexName, getIdValue(genericInstance), genericInstance);
                try {
                    client.index(request, RequestOptions.DEFAULT);
                } catch (IOException e) {
                    e.printStackTrace();
                    log.error("elasticsearch insert error", e);
                }
            });
    
        }
    
        /**
         * 删除操作
         * 当genericInstance在es中不存在时,调用该方法也不会报错
         *
         * @param genericInstance 被删除的实例对象
         */
        public void delete(T genericInstance) {
            if (ObjectUtils.isEmpty(genericInstance)) {
                // 如果对象为空,则删除全量
                searchList().forEach(result -> {
    
                    elasticsearchUtils.deleteRequest(indexName, getIdValue(genericInstance));
                });
            }
            elasticsearchUtils.deleteRequest(indexName, getIdValue(genericInstance));
        }
    
        /**
         * 搜索文档,根据指定的搜索条件
         *
         * @param searchSourceBuilder
         * @return
         */
        public List<T> search(SearchSourceBuilder searchSourceBuilder) {
            ESSort esSort = new ESSort(SortOrder.ASC,"goodsName");
            ESPageResult search = search(searchSourceBuilder, null, null);
            return search != null ? search.getResults() : null;
    
        }
    
        /**
         * 分页排序搜索文档,根据指定的搜索条件
         *
         * @param searchSourceBuilder
         * @param esPageRequest       分页
         * @param esSort              排序
         * @return
         */
        public ESPageResult<T> search(SearchSourceBuilder searchSourceBuilder, ESPageRequest esPageRequest, ESSort esSort) {
    
            // 搜索
            Assert.notNull(searchSourceBuilder, "searchSourceBuilder is null");
            SearchRequest searchRequest = new SearchRequest(indexName);
            searchRequest.source(searchSourceBuilder);
    
            // 分页
            if (esPageRequest != null) {
                searchSourceBuilder.from(esPageRequest.getPageNo());
                searchSourceBuilder.size(esPageRequest.getSize());
            }
    
            // 排序
            if (esSort != null) {
                List<ESSort.ESOrder> orders = esSort.orders;
                if (!CollectionUtils.isEmpty(orders)) {
                    orders.forEach(esOrder -> searchSourceBuilder.sort(esOrder.getProperty(), esOrder.getDirection()));
                }
            }
    
            SearchResponse searchResponse = null;
            try {
                searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (searchResponse == null) {
                return null;
            }
    
            SearchHits searchHits = searchResponse.getHits();
            SearchHit[] hits = searchHits.getHits();
            List<T> genericInstanceList = new ArrayList<>();
            Arrays.stream(hits).forEach(hit -> {
                String sourceAsString = hit.getSourceAsString();
                genericInstanceList.add(JSON.parseObject(sourceAsString, genericClass));
            });
    
            TotalHits totalHits = searchHits.getTotalHits();
            long total = totalHits.value;
            ESPageResult<T> pageResult = new ESPageResult<>(
                    total,
                    esPageRequest != null ? esPageRequest.getPageNo() : -1,
                    esPageRequest != null ? esPageRequest.getSize() : -1,
                    genericInstanceList);
            return pageResult;
    
        }
    
    
    
    
        /**
         * ============================================================================================================
         *                                                  私有方法
         * ============================================================================================================
         * */
    
        private List<T> searchList() {
            SearchResponse searchResponse = elasticsearchUtils.search(indexName);
            SearchHit[] hits = searchResponse.getHits().getHits();
            List<T> genericInstanceList = new ArrayList<>();
            Arrays.stream(hits).forEach(hit -> {
                String sourceAsString = hit.getSourceAsString();
                genericInstanceList.add(JSON.parseObject(sourceAsString, genericClass));
            });
            return genericInstanceList;
        }
    
        /**
         * 获取当前操作的genericInstance的主键ID
         *
         * @param genericInstance 实例对象
         * @return 返回主键ID值
         */
        private String getIdValue(T genericInstance) {
            try {
                Object idValue = idField.get(genericInstance);
                return idValue.toString();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
    }
    View Code

    2.ElasticsearchUtils

    /**
     * @author zhangboqing
     * @date 2019/12/10
     */
    @Slf4j
    @Component
    public class ElasticsearchUtils {
    
        @Autowired
        public RestHighLevelClient client;
    
        @Autowired
        private ElasticsearchProperties elasticsearchProperties;
    
        public static final RequestOptions COMMON_OPTIONS;
    
        static {
            RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
    
            // 默认缓冲限制为100MB,此处修改为30MB。
            builder.setHttpAsyncResponseConsumerFactory(new HttpAsyncResponseConsumerFactory.HeapBufferedResponseConsumerFactory(30 * 1024 * 1024));
            COMMON_OPTIONS = builder.build();
        }
    
    
        public boolean existIndex(String indexName) {
            boolean exists = false;
            try {
                GetIndexRequest request = new GetIndexRequest(indexName);
                exists = client.indices().exists(request, RequestOptions.DEFAULT);
            } catch (IOException e) {
                e.printStackTrace();
                throw new ElasticsearchException("判断索引 {" + indexName + "} 是否存在失败");
            }
            return exists;
        }
    
        public void createIndexRequest(String indexName) {
            createIndexRequest(indexName, elasticsearchProperties.getIndex().getNumberOfShards(), elasticsearchProperties.getIndex().getNumberOfReplicas());
        }
    
        public void createIndexRequest(String indexName, int shards, int replicas) {
            if (existIndex(indexName)) {
                return;
            }
    
            try {
                CreateIndexRequest request = new CreateIndexRequest(indexName);
                // Settings for this index
                request.settings(Settings.builder()
                        .put("index.number_of_shards", shards)
                        .put("index.number_of_replicas", replicas)
                );
    
                CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
                log.info(" acknowledged : {}", createIndexResponse.isAcknowledged());
                log.info(" shardsAcknowledged :{}", createIndexResponse.isShardsAcknowledged());
            } catch (IOException e) {
                throw new ElasticsearchException("创建索引 {" + indexName + "} 失败");
            }
        }
    
        public void putMappingRequest(String indexName, Class clazz) {
            Field[] fields = clazz.getDeclaredFields();
            if (fields == null || fields.length == 0) {
                return;
            }
    
            try {
                PutMappingRequest request = new PutMappingRequest(indexName);
                XContentBuilder builder = XContentFactory.jsonBuilder();
                builder.startObject();
                {
                    builder.startObject("properties");
                    {
                        for (int i = 0; i < fields.length; i++) {
                            Field field = fields[i];
                            ESId esId = field.getAnnotation(ESId.class);
                            if (esId != null) {
                                continue;
                            } else {
                                AnnotationAttributes esField = AnnotatedElementUtils.getMergedAnnotationAttributes(field, ESField.class);
                                if (esField == null) {
                                    continue;
                                }
                                String name = esField.getString("name");
                                if (StringUtils.isEmpty(name)) {
                                    throw new ElasticsearchException("注解ESField的name属性未指定");
                                }
                                ESFieldType esFieldType = (ESFieldType) esField.get("type");
                                if (esFieldType == null) {
                                    throw new ElasticsearchException("注解ESField的type属性未指定");
                                }
                                builder.startObject(name);
                                {
                                    builder.field("type", esFieldType.typeName);
                                    // 分词器
                                    String analyzer = esField.getString("analyzer");
                                    if (StringUtils.hasText(analyzer)) {
                                        builder.field("analyzer", analyzer);
                                    }
                                }
                                builder.endObject();
                            }
                        }
                    }
                    builder.endObject();
                }
                builder.endObject();
                request.source(builder);
    
                AcknowledgedResponse putMappingResponse = client.indices().putMapping(request, RequestOptions.DEFAULT);
                log.info("acknowledged : :{}", putMappingResponse.isAcknowledged());
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    
        public void deleteIndexRequest(String index) {
            DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(index);
            try {
                client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
            } catch (IOException e) {
                throw new ElasticsearchException("删除索引 {" + index + "} 失败");
            }
        }
    
        public static IndexRequest buildIndexRequest(String index, String id, Object object) {
            return new IndexRequest(index).id(id).source(JSON.toJSONString(object), XContentType.JSON);
        }
    
        public void updateRequest(String index, String id, Object object) {
            try {
                UpdateRequest updateRequest = new UpdateRequest(index, id).doc(JSON.toJSONString(object), XContentType.JSON);
                client.update(updateRequest, COMMON_OPTIONS);
            } catch (IOException e) {
                throw new ElasticsearchException("更新索引 {" + index + "} 数据 {" + object + "} 失败");
            }
        }
    
        public void deleteRequest(String index, String id) {
            try {
                DeleteRequest deleteRequest = new DeleteRequest(index, id);
                client.delete(deleteRequest, COMMON_OPTIONS);
            } catch (IOException e) {
                throw new ElasticsearchException("删除索引 {" + index + "} 数据id {" + id + "} 失败");
            }
        }
    
        public SearchResponse search(String index) {
            SearchRequest searchRequest = new SearchRequest(index);
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(QueryBuilders.matchAllQuery());
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse = null;
            try {
                searchResponse = client.search(searchRequest, COMMON_OPTIONS);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return searchResponse;
        }
    }
    View Code

    3.ESPageRequest

    /**
     * 分页
     * @author zhangboqing
     * @date 2019/12/29
     */
    @Data
    public class ESPageRequest {
    
        private final int pageNo;
        private final int size;
    
        public ESPageRequest(int pageNo, int size) {
    
            if (pageNo < 0) {
                throw new IllegalArgumentException("Page index must not be less than zero!");
            }
    
            if (size < 1) {
                throw new IllegalArgumentException("Page size must not be less than one!");
            }
    
            this.pageNo = pageNo;
            this.size = size;
        }
    
    
    }
    View Code

    4.ESPageResult

    /**
     * @Author zhangboqing
     * @Date 2019-12-30
     * 分页结果
     */
    @Data
    public class ESPageResult<T> {
    
        private final long total;
        private final int pageNo;
        private final int pageSize;
        private List<T> results;
    
        public ESPageResult(long total, int pageNo, int pageSize, List<T> results) {
            this.total = total;
            this.pageNo = pageNo;
            this.pageSize = pageSize;
            this.results = results;
        }
    }
    View Code

    5.ESSort

    /**
     * @Author zhangboqing
     * @Date 2019/12/29
     * 封装排序参数
     */
    public class ESSort {
    
        public final List<ESOrder> orders;
        public ESSort() {
            orders = new ArrayList<>();
        }
    
        public ESSort(SortOrder direction, String property) {
            orders = new ArrayList<>();
            add(direction,property);
        }
    
    
    
        /**
         * 追加排序字段
         * @param direction  排序方向
         * @param property  排序字段
         * @return
         */
        public ESSort add(SortOrder direction, String property) {
    
            Assert.notNull(direction, "direction must not be null!");
            Assert.hasText(property, "fieldName must not be empty!");
    
            orders.add(ESOrder.builder().direction(direction).property(property).build());
            return this;
        }
    
    
        @Builder
        @Data
        public static class ESOrder implements Serializable {
    
            private final SortOrder direction;
            private final String property;
    
        }
    
    }
    View Code

    4)定义的enum枚举类

    1.ESFieldType

    /**
     * @author zhangboqing
     * @date 2019/12/12
     */
    public enum ESFieldType {
        Text("text"),
        Byte("byte"),
        Short("short"),
        Integer("integer"),
        Long("long"),
        Date("date"),
        Float("float"),
        Double("double"),
        Boolean("boolean"),
        Object("object"),
        Keyword("keyword");
    
    
        ESFieldType(String typeName) {
            this.typeName = typeName;
        }
    
        public String typeName;
    }
    View Code

    5)自定义异常类

    1.ApiException

    public class ApiException extends RuntimeException {
        private ApiResponseCode responseCode;
        private IApiResponseCode iApiResponseCode;
        private ApiException.ErrorPrintLogLevelEnum errorPrintLogLevelEnum;
        private Integer code;
    
        public ApiException() {
        }
    
        public ApiException(String message) {
            super(message);
        }
    
        public ApiException(String message, Throwable cause) {
            super(message, cause);
        }
    
        public ApiException(Throwable cause) {
            super(cause);
        }
    
        public ApiException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    
        public ApiException(Integer code, String message) {
            super(message);
            this.code = code;
        }
    
        public ApiException(ApiResponseCode responseCode) {
            super(responseCode.message);
            this.responseCode = responseCode;
            this.code = responseCode.getCode();
        }
    
        public ApiException(ApiResponseCode responseCode, String message) {
            super(message);
            this.responseCode = responseCode;
            this.code = responseCode.getCode();
        }
    
        public ApiException(ApiResponseCode responseCode, Throwable cause) {
            super(responseCode.message, cause);
            this.responseCode = responseCode;
            this.code = responseCode.getCode();
        }
    
        public ApiException(ApiResponseCode responseCode, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(responseCode.message, cause, enableSuppression, writableStackTrace);
            this.responseCode = responseCode;
            this.code = responseCode.getCode();
        }
    
        public ApiException(IApiResponseCode iApiResponseCode) {
            super(iApiResponseCode.getMessage());
            this.iApiResponseCode = iApiResponseCode;
            this.code = iApiResponseCode.getCode();
        }
    
        public ApiException(IApiResponseCode iApiResponseCode, String message) {
            super(message);
            this.iApiResponseCode = iApiResponseCode;
            this.code = iApiResponseCode.getCode();
        }
    
        public ApiException(IApiResponseCode iApiResponseCode, String message, ApiException.ErrorPrintLogLevelEnum errorPrintLogLevel) {
            super(message);
            this.iApiResponseCode = iApiResponseCode;
            this.code = iApiResponseCode.getCode();
            this.errorPrintLogLevelEnum = errorPrintLogLevel;
        }
    
        public ApiException(IApiResponseCode iApiResponseCode, Throwable cause) {
            super(iApiResponseCode.getMessage(), cause);
            this.iApiResponseCode = iApiResponseCode;
            this.code = iApiResponseCode.getCode();
        }
    
        public ApiException(IApiResponseCode iApiResponseCode, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(iApiResponseCode.getMessage(), cause, enableSuppression, writableStackTrace);
            this.iApiResponseCode = iApiResponseCode;
            this.code = iApiResponseCode.getCode();
        }
    
        public ApiResponseCode getResponseCode() {
            return this.responseCode;
        }
    
        public IApiResponseCode getIApiResponseCode() {
            return this.iApiResponseCode;
        }
    
        public ApiException.ErrorPrintLogLevelEnum getErrorPrintLogLevelEnum() {
            return this.errorPrintLogLevelEnum;
        }
    
        public Integer getCode() {
            return this.code;
        }
    
        public void setResponseCode(ApiResponseCode responseCode) {
            this.responseCode = responseCode;
        }
    
        public void setIApiResponseCode(IApiResponseCode iApiResponseCode) {
            this.iApiResponseCode = iApiResponseCode;
        }
    
        public void setErrorPrintLogLevelEnum(ApiException.ErrorPrintLogLevelEnum errorPrintLogLevelEnum) {
            this.errorPrintLogLevelEnum = errorPrintLogLevelEnum;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    
        public static enum ErrorPrintLogLevelEnum {
            NONE,
            TRACE,
            DEBUG,
            INFO,
            WARN,
            ERROR;
    
            private ErrorPrintLogLevelEnum() {
            }
        }
    }
    View Code

    2.ApiResponseCode

    public enum ApiResponseCode implements IApiResponseCode {
        SUCCESS(0, "成功"),
        FAILED(1, "失败");
    
        public Integer code;
        public String message;
    
        @Override
        public Integer getCode() {
            return this.code;
        }
    
        @Override
        public String getMessage() {
            return this.message;
        }
    
        private ApiResponseCode(Integer code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public String toString() {
            return "ApiResponseCode(code=" + this.getCode() + ", message=" + this.getMessage() + ")";
        }
    }
    View Code

    3.IApiResponseCode

    /**
     * @author zhangboqing
     * @date 2019/12/10
     */
    public interface IApiResponseCode {
        Integer getCode();
    
        String getMessage();
    }
    View Code

    4.ElasticsearchException

    /**
     * @author zhangboqing
     * @date 2019/12/10
     */
    public class ElasticsearchException extends ApiException {
    
        public ElasticsearchException(String message) {
            super(ApiResponseCode.FAILED,message);
        }
    }
    View Code

    6)elasticsearch核心config类

    1.ElasticsearchProperties

    /**
     * @author zhangboqing
     * @date 2019/12/10
     */
    @Data
    @Builder
    @Component
    @NoArgsConstructor
    @AllArgsConstructor
    @ConfigurationProperties(prefix = "demo.data.elasticsearch")
    public class ElasticsearchProperties {
    
        /**
         * 请求协议
         */
        private String schema = "http";
    
        /**
         * 集群名称
         */
        private String clusterName = "elasticsearch";
    
        /**
         * 集群节点
         */
        @NotNull(message = "集群节点不允许为空")
        private List<String> clusterNodes = new ArrayList<>();
    
        /**
         * 连接超时时间(毫秒)
         */
        private Integer connectTimeout = 1000;
    
        /**
         * socket 超时时间
         */
        private Integer socketTimeout = 30000;
    
        /**
         * 连接请求超时时间
         */
        private Integer connectionRequestTimeout = 500;
    
        /**
         * 每个路由的最大连接数量
         */
        private Integer maxConnectPerRoute = 10;
    
        /**
         * 最大连接总数量
         */
        private Integer maxConnectTotal = 30;
    
        /**
         * 索引配置信息
         */
        private Index index = new Index();
    
        /**
         * 认证账户
         */
        private Account account = new Account();
    
        /**
         * 索引配置信息
         */
        @Data
        public static class Index {
    
            /**
             * 分片数量
             */
            private Integer numberOfShards = 3;
    
            /**
             * 副本数量
             */
            private Integer numberOfReplicas = 2;
    
        }
    
        /**
         * 认证账户
         */
        @Data
        public static class Account {
    
            /**
             * 认证用户
             */
            private String username;
    
            /**
             * 认证密码
             */
            private String password;
    
        }
    
    }
    View Code

    2.ElasticsearchConfig

    /**
     * @author zhangboqing
     * @date 2019/12/10
     */
    @Configuration
    @RequiredArgsConstructor(onConstructor_ = @Autowired)
    @EnableAutoConfiguration(exclude = {RestClientAutoConfiguration.class})
    public class ElasticsearchConfig {
    
        private final ElasticsearchProperties elasticsearchProperties;
    
        @Bean
        public RestHighLevelClient initailizationRestHighLevelClient() {
    
            // 设置es节点
            List<HttpHost> httpHosts = new ArrayList<>();
            List<String> clusterNodes = elasticsearchProperties.getClusterNodes();
            clusterNodes.forEach(node -> {
                try {
                    String[] parts = StringUtils.split(node, ":");
                    Assert.notNull(parts, "Must defined");
                    Assert.state(parts.length == 2, "Must be defined as 'host:port'");
                    httpHosts.add(new HttpHost(parts[0], Integer.parseInt(parts[1]), elasticsearchProperties.getSchema()));
                } catch (Exception e) {
                    throw new IllegalStateException("Invalid ES nodes " + "property '" + node + "'", e);
                }
            });
            RestClientBuilder builder = RestClient.builder(httpHosts.toArray(new HttpHost[0]));
    
            final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "123456"));
            builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
                        @Override
                        public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
                            return httpClientBuilder
                                    // 线程数量
    //                                .setDefaultIOReactorConfig(
    //                                        IOReactorConfig.custom()
    //                                                .setIoThreadCount(1)
    //                                                .build())
                                    // 认证设置
                                    .setDefaultCredentialsProvider(credentialsProvider);
                        }
                    })
                    // 超时时间
                    .setRequestConfigCallback(
                            new RestClientBuilder.RequestConfigCallback() {
                                @Override
                                public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {
                                    return requestConfigBuilder.setConnectTimeout(5000).setSocketTimeout(60000);
                                }
                            });
    
    
            RestHighLevelClient client = new RestHighLevelClient(builder);
            return client;
        }
    }
    View Code

    3.ElasticsearchApplicationListener

    /**
     * @author zhangboqing
     * @date 2019/12/10
     */
    @Component
    @Slf4j
    public class ElasticsearchApplicationListener implements ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware {
    
        @Autowired
        protected ElasticsearchUtils elasticsearchUtils;
    
        private ApplicationContext applicationContext;
    
        @Override
        public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
    
            String[] beanNames = applicationContext.getBeanNamesForType(BaseElasticsearchDao.class);
            if (beanNames != null && beanNames.length > 0) {
                for (int i = 0; i < beanNames.length; i++) {
                    String beanName = beanNames[i];
                    if (beanName.contains("com.zbq.springbootelasticsearch.common.elasticsearch.base.BaseElasticsearchDao")) {
                        continue;
                    }
                    Object bean = applicationContext.getBean(beanName);
                    Class<?> targetBeanClass = bean.getClass();
                    Class<?> beanClass = GenericTypeResolver.resolveTypeArgument(targetBeanClass, BaseElasticsearchDao.class);
                    ESDocument esDocument = AnnotationUtils.findAnnotation(beanClass, ESDocument.class);
                    if (esDocument == null) {
                        throw new ElasticsearchException("ESDocument注解未指定");
                    }
                    String indexName = esDocument.indexName();
                    if (StringUtils.isEmpty(indexName)) {
                        throw new ElasticsearchException("indexName未指定");
                    }
                    int shards = esDocument.shards();
                    int replicas = esDocument.replicas();
    
                    if (shards == 0 || replicas == 0) {
                        elasticsearchUtils.createIndexRequest(indexName);
                    } else {
                        elasticsearchUtils.createIndexRequest(indexName, shards, replicas);
                    }
                    elasticsearchUtils.putMappingRequest(indexName, beanClass);
                }
            }
    
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
    View Code

    三、使用步骤

    1)定义GoodsESEntity

    /**
     * @author zhangboqing
     * @date 2019/12/10
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    @ESDocument(indexName = "goods")
    public class GoodsESEntity {
        // 筛选条件包括:商品名称,品牌,规格,适用车型,商品编号,原厂编号
    
        /**
         * 主键,商品ID
         */
        @ESId
        @ESField(value = "goodsId",type = ESFieldType.Long)
        private Long goodsId;
    
        /**
         * 商品名称
         */
        @ESField(value = "goodsName",type = ESFieldType.Keyword)
        private String goodsName;
        /**
         * 品牌
         */
        @ESField(value = "goodBrand",type = ESFieldType.Keyword)
        private String goodBrand;
        /**
         * 规格
         */
        @ESField(value = "goodsSpec",type = ESFieldType.Keyword)
        private String goodsSpec;
        /**
         * 商品编号
         */
        @ESField(value = "goodsAccessoriesCode",type = ESFieldType.Keyword)
        private String goodsAccessoriesCode;
        /**
         * 原厂编号
         */
        @ESField(value = "goodsOriginalFactoryCode",type = ESFieldType.Keyword)
        private String goodsOriginalFactoryCode;
    
        /**
         * 复合字段,会被分词后存储
         */
        @ESField(value = "groupData",type = ESFieldType.Text,analyzer = "ik_smart")
        private String groupData;
    }

    2)定义GoodsESDao

    /**
     * @author zhangboqing
     * @date 2019/12/10
     */
    @Component
    public class GoodsESDao<T extends GoodsESEntity> extends BaseElasticsearchDao<GoodsESEntity> {
    
    
        /**
         * 全文搜索查询
         * @param queryName
         * @return
         */
        public List<GoodsESEntity> findListByAnalysisForGroupData(String queryName) {
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(QueryBuilders.matchQuery("groupData",queryName));
            List<GoodsESEntity> search = search(searchSourceBuilder);
            return search;
        }
    
        /**
         * 多条件等值查询查询
         * @return
         */
        public List<GoodsESEntity> findListByEq() {
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            boolQueryBuilder.filter(QueryBuilders.matchQuery("goodsName","保时捷跑车V10"));
            boolQueryBuilder.filter(QueryBuilders.matchQuery("goodBrand","国际1"));
            searchSourceBuilder.query(boolQueryBuilder);
            List<GoodsESEntity> search = search(searchSourceBuilder);
            return search;
        }
    
        /**
         * 多条件like查询查询
         * @return
         */
        public List<GoodsESEntity> findListByLike() {
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            boolQueryBuilder.filter(QueryBuilders.wildcardQuery("goodsName","?V10"));
            boolQueryBuilder.filter(QueryBuilders.wildcardQuery("goodBrand","国际1"));
            searchSourceBuilder.query(boolQueryBuilder);
            List<GoodsESEntity> search = search(searchSourceBuilder);
            return search;
        }
    
        /**
         * 全文搜索查询分页排序
         * @param queryName
         * @return
         * QueryBuilders.boolQuery()
         */
        public ESPageResult findList(String queryName) {
            // 搜索条件
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(QueryBuilders.matchQuery("groupData",queryName));
    
            // 分页
            ESPageRequest esPageRequest = new ESPageRequest(1, 2);
    
            // 排序
            ESSort esSort = new ESSort(SortOrder.ASC,"goodsName");
    
            ESPageResult<GoodsESEntity> search = search(searchSourceBuilder, esPageRequest, esSort);
            return search;
        }
    }

     3)配置属性

    demo:
      data:
        elasticsearch:
        # es集群名称
          cluster-name: elasticsearch
        # es集群节点,多个逗号分隔 localhost:9200,localhost:9400
          cluster-nodes: localhost:9200
        # 设置创建index时,默认的分片规则
          index:
            number-of-shards: 3
            number-of-replicas: 2
        # 设置连接es的用户名和密码
          account:
            username: elastic
            password: 123456
  • 相关阅读:
    扩展Dijkstra
    CodeForces 1396E. Distance Matching
    大联盟2
    整式乘除法
    美国数学会众多教授推荐的本科&研究生代数几何经典书籍教材清单
    算法题——立方体的体对角线穿过多少个正方体?
    导数练习题
    导数压轴题
    集合
    著名数学家Ky Fan的故事
  • 原文地址:https://www.cnblogs.com/756623607-zhang/p/12142403.html
Copyright © 2020-2023  润新知