springboot通过springdata整合es7.x
作者:mmseoamin日期:2023-12-21

首先要明确通过springdata操作es必须要将版本号和es的版本号对应上,否则会报错(倒不用完全一一对应,但版本号最好不要相差太多)。springdata引入的版本号由springboot的版本号决定,对应关系如下:

在这里插入图片描述

这里我用的版本号分别是:

es:elasticsearch:7.10.1

springboot:spring-boot-starter-parent:2.7.8

springdata:spring-boot-starter-data-elasticsearch:2.7.8

1、引入依赖,只关注springboot和es的部分就行



    4.0.0
    org.example
    elk
    1.0-SNAPSHOT
    elk
    Demo project for Spring Boot
    
        3.0.0
        UTF-8
        UTF-8
        1.8
        3.1.1
        3.3.1
        8.0.17
        4.0
        11.2.0.3
        1.1.13
        2.3.0
        2.6
        1.2.2
        2.5
        1.10
        1.10
        1.4.0
        0.7.0
        0.0.9
        7.2.23
        2.8.3
        4.4
        2.4.0
        2.9.9
        2.8.5
        1.2.60
        5.0.6
        1.18.4
    
    
        org.springframework.boot
        spring-boot-starter-parent
        2.7.8
    
    
        
            org.springframework.boot
            spring-boot-starter-test
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-aop
        
        
            org.springframework
            spring-context-support
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
            redis.clients
            jedis
        
        
            org.projectlombok
            lombok
            1.18.4
        
        
        
            org.apache.kafka
            kafka-clients
        
        
            com.github.danielwegener
            logback-kafka-appender
            0.2.0-RC1
        
        
            net.logstash.logback
            logstash-logback-encoder
            6.4
        
        
        
            org.springframework.boot
            spring-boot-starter-data-elasticsearch
        
        
        
            com.alibaba
            fastjson
            ${fastjson.version}
        
        
            net.sf.json-lib
            json-lib
            2.4
            jdk15
        
    
    
        ${project.artifactId}
        
            
                org.apache.maven.wagon
                wagon-ssh
                2.8
            
        
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    true
                
            
            
            
                org.apache.maven.plugins
                maven-surefire-plugin
                
                    true
                
            
        
    

2、修改配置文件

# es连接地址
spring:
  elasticsearch:
    uris: 192.168.3.22:9200    #如果是集群,用“,”分割
  data:
    elasticsearch:
      repositories:
        enabled: true
# Tomcat
server:
  tomcat:
    uri-encoding: UTF-8
    max-threads: 1000
    min-spare-threads: 30
  port: 8087
  connection-timeout: 5000ms
  servlet:
    context-path: /

3、定义实体类

package com.elk.escurd;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.GeoPointField;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import java.util.Date;
@Data
@Document(indexName = "invitation",createIndex = true)
public class Invitation {
    @Id
    private Long id;
//    //指定字段的索引方式,index是否索引、store是否存储、字段的分词方式、搜索时关键字分词的方式、type指定该字段的值以什么样的数据类型来存储
//    @Field(index=true,store=true,analyzer="ik_max_word",searchAnalyzer="ik_max_word",type=FieldType.Text)
    /* ik_smart:粗粒度分词 */
    @Field(analyzer = "ik_smart", type = FieldType.Text)
    private String name;
    /* ik_max_word:细粒度分词 */
    @Field(analyzer = "ik_max_word", type = FieldType.Text)
    private String country;
    @Field(type = FieldType.Integer)
    private Integer age;
    @Field(type = FieldType.Text)
    private String isDelete;
    @Field(type = FieldType.Text)
    private String status;
    @Field(type = FieldType.Text)
    private String sex;
    @Field(type = FieldType.Text)
    private String type;
    @Field(type = FieldType.Date)
    private Date createDate;
    //es中的位置字段,存储的是经纬度,方便进行范围搜索
    @GeoPointField
    private GeoPoint address;
}

4、定义基础操作接口:InvitationRepository

package com.elk.escurd;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
 * 此类和@Document中的createIndex = true配合,启动就可以将实体类变成索引注入到es中
 * 不需要写具体的实现,函数名遵循命名规范即可自动实现
 * 命名规则参考:https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#repositories.query-methods
 * 此类完成的是一些基础查询,复杂查询用ElasticsearchRestTemplate
 */
@Repository
public interface InvitationRepository extends ElasticsearchRepository {
    //根据名字查询 findBy+字段名
    List findByName(String name);
    //根据地址查询 findBy+字段名
    List findByAddress(String address);
    //根据地址和姓名查询  findBy+多个字段名之间And分隔
    List findByAddressAndName(String address,String name);
    //查询id小于某个值的数据  findBy+比大小的字段+LessThan
    List findByIdLessThan(int id);
    //查询年龄在多少-多少之间的   findBy+条件字段+Between
    List findByAgeBetween(Integer minAge,Integer maxAge);
}

5、定义索引管理类:IndexManage

package com.elk.escurd;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.stereotype.Service;
/**
 * 索引管理类
 */
@Service
public class IndexManage {
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
    /**
     * 创建索引和mapping
     * es不支持修改mapping,如果想要修改mapping,只能备份原来的数据,删除原有索引重新创建
     */
//    @PostConstruct
    public boolean  createIndex(){
        IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(Invitation.class);
        //如果存在索引,先删除索引
        if (indexOperations.exists()){
            indexOperations.delete();
        }
        //创建索引
        boolean a = indexOperations.create();
        if (a){
            //生成映射
            Document mapping = indexOperations.createMapping();
            //推送映射
            boolean b = indexOperations.putMapping(mapping);
            return b;
        }else {
            return a;
        }
    }
    public boolean deleteIndex() {
        IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(Invitation.class);
        boolean delete = indexOperations.delete();
        return delete;
    }
}

