Springboot2.5.6整合Elasticsearch7.12.1完整示例

Springboot2.5.6整合Elasticsearch7.12.1完整示例

SpringBoot是2.5.6,elasticsearch是7.12.1

使用依赖elasticsearch-rest-high-level-client

使用RestHighLevelClient操作

1、引入Pom文件的依赖

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.6</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>spring-boot-elasticsearch4</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-elasticsearch4</name>
    <description>spring-boot-elasticsearch4</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

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

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

        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.12.1</version>
        </dependency>

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.2.0</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2、添加配置文件ElasticsearchConfig

java 复制代码
package com.example.springbootelasticsearch4.config;

import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;

/**
 * @author zhangshixing
 * @date 2021年11月11日 13:55
 * elasticsearch 配置类
 */
@Configuration
public class ElasticsearchConfig {

    @Value("${elasticsearch.address}")
    private String address;

    /**
     * 连接超时时间
     */
    @Value("${elasticsearch.connect-timeout}")
    private int connectTimeOut = 1000;

    /**
     * 连接超时时间
     */
    @Value("${elasticsearch.socket-timeout}")
    private int socketTimeOut = 30000;

    /**
     * 获取连接的超时时间
     */
    @Value("${elasticsearch.connection-request-timeout}")
    private int connectionRequestTimeOut = 500;

    /**
     * 最大连接数
     */
    @Value("${elasticsearch.max-connect-num}")
    private int maxConnectNum = 100;

    /**
     * 最大路由连接数
     */
    @Value("${elasticsearch.max-connect-per-route}")
    private int maxConnectPerRoute = 100;

    @Bean
    RestHighLevelClient restHighLevelClient() {
        System.out.println("elasticsearch init......");
        ArrayList<HttpHost> hostList = new ArrayList<>();
        String[] addrss = address.split(",");
        for (String addr : addrss) {
            String[] arr = addr.split(":");
            hostList.add(new HttpHost(arr[0], Integer.parseInt(arr[1]), "http"));
        }
        RestClientBuilder builder = RestClient.builder(hostList.toArray(new HttpHost[0]));
        // 异步httpclient连接延时配置
        builder.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
            @Override
            public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {
                requestConfigBuilder.setConnectTimeout(connectTimeOut);
                requestConfigBuilder.setSocketTimeout(socketTimeOut);
                requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeOut);
                return requestConfigBuilder;
            }
        });
        // 异步httpclient连接数配置
        builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
            @Override
            public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
                httpClientBuilder.setMaxConnTotal(maxConnectNum);
                httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);
                return httpClientBuilder;
            }
        });
        RestHighLevelClient client = new RestHighLevelClient(builder);
        return client;
    }
}

3、配置yml文件

properties 复制代码
# elasticsearch配置
# 如果是集群,用逗号隔开
elasticsearch.address = 127.0.0.1:9200
# 连接超时时间
elasticsearch.connect-timeout = 1000
# 连接超时时间
elasticsearch.socket-timeout = 30000
elasticsearch.connection-request-timeout = 500
elasticsearch.max-connect-num = 100
elasticsearch.max-connect-per-route = 100

4、自定义注解创建索引

这里简单说一下,索引就相当于是表结构,es本身也是存储数据的,既然是存储,就需要定一个结构,比如有哪

些字段,每个字段是什么类型。但是痛点是如果我们定义的这个结构如果比较复杂,那么用原生的方法代码会很

多,很麻烦,所以我们可以自己定义一套注解,加入到实体类上,这样就可以根据实体类,让es自己去创建索

引,很方便。就类似于以前hibernate,可以根据我们写的实体类自动生表。

关于注解,这里也给出以下,在之前文章基础上做了些改动,主要就是加入了EsId注解,可以将制定字段作为es的

id,如果不加这个,es默认id是自动生成的,有了这个,那么我们可以让mysql的id直接作为es的id,方便更新。

关于这块的内容参见:https://blog.csdn.net/lsqingfeng/article/details/106526493

java 复制代码
package com.example.springbootelasticsearch4.annotation;

import java.lang.annotation.*;

