网络知识 娱乐 SpringBoot-ElasticSearch8

SpringBoot-ElasticSearch8

前沿

elasticsearch-rest-high-level-client在 7.15.0 中已弃用。
不推荐使用高级 REST 客户端,取而代之的是 Java API 客户端 。
spring-boot-starter-data-elasticsearch 也不推荐,虽然基础操作简化了很多,但是一旦使用了es高级特性,那么就如同进入了地狱,同时elasticsearch更新太快了spring-boot-starter-data-elasticsearch的版本根本就赶不上,导致升级会出现很多问题

现在在es官网推荐我们现在使用 Elasticsearch Java API 客户端 这个是相当于直接使用elasticsearch自身的接口Api所以不存在升级不兼容的问题

Java API 客户端官网地址

elasticsearch Api 操作大全 rest-api

需要的Maven

官网推荐依赖
经过各种的依赖冲突的解决,终于能跑起来了…

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
	
        <dependency>
            <groupId>co.elastic.clients</groupId>
            <artifactId>elasticsearch-java</artifactId>
            <version>8.1.2</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.3</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.12.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.12.3</version>
        </dependency>

        <dependency>
            <groupId>jakarta.json</groupId>
            <artifactId>jakarta.json-api</artifactId>
            <version>2.0.1</version>
        </dependency>



        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>


    </dependencies>

创建客户端

        RestClient restClient = RestClient.builder(
                new HttpHost("localhost", 9200)).build();
        ElasticsearchTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());
        //es 客户端
        ElasticsearchClient client = new ElasticsearchClient(transport);

创建索引和映射分片等…

官方文档没有关于怎么设置映射和索引的配置的,自能自己研究,经过千辛万苦看底层源代码(一点注释都没有),了解代码编写的逻辑,经过我不懈的努力终于实现了以下的基础配置,其他的高级配置可以进行参考下配置进行扩展

        //构建索引,并且起一个别名
        CreateIndexResponse createResponse = client.indices()
                .create(c -> c
                        .index("my-index") //创索引
                        .aliases("main-index", a -> a   //创别名
                                .isWriteIndex(true)
                        )
                        // 映射字段属性
                        .mappings((m)-> m
                                .properties("name",Property.of(p->p.text(TextProperty.of(p1->p1.analyzer("ik_max_word").searchAnalyzer("ik_smart")))))
                                .properties("sku" ,Property.of(p->p.text(TextProperty.of(p1->p1.analyzer("ik_max_word").searchAnalyzer("ik_smart")))))
                                .properties("price",Property.of(p->p.integer(IntegerNumberProperty.of(p1->p1))) )
                            )
                        //设置分片   ,numberOfShards分片     ,Replicas副本
                        .settings((s)->s.numberOfShards("1").numberOfReplicas("2"))
                );

        System.out.println(createResponse.index());

自己进行二次封装

把官方文档看完后,我只想说一句,真难用, 经过3天的代码封装,基本能解决es的百分之90%的使用场景,代码压缩和简化程度至少优化了50%,同时保留了原有的代码特性, 特殊场景提供了对外客户端自己也可以进行二次开发,特殊的操作需要自己通过es的api接口手动进行修操作,在后端操作es一般就是增删改查数据,以及聚合,高亮等

结构

在这里插入图片描述

application.yml

es:
  port: 9200
  hostname:  localhost
  alals: true  # 是否使用别名的方式访问

注解

@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface DocId {
}
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface EsClass {

    /**
     * 索引: 前缀index +   默认类名( 自动转小写)
     * 默认类名可以修改为我们自定义
     * @return
     */
    String index() default "";
    /**
     * 别名: 前缀alias + 默认类名( 自动转小写)
     *默认类名可以修改为我们自定义
     * @return
     */
    String alias() default "" ;
    /*
     *  分片
     */
    int shards()default 1;

    /**
     *  分片副本
     * @return
     */
    int replicas()default 1;
}


