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

news2025/6/1 6:41:41

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,构建高效的搜索和分析功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2392151.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ubuntu从0到1搭建监控平台:本地部署到公网访问实战教程Cpolar穿透与Docker部署全过程

文章目录 前言1.关于Ward2.Docker部署3.简单使用ward4.安装cpolar内网穿透5. 配置ward公网地址6. 配置固定公网地址总结 前言 IT运维人员是否常为服务器管理系统的复杂操作所困扰&#xff1f;当海量性能指标图表与密集预警信号同时涌现时&#xff0c;这种信息过载往往让专业团…

vscode java debug terminal 中文乱码

现象 解决 快捷键 ctrl , 进入setting 配文件添加 "terminal.integrated.automationProfile.windows": {"path": "cmd","args": ["/k","chcp","65001"]}terminal 启动时&#xff0c;活动也改为 utf-…

3D PDF如何制作?SOLIDWORKS MBD模板定制技巧

SOLIDWORKS制作3D PDF模版 SOLIDWORKS MBD能够帮助工程师以清晰直观的方式描述产品尺寸信息。在3D PDF文件中&#xff0c;用户可以自由旋转和移动视图&#xff0c;方便查看模型的各个尺寸细节。 本文将带您一步步学习如何使用SOLIDWORKS MBD制作专业的3D PDF模板&#xff0c;…

Qt DateTimeEdit(时间⽇期的微调框)

使⽤ QDateEdit 作为⽇期的微调框. 使⽤ QTimeEdit 作为时间的微调框 使⽤ QDateTimeEdit 作为时间⽇期的微调框. 这⼏个控件⽤法⾮常相似, 我们以 QDateTimeEdit 为例进⾏介绍. QDateTimeEdit 核⼼属性 属性说明dateTime时间⽇期的值. 形如 2000/1/1 0:00:00date单纯⽇期…

C# 类和继承(屏蔽基类的成员)

屏蔽基类的成员 虽然派生类不能删除它继承的任何成员&#xff0c;但可以用与基类成员名称相同的成员来屏蔽&#xff08;mask&#xff09; 基类成员。这是继承的主要功能之一&#xff0c;非常实用。 例如&#xff0c;我们要继承包含某个特殊方法的基类。该方法虽然适合声明它的…