/**
 * @author zhangshixing
 * @date 2021年11月11日 16:01
 * Es 文档注解,用于做索引实体映射
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Inherited
public @interface Document {

    /**
     * index : 索引名称
     *
     * @return
     */
    String index();

    /**
     * 类型名称
     *
     * @return
     */
    String type();

}
java 复制代码
package com.example.springbootelasticsearch4.annotation;

import java.lang.annotation.*;

/**
 * @author zhangshixing
 * @date 2021年11月11日 16:02
 * 用于标识使用,该字段作为ES数据中的id
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface EsId {
}
java 复制代码
package com.example.springbootelasticsearch4.annotation;

import java.lang.annotation.*;

/**
 * @author zhangshixing
 * @date 2021年11月11日 16:03
 * 作用在字段上,用于定义类型,映射关系
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface Field {

    FieldType type() default FieldType.TEXT;

    /**
     * 指定分词器
     *
     * @return
     */
    AnalyzerType analyzer() default AnalyzerType.STANDARD;
}

两个枚举:

java 复制代码
package com.example.springbootelasticsearch4.annotation;

/**
 * @author zhangshixing
 * @date 2021年11月11日 19:05
 */

import lombok.Getter;

/**
 * es 类型参看
 * https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html
 */
@Getter
public enum FieldType {
    /**
     * text
     */
    TEXT("text"),

    KEYWORD("keyword"),

    INTEGER("integer"),

    DOUBLE("double"),

    DATE("date"),

    /**
     * 单条数据
     */
    OBJECT("object"),

    /**
     * 嵌套数组
     */
    NESTED("nested"),;


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

    private String type;


}
java 复制代码
package com.example.springbootelasticsearch4.annotation;

import lombok.Getter;

/**
 * @author zhangshixing
 * @date 2021年11月11日 16:04
 */
@Getter
public enum AnalyzerType {

    NO("不使用分词"),
    /**
     * 标准分词,默认分词器
     */
    STANDARD("standard"),

    /**
     * ik_smart:会做最粗粒度的拆分;已被分出的词语将不会再次被其它词语占有
     */
    IK_SMART("ik_smart"),

    /**
     * ik_max_word :会将文本做最细粒度的拆分;尽可能多的拆分出词语
     */
    IK_MAX_WORD("ik_max_word");

    private String type;

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

5、封装Elasticsearch工具类

java 复制代码
package com.example.springbootelasticsearch4.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.springbootelasticsearch4.annotation.Document;
import com.example.springbootelasticsearch4.annotation.EsId;
import com.example.springbootelasticsearch4.annotation.FieldType;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkItemResponse;
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.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.support.replication.ReplicationResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
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.client.indices.PutMappingRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

/**
 * @author zhangshixing
 * @date 2021年11月11日 19:10
 * es 操作工具类,这里均采用同步调用的方式
 */
@Component
@Slf4j
public class ElasticsearchUtil {

    @Resource
    private RestHighLevelClient restHighLevelClient;

