整合Jdk17+Spring Boot3.2+Elasticsearch9.0+mybatis3.5.12的简单用法

发布于:2025-05-29 ⋅ 阅读:(17) ⋅ 点赞:(0)

Elasticsearch是一个基于Lucene的分布式搜索和分析引擎,广泛应用于全文搜索、日志分析等场景。结合Spring Boot可以快速构建强大的搜索应用。本文将介绍如何在Spring Boot项目中集成和使用Elasticsearch。

ES9.0.1目前支持的包只有

elasticsearch-rest-client/                                       -         -      
elasticsearch-rest-client-sniffer/  

注意:9.0版本暂不支持elasticsearch-rest-high-level-client

所以我们要用elasticsearch-rest-client来进行开发。

一、环境准备

1. 添加依赖

首先,在Spring Boot项目的`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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>EsExample</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>

        <java.version>17</java.version>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <mybatisplus.version>3.5.12</mybatisplus.version>

    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        </dependency>

<!--        &lt;!&ndash; MyBatis集成 &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.mybatis.spring.boot</groupId>-->
<!--            <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!--            <version>${mybatis.version}</version>-->
<!--        </dependency>-->

        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.23</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>3.3.4</version>
        </dependency>
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
            <version>3.1.6</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>6.2.5.Final</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-jsqlparser-4.9</artifactId>
            <version>${mybatisplus.version}</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-client</artifactId>
            <version>9.0.1</version>
        </dependency>
        <dependency>
            <groupId>co.elastic.clients</groupId>
            <artifactId>elasticsearch-java</artifactId>
            <version>9.0.1</version>
        </dependency>
        <dependency>
            <groupId>jakarta.json</groupId>
            <artifactId>jakarta.json-api</artifactId>
            <version>2.1.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>2.16.0</version>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>1.21.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-compress</artifactId>
            <version>1.21</version>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>testcontainers</artifactId>
            <version>1.21.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.slugify</groupId>
            <artifactId>slugify</artifactId>
            <version>3.0.6</version>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.apache.httpcomponents</groupId>-->
<!--            <artifactId>httpclient</artifactId>-->
<!--            <version>4.5.13</version>-->
<!--        </dependency>-->
        <dependency>
            <groupId>jakarta.json.bind</groupId>
            <artifactId>jakarta.json.bind-api</artifactId>
            <version>3.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse</groupId>
            <artifactId>yasson</artifactId>
            <version>3.0.4</version>
        </dependency>

    </dependencies>
    <dependencyManagement>

        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>3.2.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
                <version>${mybatisplus.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <repositories>
        <repository>
            <id>aliyun</id>
            <url>https://maven.aliyun.com/nexus/content/groups/public</url>
        </repository>
        <repository>
            <id>oss-public</id>
            <url>https://oss.sonatype.org/content/repositories/public</url>
        </repository>
        <repository>
            <id>snapshots</id>
            <url>https://central.sonatype.com/repository/maven-snapshots/</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>
</project>

 2. 配置Elasticsearch连接

在 application.yml 中配置Elasticsearch连接,pgsql连接:
 Elasticsearch配置

spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/postgres?stringtype=unspecified
    username: postgres
    password: 123456
  jpa:
    hibernate:
      ddl-auto: update  #自动生成数据库表
    properties:
      hibernate:
        dialect: org.hibernate.dialect.PostgreSQLDialect
    show-sql: true # jpa配置,在控制台显示hibernate的sql

server:
  port: 8904
  servlet:
    encoding:
      charset: UTF-8

elasticsearch:
  server:
    url: https://localhost:9200
  api:
    key: YL5i65YB6ixG4DLbphIW
  userName:  elastic
  password:  _XRuyepioTeZGzQOhUfk
  host: localhost
  port: 9200

logging:
  level:
    org.example.mapper: debug


file:
  path: D://testaa/img/

二、基本用法

 1. 定义实体类

使用  Table  来注解标记表名称以及es的索引:

@Id注解用于es的id

/**
 * 测试日期
 *
 * @author lyl
 * @version v1.0
 * @since 2025/5/26
 */
@Data
@Table(name="sys_base_data")
@TableName("sys_base_data")
public class StockBaseEntity implements Serializable {
    @Id
    private String id;


    private String name;



    @TableField(fill= FieldFill.INSERT)
    @JsonFormat(shape= JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd",timezone = "GMT+8")
    private String createDate;

    @TableField(fill= FieldFill.INSERT)
    @JsonFormat(shape=JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date updateDate;
}

 2. 创建ES连接对象

Spring Data Elasticsearch提供了类似于JPA的Repository接口:

 



package org.example.es.service;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.endpoints.BooleanResponse;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.client.RestClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;


@Configuration
public class ElasticClient {

    @Value("${elasticsearch.server.url}")
    private String serverUrl;

    @Value("${elasticsearch.api.key}")
    private String apiKey;

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

    @Value("${elasticsearch.port}")
    private int port;

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

    @Autowired
    ResourceLoader resourceLoader;

    @Bean
    public ElasticsearchClient elasticRestClient() throws IOException {
        // ES服务器URL
        // Connection settings
        try {
            HttpHost httphost = new HttpHost(host, port, "https");
            SSLContext sslContext = createInsecureSSLContext();


            BasicCredentialsProvider credsProv = new BasicCredentialsProvider();
            credsProv.setCredentials(
                    AuthScope.ANY, new UsernamePasswordCredentials(userName, password)
            );

            // Building the rest client
            RestClient restClient = RestClient.builder(httphost)
                    .setHttpClientConfigCallback(hc -> hc
                            .setDefaultCredentialsProvider(credsProv)
                            .setSSLContext(sslContext)
                    )
                    .build();
            ObjectMapper mapper = JsonMapper.builder()
                    .addModule(new JavaTimeModule())
                    .build();
            ElasticsearchTransport transport = new RestClientTransport(restClient,
                    new JacksonJsonpMapper(mapper));
            ElasticsearchClient esClient = new ElasticsearchClient(transport);

            // Creating the indexes
            // createSimpleIndex(esClient, USERS);


            return esClient;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (KeyManagementException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 禁用ssl验证
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    public SSLContext createInsecureSSLContext() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[]{new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }}, new SecureRandom());
        return sslContext;

    }

    private void createSimpleIndex(ElasticsearchClient esClient, String index) throws IOException {
        BooleanResponse indexRes = esClient.indices().exists(ex -> ex.index(index));
        if (!indexRes.value()) {
            esClient.indices().create(c -> c
                    .index(index));
        }
    }

    private void createIndexWithDateMapping(ElasticsearchClient esClient, String index) throws IOException {
        BooleanResponse indexRes = esClient.indices().exists(ex -> ex.index(index));
        if (!indexRes.value()) {
            esClient.indices().create(c -> c
                    .index(index)
                    .mappings(m -> m
                            .properties("createdAt", p -> p
                                    .date(d -> d))
                            .properties("updatedAt", p -> p
                                    .date(d -> d))));

        }
    }
}

3. 创建通用的操作,让所有Reposity继承此类

 

package org.example.es.service;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.Refresh;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.transport.endpoints.BooleanResponse;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.example.es.util.TableInfoParamUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 业务实现类封装
 *
 * @author lyl
 * @version v1.0
 * @since 2025/5/21
 */
@Slf4j
public class EsService<T> {
    public final ElasticsearchClient esClient;

    /**
     * 索引字段,自动获取T对象里@Table注解字段名
     */
    public String index;
    private Class<T> clazz;


    /**
     * 获取当前类对象
     *
     * @return
     */
//    private Class<T> getClazz() {
//        return (Class<T>) GenericTypeUtils.resolveTypeArguments(getClass(), EsService.class)[0];
//    }
    public EsService(ElasticsearchClient esClient, Class<T> clazz) {
        this.esClient = esClient;
        // 获取当前类对应的数据库表名
        TableInfoParamUtil tableInfoParamUtil = new TableInfoParamUtil();
        //Class<T> clazz = getClazz();
        this.clazz = clazz;
        this.index = tableInfoParamUtil.getTableName(clazz);
    }


    /**
     * 列表
     *
     * @param params 参数列表,key为数据库字段名
     * @return
     */
    public List<T> list(Map<String, Object> params) {
        Class<T> clazz = this.clazz;
        try {
            List<Query> conditions = TableInfoParamUtil.changeMapParam(params);
            Query query = new Query.Builder().bool(b -> b.should(conditions)).build();
            SearchResponse<T> response = esClient.search(s -> s
                            .index(index)
                            .query(query)
                    , clazz);
            return response.hits().hits().stream()
                    .map(hit -> hit.source())
                    .collect(Collectors.toList());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 分页查询
     *
     * @param params    查询参数
     * @param pageNum   当前页码
     * @param pageSize  每页条数
     * @param sortField 排序字段
     * @return
     */
    public IPage<T> page(Map<String, Object> params, int pageNum, int pageSize, String sortField) {
        int offset = (pageNum - 1) * pageSize;
        List<Query> conditions = TableInfoParamUtil.changeMapParam(params);
        Query query = new Query.Builder().bool(b -> b.should(conditions)).build();
        try {
            SearchResponse<T> getArticle = esClient.search(ss -> ss
                            .index(index)
                            .size(pageSize) // how many results to return
                            .from(offset) // starting point
                            .query(query)
                            .sort(srt -> srt
                                    .field(fld -> fld
                                            .field(sortField)
                                            .order(SortOrder.Desc))) // last updated first
                    , this.clazz);
            if (getArticle.hits().hits().size() > 0) {
                List<T> collect = getArticle.hits().hits().stream()
                        .map(hit -> hit.source())
                        .collect(Collectors.toList());
                IPage<T> page = new Page<>(pageNum, pageSize);
                page.setRecords(collect);
                page.setTotal(getArticle.hits().total().value());
                return page;
            }
        } catch (IOException e) {
            log.error(e.getMessage());
        }
        return null;
    }


    /**
     * 保存
     *
     * @param nodeDocument
     */
    public void save(T nodeDocument) {
        IndexRequest<T> articleReq = IndexRequest.of((id -> id
                .index(index)
                .id(TableInfoParamUtil.getIdValue(nodeDocument))
                .refresh(Refresh.WaitFor)
                .document(nodeDocument)));
        try {
            esClient.index(articleReq).id();
        } catch (IOException e) {
            log.error(e.getMessage());
        }

    }


    /**
     * 批量保存
     *
     * @param nodeList
     */
    public boolean saveBatch(List<T> nodeList) {
        try {
            BulkRequest.Builder brBuilder = new BulkRequest.Builder();
            for (T product : nodeList) {
                brBuilder.operations(b -> b
                        .index(idx -> idx.index(index)
                                .id(TableInfoParamUtil.getIdValue(product))
                                .document(product)));
            }
            BulkRequest bulkRequest = brBuilder.build();
            BulkResponse response = esClient.bulk(bulkRequest);
            if (!response.errors()) {
                return true;
            }
        } catch (IOException e) {
            log.error(e.getMessage());
        }
        return false;
    }


    /**
     * 根据id删除数据
     *
     * @param id
     */
    public void delete(String id) {
        try {
            DeleteResponse deleteArticle = esClient.delete(d -> d
                    .index(index)
                    .id(id));
            if (!deleteArticle.result().name().equals("deleted")) {
                throw new RuntimeException("Failed to delete article");
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 更新
     *
     * @param nodeDocument
     */
    public void update(T nodeDocument, String id) {
        try {
            Class<T> cla = (Class<T>) nodeDocument.getClass();
            boolean exists = esClient.exists(b -> b
                    .index(index)
                    .id(id)
            ).value();
            if (exists) {
                System.out.println("exists");
                UpdateResponse<T> upArticle = esClient.update(up -> up
                        .index(index)
                        .id(id)
                        .doc(nodeDocument), cla);
                if (!upArticle.result().name().toLowerCase().equals("updated")) {
                    throw new RuntimeException("Article update failed");
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 根据id查询
     *
     * @param id 对象id
     * @return
     */
    public T getById(String id) {
        try {
            GetResponse<T> searchResponse = esClient.get(ss -> ss
                            .index(index)
                            .id(id)
                    , this.clazz);
            if (searchResponse.source() == null) {
                return null;
            }
            return searchResponse.source();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取总数
     *
     * @return
     */
    public long getCount(Map<String, Object> params) {
        try {
            SearchResponse<T> searchResponse = null;
            if (null != params && params.size() > 0) {
                List<Query> conditions = TableInfoParamUtil.changeMapParam(params);
                Query query = new Query.Builder().bool(b -> b.should(conditions)).build();
                searchResponse = esClient.search(ss -> ss
                                .index(index)
                                .size(0)
                                .query(query)
                        , this.clazz);
            } else {
                //查全部
                searchResponse = esClient.search(ss -> ss
                                .index(index)
                                .size(0)
                                .query(q -> q
                                        .matchAll(m -> m))
                        , this.clazz);
            }
            return searchResponse.hits().total().value();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }


    public void createIndex(){
        try {
            BooleanResponse indexRes = esClient.indices().exists(ex -> ex.index(index));
            if (!indexRes.value()) {
                esClient.indices().create(c -> c
                        .index(index));
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void deleteIndex(){
        try {
            BooleanResponse indexRes = esClient.indices().exists(ex -> ex.index(index));
            if (indexRes.value()) {
                esClient.indices().delete(d -> d
                        .index(index));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void createMapping() {
        try {
            BooleanResponse indexRes = esClient.indices().exists(ex -> ex.index(index));
            if (indexRes.value()) {
                esClient.indices().delete(d->d.index(index));
                esClient.indices().create(p -> p
                        .index(index)
                        .mappings(m -> m
                                .properties("createDate", pp -> pp
                                        .date(d -> d.format("yyyy-MM-dd")))
                                .properties("updateDate",pp->pp.date(d->d.format("yyyy-MM-dd HH:mm:ss")))));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

从实体对象分析es的索引和id自动注入。

package org.example.es.util;

import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import jakarta.persistence.Id;
import jakarta.persistence.Table;


import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 参数转换工具类
 *
 * @author lyl
 * @version v1.0
 * @since 2025/5/21
 */
public class TableInfoParamUtil {

    /**
     * 查询参数封装
     * @param params
     * @return
     */
    public static List<Query> changeMapParam(Map<String, Object> params) {
        List<Query> conditions = new ArrayList<>();

        params.forEach((k, v) -> {
            Query query = null;
            if (v instanceof String) {
                String value = v.toString();
                query = new MatchQuery.Builder()
                        .field(k)
                        .query(value).build()._toQuery();
            } else if (v instanceof Number) {
                query = new MatchQuery.Builder()
                        .field(k)
                        .query(Long.valueOf(v.toString())).build()._toQuery();
            } else if (v instanceof Boolean) {
                query = new MatchQuery.Builder()
                        .field(k)
                        .query(Boolean.valueOf(v.toString())).build()._toQuery();
            } else if (v instanceof Integer) {
                query = new MatchQuery.Builder()
                        .field(k)
                        .query(Integer.valueOf(v.toString())).build()._toQuery();
            } else if (v instanceof Float) {
                query = new MatchQuery.Builder()
                        .field(k)
                        .query(Float.valueOf(v.toString())).build()._toQuery();
            } else if (v instanceof Double) {
                query = new MatchQuery.Builder()
                        .field(k)
                        .query(Double.valueOf(v.toString())).build()._toQuery();
            }

            if (null != query) {
                conditions.add(query);
            }

        });
        return conditions;
    }


    /**
     * 获取表名,分析@Table注解
     * @param clazz
     * @return
     */
    public String getTableName(Class<?> clazz) {

        // 检查类是否有 Table 注解
        if (clazz.isAnnotationPresent(Table.class)) {
            // 获取注解实例
            Table tableAnnotation = clazz.getAnnotation(Table.class);
            // 读取注解的属性值
            String tableName = tableAnnotation.name();
            return tableName;
        }
        return null;
    }

    /**
     * 获取主键值
     * @param entity
     * @return
     */
    public static <T> String getIdValue(T entity) {
        Class<?> clazz = entity.getClass();

        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(Id.class)) {
                field.setAccessible(true); // 允许访问私有字段
                try {
                    String id=String.valueOf(field.get(entity));
                    return id;   // 获取字段值
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return null;

    }
}

业务类实现EsService接口,除了基本增加,修改,删除,查询外,编写自定义查询方法。

package org.example.es.service;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.RangeQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.RangeRelation;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import org.example.es.entity.StockBaseEntity;
import org.example.es.util.TableInfoParamUtil;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 业务实现类
 *
 * @author lyl
 * @version v1.0
 * @since 2025/5/26
 */
@Service
public class StockBaseResposity extends EsService<StockBaseEntity> {
    /**
     * 获取当前类对象
     *
     * @param esClient
     * @param clazz
     * @return
     */
    @Autowired
    public StockBaseResposity(ElasticsearchClient esClient) {
        super(esClient, StockBaseEntity.class);
    }


    /**
     * 把某一年的数据按7天分组
     * @return
     */

    public List<StockBaseEntity> search(){
        try {


            Query byMaxPrice = RangeQuery.of(r -> r
                    .date(n -> n
                            .field("createDate")
                            .gte("2024-09-01")
                            .lte("2025-04-01")
                            .format("yyyy-MM-dd"))
            )._toQuery();

            Query dateDistanceFeatureQuery = Query.of(q -> q.bool(b -> b
                    .must(m -> m.matchAll(mm -> mm))
                    .should(sh -> sh.distanceFeature(df -> df
                            .date(d -> d
                                    .field("createDate")
                                    .pivot(Time.of(t -> t.time("7d")))
                                    .origin("now"))))));
            //根据条件查询相应的数据
            Query query1 = Query.of(q->q.bool(b->b.must(byMaxPrice)));
            SearchResponse<StockBaseEntity> response = esClient.search(s -> s
                            .index(index)
                            .query(dateDistanceFeatureQuery)
                            .aggregations("dd",t->t.aggregations("day",
                                    a->a.dateHistogram(d->d.field("createDate").format("yyyy"))))
                            .size(100)
                    , StockBaseEntity.class);
            return response.hits().hits().stream()
                    .map(hit -> hit.source())
                    .collect(Collectors.toList());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }



}

三、高级查询

 1. 使用
    // 聚合查询示例

   public List<StockBaseEntity> search(){
        try {
 
            Query dateDistanceFeatureQuery = Query.of(q -> q.bool(b -> b
                    .must(m -> m.matchAll(mm -> mm))
                    .should(sh -> sh.distanceFeature(df -> df
                            .date(d -> d
                                    .field("createDate")
                                    .pivot(Time.of(t -> t.time("7d")))
                                    .origin("now"))))));
            //查某一年的数据
            SearchResponse<StockBaseEntity> response = esClient.search(s -> s
                            .index(index)
                            .query(dateDistanceFeatureQuery)
                            .aggregations("dd",t->t.aggregations("day",
                                    a->a.dateHistogram(d->d.field("createDate").format("yyyy"))))
                            .size(100)
                    , StockBaseEntity.class);
            return response.hits().hits().stream()
                    .map(hit -> hit.source())
                    .collect(Collectors.toList());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

 


 

 四、索引管理

 1. 创建索引

 

  public void createIndex(){
        try {
            BooleanResponse indexRes = esClient.indices().exists(ex -> ex.index(index));
            if (!indexRes.value()) {
                esClient.indices().create(c -> c
                        .index(index));
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

  2. 索引映射管理

可以在实体类中使用注解定义更详细的映射:

 Elasticsearch 默认支持的日期格式是 strict_date_optional_time,它支持以下形式的日期时间字符串:
yyyy-MM-dd
yyyy-MM-dd HH:mm:ss
yyyy-MM-dd HH:mm:ss.SSS
例如:
2023-10-01
2023-10-01 12:30:45
2023-10-01 12:30:45.123
如果你需要使用其他日期格式,可以在字段映射(mapping)中自定义日期格式。例如:

    public void createMapping() {
        try {
            BooleanResponse indexRes = esClient.indices().exists(ex -> ex.index(index));
            if (indexRes.value()) {
//                esClient.indices().delete(d->d.index(index));
                esClient.indices().create(p -> p
                        .index(index)
                        .mappings(m -> m
                                .properties("createDate", pp -> pp
                                        .date(d -> d.format("yyyy-MM-dd")))
                                .properties("updateDate",pp->pp.date(d->d.format("yyyy-MM-dd HH:mm:ss")))));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

然后在resources目录下创建对应的settings和mappings文件。

  五、性能优化

1. 批量操作:使用`bulk`API进行批量索引

    /**
     * 批量保存
     *
     * @param nodeList
     */
    public boolean saveBatch(List<T> nodeList) {
        try {
            BulkRequest.Builder brBuilder = new BulkRequest.Builder();
            for (T product : nodeList) {
                brBuilder.operations(b -> b
                        .index(idx -> idx.index(index)
                                .id(TableInfoParamUtil.getIdValue(product))
                                .document(product)));
            }
            BulkRequest bulkRequest = brBuilder.build();
            BulkResponse response = esClient.bulk(bulkRequest);
            if (!response.errors()) {
                return true;
            }
        } catch (IOException e) {
            log.error(e.getMessage());
        }
        return false;
    }

 

七、总结

Spring Boot与Elasticsearch的集成为开发强大的搜索功能提供了便利。通过elasticsearch-rest-client,我们可以:

1. 使用熟悉的Repository模式进行基本操作
2. 利用ElasticsearchTemplate实现复杂查询
3. 轻松管理索引和映射
4. 实现全文搜索、聚合分析等高级功能

在实际项目中,应根据业务需求合理设计索引结构,优化查询性能,并注意数据同步策略,确保Elasticsearch中的数据与主数据源保持一致。

通过本文介绍的方法,您可以快速在Spring Boot项目中集成Elasticsearch,构建高效的搜索和分析功能。


网站公告

今日签到

点亮在社区的每一天
去签到