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

一、相关介绍

1)版本信息:

Java High Level REST Client 的版本为:7.4.2

elasticsearch: 7.4.2

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

猜你喜欢

转载自www.cnblogs.com/756623607-zhang/p/12142403.html