    /**
     * 生成索引对应的字段信息
     *
     * @param clazz
     * @return
     * @throws IOException
     */
    private XContentBuilder generateBuilder(Class clazz) throws IOException {
        // 获取索引名称及类型
        Document doc = (Document) clazz.getAnnotation(Document.class);
        System.out.println(doc.index());
        System.out.println(doc.type());
        XContentBuilder builder = XContentFactory.jsonBuilder();
        builder.startObject();
        builder.startObject("properties");
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f : declaredFields) {
            if (f.isAnnotationPresent(com.example.springbootelasticsearch4.annotation.Field.class)) {
                // 获取注解
                com.example.springbootelasticsearch4.annotation.Field declaredAnnotation = f.getDeclaredAnnotation(com.example.springbootelasticsearch4.annotation.Field.class);
                // 如果嵌套对象
                if (declaredAnnotation.type() == FieldType.OBJECT) {
                    // 获取当前类的对象-- Action
                    Class<?> type = f.getType();
                    Field[] df2 = type.getDeclaredFields();
                    builder.startObject(f.getName());
                    builder.startObject("properties");
                    // 遍历该对象中的所有属性
                    for (Field f2 : df2) {
                        if (f2.isAnnotationPresent(com.example.springbootelasticsearch4.annotation.Field.class)) {
                            // 获取注解
                            com.example.springbootelasticsearch4.annotation.Field declaredAnnotation2 = f2.getDeclaredAnnotation(com.example.springbootelasticsearch4.annotation.Field.class);
                            builder.startObject(f2.getName());
                            builder.field("type", declaredAnnotation2.type().getType());
                            // keyword不需要分词
                            if (declaredAnnotation2.type() == FieldType.TEXT) {
                                builder.field("analyzer", declaredAnnotation2.analyzer().getType());
                            }
                            if (declaredAnnotation2.type() == FieldType.DATE) {
                                builder.field("format", "yyyy-MM-dd HH:mm:ss");
                            }
                            builder.endObject();
                        }
                    }
                    builder.endObject();
                    builder.endObject();
                } else {
                    builder.startObject(f.getName());
                    builder.field("type", declaredAnnotation.type().getType());
                    // keyword不需要分词
                    if (declaredAnnotation.type() == FieldType.TEXT) {
                        builder.field("analyzer", declaredAnnotation.analyzer().getType());
                    }
                    if (declaredAnnotation.type() == FieldType.DATE) {
                        builder.field("format", "yyyy-MM-dd HH:mm:ss");
                    }
                    builder.endObject();
                }
            }
        }
        // 对应property
        builder.endObject();
        builder.endObject();
        return builder;
    }

    /**
     * 1、创建索引
     * 默认分片数为5和副本数为1
     *
     * @param clazz 根据实体自动映射es索引
     * @throws IOException
     */
    public boolean createIndex(Class clazz) throws Exception {
        Document declaredAnnotation = (Document) clazz.getDeclaredAnnotation(Document.class);
        if (declaredAnnotation == null) {
            throw new Exception(String.format("class name: %s can not find Annotation [Document], please check", clazz.getName()));
        }
        String indexName = declaredAnnotation.index();
        boolean indexExists = isIndexExists(indexName);
        if (!indexExists) {
            CreateIndexRequest request = new CreateIndexRequest(indexName);
            request.settings(Settings.builder()
                    // 设置分片数为3, 副本为2
                    .put("index.number_of_shards", 3)
                    .put("index.number_of_replicas", 2)
            );
            request.mapping(generateBuilder(clazz));
            CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
            // 指示是否所有节点都已确认请求
            boolean acknowledged = response.isAcknowledged();
            // 指示是否在超时之前为索引中的每个分片启动了必需的分片副本数
            boolean shardsAcknowledged = response.isShardsAcknowledged();
            if (acknowledged || shardsAcknowledged) {
                log.info("创建索引成功!索引名称为{}", indexName);
                return true;
            }
        } else {
            log.info("索引已经存在!索引名称为{}", indexName);
            return false;
        }
        return false;
    }

    /**
     * 2、判断索引是否存在
     *
     * @param indexName
     * @return
     */
    public boolean isIndexExists(String indexName) {
        boolean exists = false;
        try {
            GetIndexRequest getIndexRequest = new GetIndexRequest(indexName);
            getIndexRequest.humanReadable(true);
            exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return exists;
    }

    /**
     * 3、更新索引
     * 默认分片数为5和副本数为1
     * 只能给索引上添加一些不存在的字段
     * 已经存在的映射不能改
     *
     * @param clazz 根据实体自动映射es索引
     * @throws IOException
     */
    public boolean updateIndex(Class clazz) throws Exception {
        Document declaredAnnotation = (Document) clazz.getDeclaredAnnotation(Document.class);
        if (declaredAnnotation == null) {
            throw new Exception(String.format("class name: %s can not find Annotation [Document], please check", clazz.getName()));
        }
        String indexName = declaredAnnotation.index();
        PutMappingRequest request = new PutMappingRequest(indexName);
        request.source(generateBuilder(clazz));
        AcknowledgedResponse response = restHighLevelClient.indices().putMapping(request, RequestOptions.DEFAULT);
        // 指示是否所有节点都已确认请求
        boolean acknowledged = response.isAcknowledged();
        if (acknowledged) {
            log.info("更新索引索引成功!索引名称为{}", indexName);
            return true;
        } else {
            log.info("更新索引索引失败!索引名称为{}", indexName);
            return false;
        }
    }

    /**
     * 4、删除索引
     *
     * @param indexName
     * @return
     */
    public boolean delIndex(String indexName) {
        boolean acknowledged = false;
        try {
            DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexName);
            deleteIndexRequest.indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN);
            AcknowledgedResponse delete = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
            acknowledged = delete.isAcknowledged();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return acknowledged;
    }