基于vue框架的动物园饲养管理系统a7s60(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;饲养员,健康登记,工作进度,动物信息,进食信息,动物健康,动物医治,饲料信息,工作留言 开题报告内容 基于Vue框架的动物园饲养管理系统开题报告 一、研究背景与意义 &#xff08;一&#xff09;研究背景 随着城市化进程加快和公众对生…

WPS自动换行

换行前 换行后 快捷键 第一步&#xff1a;启用「自动换行」功能 选中目标单元格/区域&#xff1a;点击需要设置的单元格&#xff08;或拖动选中多个单元格&#xff09;。开启自动换行&#xff08;3种方式任选&#xff09;&#xff1a; 快捷按钮&#xff1a;在顶部菜单栏点击「…

maven中的grpc编译插件protobuf-maven-plugin详解

protobuf-maven-plugin 是 Maven 中用于编译 Protocol Buffers&#xff08;protobuf&#xff09;文件并生成对应语言代码&#xff08;如 Java、C、Python 等&#xff09;的插件。在 gRPC 项目中&#xff0c;它常被用来生成服务端和客户端所需的代码。以下是该插件的详细解析&am…

服务发现Nacos

目录 Nacos server 安装 注册服务到Nacos server 接口访问Nacos server中的已注册服务 Nacos控制台介绍 Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 在分布式服务应用中&#xff0c;各类服务需要统一的注册、统一的管理&#xff0c;这个组件工具…

社群分享:义乌|杭州电商|店群卖家,私域鱼塘运营的排单系统开源|私域鱼塘运营|返款软件开源

熟悉东哥的朋友都知道&#xff0c;我自己也运营一个电商社群&#xff0c;主要是针对玩私域|鱼塘的电商玩家。 在当前电商环境下&#xff0c;社群分享型电商、店群卖家及私域鱼塘运营者&#xff0c;面临着日益复杂的订单管理和客服调度问题。传统的人工处理不仅效率低…

C#回调函数深度解析

文章目录 前言什么是回调函数C#中实现回调的方式委托(Delegate)事件(Event)Action和FuncPredicateAsyncCallback匿名方法和Lambda表达式 回调函数实际应用场景异步编程事件处理策略模式LINQ查询 回调函数的优缺点优点缺点 最佳实践与注意事项总结相关资源 前言 在现代软件开发…

通义智文开源QwenLong-L1: 迈向长上下文大推理模型的强化学习

&#x1f389; 动态 2025年5月26日: &#x1f525; 我们正式发布&#x1f917;QwenLong-L1-32B——首个采用强化学习训练、专攻长文本推理的LRM模型。在七项长文本文档问答基准测试中&#xff0c;QwenLong-L1-32B性能超越OpenAI-o3-mini和Qwen3-235B-A22B等旗舰LRM&#xff0c…

低代码——表单生成器以form-generator为例

主要执行流程说明&#xff1a; 初始化阶段 &#xff1a; 接收表单配置对象formConf深拷贝配置&#xff0c;初始化表单数据和验证规则处理每个表单组件的默认值和特殊配置&#xff08;如文件上传&#xff09; 渲染阶段 &#xff1a; 通过render函数创建el-form根组件递归渲染表…

linux centos 服务器性能排查 vmstat、top等常用指令

背景:项目上经常出现系统运行缓慢,由于数据库服务器是linux服务器,记录下linux服务器性能排查常用指令 vmstat vmstat介绍 vmstat 命令报告关于内核线程、虚拟内存、磁盘、陷阱和 CPU 活动的统计信息。由 vmstat 命令生成的报告可以用于平衡系统负载活动。系统范围内的这…

LiveGBS国标视频平台收流模式:UDP、TCP被动与TCP主动传输模式之差异剖析

LiveGBS国标视频平台收流模式&#xff1a;UDP、TCP被动与TCP主动传输模式之差异剖析 1、背景2、信令传输3、视频流传输3.1、UDP传输模式3.2、TCP被动传输模式3.3、TCP主动传输模式 4、WEB配置流传输模式4.1、编辑模式4.2、下拉切换模式 5、搭建GB28181视频直播平台 1、背景 在…

Tomcat 使用与配置全解

一、 Tomcat简介 Tomcat服务器是Apache的一个开源免费的Web容器。它实现了JavaEE平台下部分技术规范&#xff0c;属于轻量级应用服务器。 1. Tomcat版本 Tomcat版本 JDK版本 Servlet版本 JSP版本 10.0.X 8 and later 5.0 3.0 9.0.x 8 and later 4.0 2.3 8.0.x 7…

aws instance store 的恢复

1: aws instance store 要在launch instance 才可以创建,而且,通过snapshot 恢复后,instance store 里面的数据会丢失。 下面是创建instance store 的过程,和通过两种方式恢复,发现/etc/fstab 不同的写法,有的不能启动: [root@ip-xx ~]# lsblk NAME MAJ:MIN RM …

EasyRTC音视频实时通话助力微信小程序:打造低延迟、高可靠的VoIP端到端呼叫解决方案

一、方案概述​ 在数字化通信浪潮下&#xff0c;端到端实时音视频能力成为刚需。依托庞大用户生态的微信小程序&#xff0c;是实现此类功能的优质载体。基于WebRTC的EasyRTC音视频SDK&#xff0c;为小程序VoIP呼叫提供轻量化解决方案&#xff0c;通过技术优化实现低延迟通信&a…

STM32 SPI通信(软件)

一、SPI简介 SPI&#xff08;Serial Peripheral Interface&#xff09;是由Motorola公司开发的一种通用数据总线四根通信线&#xff1a;SCK&#xff08;Serial Clock&#xff09;、MOSI&#xff08;Master Output Slave Input&#xff09;、MISO&#xff08;Master Input Slav…

每日刷题c++

快速幂 #include <iostream> using namespace std; #define int long long int power(int a, int b, int p) {int ans 1;while (b){if (b % 2){ans * a;ans % p; // 随时取模}a * a;a % p; // 随时取模b / 2;}return ans; } signed main() {int a, b, p;cin >> a …