6、测试类

package com.elk.escurd;
import com.alibaba.fastjson.JSON;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.sort.GeoDistanceSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import java.util.Date;
import java.util.List;
/**
 * @author 954L
 * @create 2023/2/8 15:13
 */
@SpringBootTest
class InvitationTest {
    /**
     * 基础操作
     */
    @Autowired
    private InvitationRepository invitationRepository;
    /**
     * 复杂操作
     */
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    /**
     * 测试新增
     */
    @Test
    void testInsert() {
        Invitation invitation = new Invitation();
        invitation.setId(3l);
        invitation.setName("龟儿子");
        invitation.setCountry("我是美国佬");
        invitation.setAge(20);
        invitation.setIsDelete("0");
        invitation.setStatus("0");
        invitation.setSex("2");
        invitation.setType("2");
        invitation.setCreateDate(new Date());
        GeoPoint address =new GeoPoint(41.15998589289666,123.10144051130709);
        invitation.setAddress(address);
        invitationRepository.save(invitation);
    }
    /**
     * 查所有数据
     */
    @Test
    void testFindAll() {
        Iterable invitationIterable = invitationRepository.findAll();
        invitationIterable.forEach(x -> System.out.println(JSON.toJSONString(x)));
    }
    /**
     * 修改指定数据
     */
    @Test
    void testUpdate() {
        Invitation invitation = invitationRepository.findById(1L).orElse(null);
        System.out.println("修改前名称:" + invitation.getName());
        invitation.setName("龟儿子2");
        invitationRepository.save(invitation);
        invitation = invitationRepository.findById(1L).orElse(null);
        System.out.println("修改后名称:" + invitation.getName());
    }
    /**
     * 删除指定数据
     */
    @Test
    void testDelete() {
        invitationRepository.deleteById(1L);
        Invitation invitation = invitationRepository.findById(1L).orElse(null);
        System.out.println(invitation == null? "删除成功": "删除失败");
    }
    /**
     * 通过name查询
     */
    @Test
    void testfindByName(){
        List invitationList = invitationRepository.findByName("儿子");
        invitationList.forEach(x -> System.out.println(JSON.toJSONString(x)));
    }
    /**
     * 通过name查询
     */
    @Test
    void testfindByName2() {
        MatchQueryBuilder matchQueryBuilder = new MatchQueryBuilder("name", "儿子");
        NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(matchQueryBuilder)
            .withFields("id", "name", "country").build();
        SearchHits hits = elasticsearchRestTemplate.search(query, Invitation.class);
        List> searchHits = hits.getSearchHits();
        System.out.println("共" + hits.getTotalHits() + "条");
        searchHits.forEach(x -> System.out.println(JSON.toJSONString(x.getContent())));
    }
    /**
     * 复杂查询:通过范围、性别、年龄、类型、按距离升序、按时间倒序分页查询
     * @return
     */
    @Test
    void getInvitationList(){
        Integer page = 1;
        Integer size = 5;
        Double latitude = 41.1637913541259;
        Double longitude = 123.10181515177084;
        String sex = "3";
        Integer minage = 5;
        Integer maxage = 25;
        String type = "10";

        Pageable pageable = PageRequest.of(page - 1, size);
        //构建查询条件生成器
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        //拼接条件
        //指定字段范围查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.rangeQuery("age").gte(minage).lte(maxage));
        //指定字段查询
        QueryBuilder isdeleteBuilder = QueryBuilders.termQuery("isDelete", "0");
        boolQueryBuilder.must(isdeleteBuilder);
        QueryBuilder statusBuilder = QueryBuilders.termQuery("status","0");
//        boolQueryBuilder.mustNot(statusBuilder);
        boolQueryBuilder.must(statusBuilder);
        if (!"3".equals(sex)&&null!=sex){
            QueryBuilder sexBuilder = QueryBuilders.termQuery("sex",sex);
            boolQueryBuilder.must(sexBuilder);
        }
        if (!"10".equals(type)&&null!=type){
            QueryBuilder typeBuilder = QueryBuilders.termQuery("type",type);
            boolQueryBuilder.must(typeBuilder);
        }
        //以某点为中心,搜索指定范围
        GeoDistanceQueryBuilder distanceQueryBuilder = new GeoDistanceQueryBuilder("address");
        distanceQueryBuilder.point(latitude, longitude);
        //查询单位:m
        distanceQueryBuilder.distance(10000, DistanceUnit.METERS);
        boolQueryBuilder.filter(distanceQueryBuilder);
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
        //按距离升序
        GeoDistanceSortBuilder distanceSortBuilder =
                new GeoDistanceSortBuilder("address", latitude, longitude);
        distanceSortBuilder.unit(DistanceUnit.KILOMETERS);
        distanceSortBuilder.order(SortOrder.ASC);
        nativeSearchQueryBuilder.withSort(distanceSortBuilder);
        //按时间倒序
        SortBuilder timeSort = SortBuilders.fieldSort("createDate").order(SortOrder.DESC);
        nativeSearchQueryBuilder.withSort(timeSort);
        //分页
        nativeSearchQueryBuilder.withPageable(pageable);
        NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build();
        SearchHits hits = elasticsearchRestTemplate.search(searchQuery, Invitation.class);
        List> searchHits = hits.getSearchHits();
        System.out.println("共" + hits.getTotalHits() + "条");
        searchHits.forEach(x -> System.out.println(JSON.toJSONString(x.getContent())));
    }
}