    /**
     * 获取字段信息
     *
     * @param o
     * @param annotationClass
     * @return
     */
    private static Field getFieldByAnnotation(Object o, Class annotationClass) {
        Field[] declaredFields = o.getClass().getDeclaredFields();
        if (declaredFields != null && declaredFields.length > 0) {
            for (Field f : declaredFields) {
                if (f.isAnnotationPresent(annotationClass)) {
                    return f;
                }
            }
        }
        return null;
    }

    /**
     * 5、添加单条数据
     * 提供多种方式:
     * 1. json
     * 2. map
     * Map<String, Object> jsonMap = new HashMap<>();
     * jsonMap.put("user", "kimchy");
     * jsonMap.put("postDate", new Date());
     * jsonMap.put("message", "trying out Elasticsearch");
     * IndexRequest indexRequest = new IndexRequest("posts").id("1").source(jsonMap);
     * 3. builder
     * XContentBuilder builder = XContentFactory.jsonBuilder();
     * builder.startObject();
     * {
     * builder.field("user", "kimchy");
     * builder.timeField("postDate", new Date());
     * builder.field("message", "trying out Elasticsearch");
     * }
     * builder.endObject();
     * IndexRequest indexRequest = new IndexRequest("posts").id("1").source(builder);
     * 4. source:
     * IndexRequest indexRequest = new IndexRequest("posts").id("1").source("user", "kimchy","postDate", new Date(),"message", "trying out Elasticsearch");
     *
     * @return
     */
    public IndexResponse addDocument(Object o) throws Exception {
        Document declaredAnnotation = (Document) o.getClass().getDeclaredAnnotation(Document.class);
        if (declaredAnnotation == null) {
            throw new Exception(String.format("class name: %s can not find Annotation [Document], please check", o.getClass().getName()));
        }
        String indexName = declaredAnnotation.index();
        IndexRequest request = new IndexRequest(indexName);
        Field fieldByAnnotation = getFieldByAnnotation(o, EsId.class);
        if (fieldByAnnotation != null) {
            fieldByAnnotation.setAccessible(true);
            try {
                Object id = fieldByAnnotation.get(o);
                request = request.id(id.toString());
            } catch (IllegalAccessException e) {
                log.error("获取id字段出错:{}", e);
            }
        }
        String userJson = JSON.toJSONString(o);
        request.source(userJson, XContentType.JSON);
        IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT);
        return indexResponse;
    }

    /**
     * 6、根据id查询
     *
     * @return
     */
    public String queryDocumentById(String indexName, String id) throws IOException {
        GetRequest getRequest = new GetRequest(indexName, id);
        // getRequest.fetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE);
        GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
        String jsonStr = getResponse.getSourceAsString();
        return jsonStr;
    }

    /**
     * 7、查询封装,返回json字符串
     *
     * @param indexName
     * @param searchSourceBuilder
     * @return
     * @throws IOException
     */
    public String search(String indexName, SearchSourceBuilder searchSourceBuilder) throws IOException {
        SearchRequest searchRequest = new SearchRequest(indexName);
        searchRequest.source(searchSourceBuilder);
        searchRequest.scroll(TimeValue.timeValueMinutes(1L));
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        String scrollId = searchResponse.getScrollId();
        SearchHits hits = searchResponse.getHits();
        JSONArray jsonArray = new JSONArray();
        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();
            JSONObject jsonObject = JSON.parseObject(sourceAsString);
            jsonArray.add(jsonObject);
        }
        log.info("返回总数为:" + hits.getTotalHits());
        return jsonArray.toJSONString();
    }

    /**
     * 8、查询封装,返回带分页
     *
     * @param searchSourceBuilder
     * @param pageNum
     * @param pageSize
     * @param s
     * @param <T>
     * @return
     * @throws IOException
     */
    public <T> PageInfo<T> search(SearchSourceBuilder searchSourceBuilder, int pageNum, int pageSize, Class<T> s) throws Exception {
        Document declaredAnnotation = (Document) s.getDeclaredAnnotation(Document.class);
        if (declaredAnnotation == null) {
            throw new Exception(String.format("class name: %s can not find Annotation [Document], please check", s.getName()));
        }
        String indexName = declaredAnnotation.index();
        SearchRequest searchRequest = new SearchRequest(indexName);
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = searchResponse.getHits();
        JSONArray jsonArray = new JSONArray();
        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();
            JSONObject jsonObject = JSON.parseObject(sourceAsString);
            jsonArray.add(jsonObject);
        }
        log.info("返回总数为:" + hits.getTotalHits());
        int total = (int) hits.getTotalHits().value;

        // 封装分页
        List<T> list = jsonArray.toJavaList(s);
        PageInfo<T> page = new PageInfo<>();
        page.setList(list);
        page.setPageNum(pageNum);
        page.setPageSize(pageSize);
        page.setTotal(total);
        page.setPages(total == 0 ? 0 : (total % pageSize == 0 ? total / pageSize : (total / pageSize) + 1));
        page.setHasNextPage(page.getPageNum() < page.getPages());
        return page;
    }

    /**
     * 9、查询封装,返回集合
     *
     * @param searchSourceBuilder
     * @param s
     * @param <T>
     * @return
     * @throws IOException
     */
    public <T> List<T> search(SearchSourceBuilder searchSourceBuilder, Class<T> s) throws Exception {
        Document declaredAnnotation = (Document) s.getDeclaredAnnotation(Document.class);
        if (declaredAnnotation == null) {
            throw new Exception(String.format("class name: %s can not find Annotation [Document], please check", s.getName()));
        }
        String indexName = declaredAnnotation.index();
        SearchRequest searchRequest = new SearchRequest(indexName);
        searchRequest.source(searchSourceBuilder);
        searchRequest.scroll(TimeValue.timeValueMinutes(1L));
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        String scrollId = searchResponse.getScrollId();
        SearchHits hits = searchResponse.getHits();
        JSONArray jsonArray = new JSONArray();
        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();
            JSONObject jsonObject = JSON.parseObject(sourceAsString);
            jsonArray.add(jsonObject);
        }
        // 封装分页
        List<T> list = jsonArray.toJavaList(s);
        return list;
    }


    /**
     * 10、批量插入文档
     * 文档存在则更新
     * 文档不存在则插入
     *
     * @param list
     * @return
     */
    public <T> boolean batchSaveOrUpdate(List<T> list) throws Exception {
        Object o1 = list.get(0);
        Document declaredAnnotation = (Document) o1.getClass().getDeclaredAnnotation(Document.class);
        if (declaredAnnotation == null) {
            throw new Exception(String.format("class name: %s can not find Annotation [@Document], please check", o1.getClass().getName()));
        }
        String indexName = declaredAnnotation.index();
        BulkRequest request = new BulkRequest(indexName);
        for (Object o : list) {
            String jsonStr = JSON.toJSONString(o);
            IndexRequest indexReq = new IndexRequest().source(jsonStr, XContentType.JSON);
            Field fieldByAnnotation = getFieldByAnnotation(o, EsId.class);
            if (fieldByAnnotation != null) {
                fieldByAnnotation.setAccessible(true);
                try {
                    Object id = fieldByAnnotation.get(o);
                    indexReq = indexReq.id(id.toString());
                } catch (IllegalAccessException e) {
                    log.error("获取id字段出错:{}", e);
                }
            }
            request.add(indexReq);
        }
        BulkResponse bulkResponse = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
        for (BulkItemResponse bulkItemResponse : bulkResponse) {
            DocWriteResponse itemResponse = bulkItemResponse.getResponse();
            IndexResponse indexResponse = (IndexResponse) itemResponse;
            log.info("单条返回结果:{}", indexResponse);
            if (bulkItemResponse.isFailed()) {
                log.error("es 返回错误{}", bulkItemResponse.getFailureMessage());
                return false;
            }
        }
        return true;
    }

    /**
     * 11、删除文档
     *
     * @param indexName: 索引名称
     * @param docId:     文档id
     */
    public boolean deleteDoc(String indexName, String docId) throws IOException {
        DeleteRequest request = new DeleteRequest(indexName, docId);
        DeleteResponse deleteResponse = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
        // 解析response
        String index = deleteResponse.getIndex();
        String id = deleteResponse.getId();
        long version = deleteResponse.getVersion();
        ReplicationResponse.ShardInfo shardInfo = deleteResponse.getShardInfo();
        if (shardInfo.getFailed() > 0) {
            for (ReplicationResponse.ShardInfo.Failure failure :
                    shardInfo.getFailures()) {
                String reason = failure.reason();
                log.info("删除失败,原因为 {}", reason);
            }
        }
        return true;
    }

    /**
     * 12、根据json类型更新文档
     *
     * @param indexName
     * @param docId
     * @param o
     * @return
     * @throws IOException
     */
    public boolean updateDoc(String indexName, String docId, Object o) throws IOException {
        UpdateRequest request = new UpdateRequest(indexName, docId);
        request.doc(JSON.toJSONString(o), XContentType.JSON);
        UpdateResponse updateResponse = restHighLevelClient.update(request, RequestOptions.DEFAULT);
        String index = updateResponse.getIndex();
        String id = updateResponse.getId();
        long version = updateResponse.getVersion();
        if (updateResponse.getResult() == DocWriteResponse.Result.CREATED) {
            return true;
        } else if (updateResponse.getResult() == DocWriteResponse.Result.UPDATED) {
            return true;
        } else if (updateResponse.getResult() == DocWriteResponse.Result.DELETED) {
            return false;
        } else if (updateResponse.getResult() == DocWriteResponse.Result.NOOP) {
            return false;
        }
        return false;
    }

    /**
     * 13、根据Map类型更新文档
     *
     * @param indexName
     * @param docId
     * @param map
     * @return
     * @throws IOException
     */
    public boolean updateDoc(String indexName, String docId, Map<String, Object> map) throws IOException {
        UpdateRequest request = new UpdateRequest(indexName, docId);
        request.doc(map);
        UpdateResponse updateResponse = restHighLevelClient.update(request, RequestOptions.DEFAULT);
        String index = updateResponse.getIndex();
        String id = updateResponse.getId();
        long version = updateResponse.getVersion();
        if (updateResponse.getResult() == DocWriteResponse.Result.CREATED) {
            return true;
        } else if (updateResponse.getResult() == DocWriteResponse.Result.UPDATED) {
            return true;
        } else if (updateResponse.getResult() == DocWriteResponse.Result.DELETED) {
            return false;
        } else if (updateResponse.getResult() == DocWriteResponse.Result.NOOP) {
            return false;
        }
        return false;
    }

}