@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Field {
    //默认属性名
    String name()default "" ;
    //数据类型
    EsDataType type();
    String analyzer()default ""; //分词
    String searchAnalyzer()default ""; //搜索分词
}

枚举

不一定都能用,有些可能随着es版本的变化,去掉了,需要啥自行修改

public enum EsDataType {
    TEXT("text"),
    KEYWORD("keyword"),


    FLOAT("float"),
    LONG("long"),
    INTEGER("integer"),
    SHORT("short"),
    DOUBLE("double"),
    HALF_FLOAT("half_float"),
    SCALED_FLOAT("scaled_float"),
    BYTE("byte"),


    DATE("date"),


    BOOLEAN("boolean"),
    RANGE("rang"),
    BINARY("binary"),
    ARRAY("array"),
    OBJECT("object"),
    NESTED("nested"),
    GEO_POINT("geo_point"),
    GEO_SHAPE("geo_shape"),
    IP("ip"),
    COMPLETION("completion"),
    TOKEN_COUNT("token_count"),
    ATTACHMENT("attachment"),
    PERCOLATOR("percolator");

    private String type;

    EsDataType(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }
}

自定义es客户端工具类

package com.es8.utli;

import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.cat.aliases.AliasesRecord;
import co.elastic.clients.elasticsearch.cat.indices.IndicesRecord;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
import co.elastic.clients.elasticsearch.core.search.Highlight;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.indices.Alias;
import co.elastic.clients.elasticsearch.indices.CreateIndexRequest;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import co.elastic.clients.util.ObjectBuilder;
import com.alibaba.fastjson.JSONObject;
import com.es8.an.DocId;
import com.es8.an.EsClass;
import com.es8.esenum.EsDataType;
import com.obj.objcopy.ObjectCopyUtil;
import com.reflect.ReflectUtil;
import com.string.PatternCommon;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
public class Es8Client implements InitializingBean {
    private static final Logger log = LoggerFactory.getLogger(Es8Client.class);
    @Value("${es.port}")
    private int prot;
    @Value("${es.hostname}")
    private String hostname;
    @Value("${es.alals}")
    private boolean alals;

    private ElasticsearchClient client;
    private ElasticsearchAsyncClient asyncClient;
    private final String indexPrefix = "index-";
    private final String aliasPrefix = "alias-";


    // 同步客户端
    public ElasticsearchClient getClient() {
        return client;
    }
    //异步客户端
    public ElasticsearchAsyncClient getAsyncClient() {
        return asyncClient;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        RestClient restClient = RestClient.builder(
                new HttpHost(hostname, prot)).build();
        ElasticsearchTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());
        //es 客户端
        this.client = new ElasticsearchClient(transport);
        this.asyncClient = new ElasticsearchAsyncClient(transport);


    }

    /**
     * @return 是否成功
     * @throws Exception
     */
    public <T> boolean createIndexSettingsMappings(Class<T> tClass) throws Exception {

        EsClass esClassAnnotation = ReflectUtil.getClassAnnotation(tClass, EsClass.class);
        int shards = esClassAnnotation.shards();
        int replicas = esClassAnnotation.replicas();
        StringBuilder stringBuilder = new StringBuilder("{");
        stringBuilder.append(""settings": {n" +
                "    "number_of_shards": " + shards + ",n" +
                "    "number_of_replicas": " + replicas + "n" +
                "  },");
        stringBuilder.append(""mappings": {n" +
                "    "properties": ");
        JSONObject jsonObject = new JSONObject();

        Map<String, Annotation[]> fieldsAnnotation = ReflectUtil.getFieldsAnnotation(tClass);
        for (Map.Entry<String, Annotation[]> stringEntry : fieldsAnnotation.entrySet()) {
            String key = stringEntry.getKey();
            Annotation[] value = stringEntry.getValue();
            for (Annotation annotation1 : value) {
                JSONObject jsonObject1 = new JSONObject();
                if