6、实体

java 复制代码
package com.example.springbootelasticsearch4.pojo;

import com.example.springbootelasticsearch4.annotation.*;
import lombok.Data;

/**
 * @author zhangshixing
 * @date 2022年01月16日 9:53
 * @Description ElasticSearch索引实体类
 */

@Data
@Document(index = "search_index", type = "")
public class Search {

    /**
     * 商品id
     */
    @EsId
    @Field(type = FieldType.INTEGER)
    private Integer itemId;

    /**
     * 商品名字
     */
    @Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_MAX_WORD)
    private String itemName;

    /**
     * 价格
     * 要进行范围筛选
     */
    @Field(type = FieldType.DOUBLE)
    private Double price;

    /**
     * 规格名称
     * 多个规格组合
     */
    @Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_MAX_WORD)
    private String itemSpecName;

    /**
     * 产地
     */
    @Field(type = FieldType.KEYWORD)
    private String productPlace;

    /**
     * 保质期
     * 200天
     * 要进行范围筛选
     */
    @Field(type = FieldType.INTEGER)
    private Integer footPeriod;

    /**
     * 品牌
     */
    @Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_MAX_WORD)
    private String brand;

    /**
     * 包装方式
     */
    @Field(type = FieldType.KEYWORD)
    private String packagingMethod;

    /**
     * 商品重量
     * 25g
     * 要进行范围筛选
     */
    @Field(type = FieldType.DOUBLE)
    private Integer weight;

    /**
     * 食用方式
     */
    @Field(type = FieldType.KEYWORD)
    private String eatMethod;

    /**
     * 销量
     * 要进行范围筛选
     */
    @Field(type = FieldType.INTEGER)
    private Integer sellCounts;

    /**
     * 口味
     * 多种口味组合
     */
    @Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_MAX_WORD)
    private String flavor;

    /**
     * 图片的展示图片
     */
    @Field(type = FieldType.KEYWORD)
    private String showImg;

}

这里会使用反射通过上面标注的注解来使用:比如我有一个实体,根据这个实体自动创建索引的方式如下; 首先

在实体上加入我们自定义的注解,来设置索引名称,字段的类型,分词器是什么。这里字符串类型在es中有两

种,一是KEY_WORD,不分词, 二是TEXT,会分词。

java 复制代码
package com.example.springbootelasticsearch4.pojo;

import lombok.*;


/**
 * 将查询条件和分页条件都封装到一个VO中。
 */
@Data
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class SearchVO {

    /**
     * 商品id
     */
    private Integer itemId;

    /**
     * 商品名字
     */
    private String itemName;

    /**
     * 价格
     * 要进行范围筛选
     */
    private Double price;

    /**
     * 规格名称
     * 多个规格组合
     */
    private String itemSpecName;

    /**
     * 产地
     */
    private String productPlace;

    /**
     * 保质期
     * 200天
     * 要进行范围筛选
     */
    private Integer footPeriod;

    /**
     * 品牌
     */
    private String brand;

    /**
     * 包装方式
     */
    private String packagingMethod;

    /**
     * 商品重量
     * 25g
     * 要进行范围筛选
     */
    private Integer weight;

    /**
     * 食用方式
     */
    private String eatMethod;

    /**
     * 销量
     * 要进行范围筛选
     */
    private Integer sellCounts;

    /**
     * 口味
     * 多种口味组合
     */
    private String flavor;

    /**
     * 图片的展示图片
     */
    private String showImg;

    /**
     * 当前第几页
     */
    private Integer pageNum;

    /**
     * 每页显示多少条数据
     */
    private Integer pageSize;

    /**
     * 排序的字段名
     */
    private String sortName;

    /**
     * 排的方式
     * 降序或者是升序
     */
    private String orderSort;
}

7、Service

java 复制代码
package com.example.springbootelasticsearch4.service;

import com.example.springbootelasticsearch4.pojo.SearchVO;
import com.github.pagehelper.PageInfo;

public interface QueryService {

    public PageInfo<SearchVO> pageSearchQuery(SearchVO searchVO) throws Exception;
}
java 复制代码
package com.example.springbootelasticsearch4.service.impl;

import com.example.springbootelasticsearch4.pojo.Search;
import com.example.springbootelasticsearch4.pojo.SearchVO;
import com.example.springbootelasticsearch4.service.QueryService;
import com.example.springbootelasticsearch4.utils.ElasticsearchUtil;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author zhangshixing
 * @date 2021年11月11日 20:45
 */
@Service
public class QueryServiceImpl implements QueryService {

    @Autowired
    private ElasticsearchUtil elasticsearchUtil;

    @Override
    public PageInfo<SearchVO> pageSearchQuery(SearchVO searchVO) throws Exception {
        SearchSourceBuilder searchSourceBuilder = getSearchSourceBuilder(searchVO);
        PageInfo page = elasticsearchUtil.search(searchSourceBuilder, searchVO.getPageNum(), searchVO.getPageSize(), Search.class);
        return page;
    }

    /**
     * 拼接综合查询 查询条件
     *
     * @param searchVO
     * @return
     */
    private SearchSourceBuilder getSearchSourceBuilder(SearchVO searchVO) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 设置第几页
        if (searchVO.getPageNum() == null) {
            searchVO.setPageNum(1);
        }
        // 设置每页显示的数量
        if (searchVO.getPageSize() == null) {
            searchVO.setPageSize(10000);
        }
        // 设置页数
        sourceBuilder.from((searchVO.getPageNum() - 1) * searchVO.getPageSize());
        sourceBuilder.size(searchVO.getPageSize());
        // 符合条件查询
        BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
        if (StringUtils.isNotEmpty(searchVO.getItemName())) {
            boolBuilder.must(QueryBuilders.matchQuery("itemName", searchVO.getItemName()));
        }
        if (StringUtils.isNotEmpty(searchVO.getItemSpecName())) {
            boolBuilder.must(QueryBuilders.termQuery("itemSpecName", searchVO.getItemSpecName()));
        }
        if (searchVO.getPrice() != null) {
            boolBuilder.must(QueryBuilders.rangeQuery("price").gte(searchVO.getPrice()));
        }
        sourceBuilder.query(boolBuilder);
        // 排序
        if (StringUtils.isNotEmpty(searchVO.getSortName())) {
            FieldSortBuilder fieldSortBuilder = new FieldSortBuilder(searchVO.getSortName());
            // 按照升序还是降序排序
            fieldSortBuilder = fieldSortBuilder.order("orderDesc".equals(searchVO.getOrderSort()) ? SortOrder.DESC : SortOrder.ASC);
            sourceBuilder.sort(fieldSortBuilder);
        }
        return sourceBuilder;
    }
}

8、启动类

java 复制代码
package com.example.springbootelasticsearch4;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootElasticsearch4Application {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootElasticsearch4Application.class, args);
	}

}

9、测试

java 复制代码
package com.example.springbootelasticsearch4;

import com.example.springbootelasticsearch4.pojo.Search;
import com.example.springbootelasticsearch4.pojo.SearchVO;
import com.example.springbootelasticsearch4.service.QueryService;
import com.example.springbootelasticsearch4.utils.ElasticsearchUtil;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@Slf4j
@SpringBootTest
class SpringBootElasticsearch4ApplicationTests {

    @Autowired
    private ElasticsearchUtil elasticsearchUtil;

    @Autowired
    private QueryService queryService;

    @Test
    void contextLoads() {
        try {
            elasticsearchUtil.createIndex(Search.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    void contextLoads1() {
        // 这里可以设置一些属性
        SearchVO searchVO = new SearchVO();
        PageInfo<SearchVO> returnPageInfo = null;
        try {
            returnPageInfo = queryService.pageSearchQuery(searchVO);
        } catch (Exception e) {
            log.error("es 综合查询异常,开始使用数据库做综合查询,错误为 :{}", e);
            // es异常,使用数据库查询
            // 进行数据库的一些查询操作
            // returnPageInfo = 数据库操作
        }
        System.out.println(returnPageInfo);
    }
}

这样就会根据创建一个叫做search_index的索引(实体注解上配置),并且可以设置一些查询条件进行查询。

相关推荐
SafePloy安策31 分钟前
ES信息防泄漏:策略与实践
大数据·elasticsearch·开源
涔溪37 分钟前
Ecmascript(ES)标准
前端·elasticsearch·ecmascript
csdn5659738504 小时前
Elasticsearch 重建索引 数据迁移
elasticsearch·数据迁移·重建索引
天幕繁星4 小时前
docker desktop es windows解决vm.max_map_count [65530] is too low 问题
windows·elasticsearch·docker·docker desktop
Elastic 中国社区官方博客4 小时前
Elasticsearch 8.16:适用于生产的混合对话搜索和创新的向量数据量化,其性能优于乘积量化 (PQ)
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
m1chiru4 小时前
Elasticsearch 实战应用:高效搜索与数据分析
elasticsearch
飞翔的佩奇4 小时前
ElasticSearch:使用dsl语句同时查询出最近2小时、最近1天、最近7天、最近30天的数量
大数据·elasticsearch·搜索引擎·dsl
Elastic 中国社区官方博客11 小时前
Elasticsearch 和 Kibana 8.16:Kibana 获得上下文和 BBQ 速度并节省开支!
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
一个处女座的程序猿11 小时前
LLMs之VDB:Elasticsearch的简介、安装和使用方法、案例应用之详细攻略
大数据·elasticsearch·搜索引擎
未 顾20 小时前
day12:版本控制器
大数据·elasticsearch·搜索引擎