重磅来袭!!!Elasticsearch7.14.1(ES 7.14.1)与Springboot2.5.4的整合

1. 概述

 前面我们聊了 Elasticsearch(ES)集群的搭建,今天我们来聊一下,Elasticsearch(ES)集群如何与 Springboot 进行整合。

 Elasticsearch(ES)集群的搭建可参见我的另一篇文章《Elasticsearch(ES)集群的搭建》。

 Elasticsearch(ES)集群 我们采用的是目前最新的 7.14.1 版本。

 Springboot 我们采用的是目前最新的 2.5.4 版本。

 

2. Springboot 与  Elasticsearch(ES)的版本对应问题

 

从 spring-boot-starter-data-elasticsearch 的 jar 包依赖来看,最新的 Springboot 2.5.4 版本 对应的 ElasticSearch(ES)版本应该是 7.12.1。

经本人亲测,API完全可以兼容 ElasticSearch(ES)7.14.1 版本,所以完全不用担心兼容问题。

如果担心有风险,可搭建 ElasticSearch(ES)7.12.1 版本的集群,搭建方法与 7.14.1 版本完全一致,可参见我的另一篇文章《Elasticsearch(ES)集群的搭建》。

ElasticSearch(ES)7.12.1 版本下载地址:

https://www.elastic.co/cn/downloads/past-releases#elasticsearch

https://www.elastic.co/cn/downloads/past-releases/elasticsearch-7-12-1

 

3. Elasticsearch(ES )与 Springboot 的整合

3.1 使用 InterlliJ IDEA 创建Springboot项目

1)打开IDEA,选择 New —> Project...

 

 

 2)选择 Spring Initializr, 填写项目名称等信息,点击【Next】

 

 

3)依赖中勾选 Spring Data Elasticsearch(Access+Driver),点击【Finish】即可

 

 

4)pom.xml 中的主要依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

 

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

 

3.2 在 Elasticsearch(ES) 中创建索引 index_user 同时创建映射

 

特别说明

不建议使用 Java代码 对索引进行管理类操作,例如:创建索引、更新映射、删除索引。

类似在mysql,我们通常不会用 Java代码 去进行建库、建表、改表、删表的操作,只用代码对表中的数据进行增删改查操作。

 

PUT  http://192.168.1.8:9200/index_user

参数:

{
    "settings":{
        "index":{
            "number_of_shards":3,
            "number_of_replicas":1
        }
    },
    "mappings" : {
        "properties":{
            "user_id":{
                "type":"keyword"
            },
            "name":{
                "type": "text",
                "fields": {
                    "keyword": {
                        "ignore_above": 256,
                        "type": "keyword"
                    }
                },
                "analyzer":"ik_max_word"
            },
            "login_name":{
                "type":"keyword"
            },
            "age":{
                "type":"integer"
            },
            "birthday":{
                "type":"date"
            },
            "desc":{
                "type":"text",
                "analyzer":"ik_max_word"
            },
            "head_url":{
                "type":"text",
                "index":false
            }
        }
    }
}

 

3.3 配置 Springboot 配置文件

打开 application.yml 文件,将 Elasticsearch(ES)的集群信息配置在里面

spring:
  data:
    elasticsearch:
      client:
        reactive:
          endpoints: 192.168.1.8:9200,192.168.1.22:9200,192.168.1.144:9200
  elasticsearch:
    rest:
      uris: 192.168.1.8:9200,192.168.1.22:9200,192.168.1.144:9200

 

3.4 创建实体类 User

在实体类中用注解标识 实体 与 索引 的对应关系

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
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.Setting;

import java.util.Date;

@Builder
@Setter
@Getter
@Setting(shards = 3, replicas = 1)
@Document(indexName = "index_user", createIndex = false)
public class User {

    @Id
    @Field(store = true, name = "user_id", type = FieldType.Keyword)
    private String userId;

    @Field(store = true, searchAnalyzer = "ik_max_word", analyzer = "ik_max_word")
    private String name;

    @Field(store = true, name = "login_name", type = FieldType.Keyword)
    private String loginName;

    @Field(store = true, type = FieldType.Integer)
    private Integer age;

    @Field(store = true, type = FieldType.Date)
    private Date birthday;

    @Field(store = true, searchAnalyzer = "ik_max_word", analyzer = "ik_max_word")
    private String desc;

    @Field(store = true, name = "head_url", type = FieldType.Keyword)
    private String headUrl;

}

 

3.5 单条文档的新增或更新

这里我们使用单元测试,演示一下,Java代码是如何操作Elasticsearch(ES)的。

文档ID不存在则新增文档,ID存在则更新文档

import cn.zhuifengren.myelasticsearch.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;

import java.text.ParseException;
import java.text.SimpleDateFormat;

@SpringBootTest(classes = MyelasticsearchApplication.class)
public class ElasticsearchTest {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    
    @Test
    public void save() throws ParseException {

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

        User user = User.builder()
                .userId("2")
                .name("夏维尔")
                .loginName("xwe")
                .age(28)
                .birthday(sdf.parse("1992-06-06"))
                .desc("我是一名高级开发经理,每天坐地铁上班,在北京住,从不堵车")
                .headUrl("https://www.zhuifengren.cn/img/xwe.jpg")
                .build();

        elasticsearchRestTemplate.save(user);
    }

}

 

3.6 依据文档ID更新文档的部分字段

import cn.zhuifengren.myelasticsearch.pojo.User;
import cn.zhuifengren.myelasticsearch.utils.JsonUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

@SpringBootTest(classes = MyelasticsearchApplication.class)
public class ElasticsearchTest {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Test
    public void update() {

        Map<String, Object> params = new HashMap<>();
        params.put("name", "夏维尔5");

        Document document = Document.from(params);

        UpdateQuery updateQuery = UpdateQuery.builder("2")  // 2 是文档的ID
                .withDocument(document)
                .build();

        UpdateResponse result = elasticsearchRestTemplate.update(updateQuery, IndexCoordinates.of("index_user"));
        System.out.println(JsonUtils.objectToJson(result));  // 结果:{"result":"UPDATED"}
    }

}

 

3.7 依据文档ID获得文档

import cn.zhuifengren.myelasticsearch.pojo.User;
import cn.zhuifengren.myelasticsearch.utils.JsonUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

@SpringBootTest(classes = MyelasticsearchApplication.class)
public class ElasticsearchTest {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Test
    public void getById() {

        User user = elasticsearchRestTemplate.get("2", User.class);
        System.out.println(JsonUtils.objectToJson(user));
        // 结果:{"userId":"2","name":"夏维尔5","loginName":"xwe","age":28,"birthday":707760000000,"desc":"我是一名高级开发经理,每天坐地铁上班,在北京住,从不堵车","headUrl":"https://www.zhuifengren.cn/img/xwe.jpg"}
    }
}

 

3.8 依据文档ID删除文档

import cn.zhuifengren.myelasticsearch.pojo.User;
import cn.zhuifengren.myelasticsearch.utils.JsonUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

@SpringBootTest(classes = MyelasticsearchApplication.class)
public class ElasticsearchTest {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Test
    public void delete() {

        String result = elasticsearchRestTemplate.delete("2", User.class);
        System.out.println(JsonUtils.objectToJson(result));  // 结果:"2"
    }
}

 

3.9 分页检索

import cn.zhuifengren.myelasticsearch.pojo.User;
import cn.zhuifengren.myelasticsearch.utils.JsonUtils;
import org.elasticsearch.index.query.QueryBuilders;
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.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

@SpringBootTest(classes = MyelasticsearchApplication.class)
public class ElasticsearchTest {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Test
    public void searchForPage() {

        Pageable pageable = PageRequest.of(0,10);  // page 从第 0 页开始

        Query query = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchQuery("desc", "一名小学生")) 
                .withQuery(QueryBuilders.termQuery("age", 10)) 
                .withPageable(pageable)
                .build();

        SearchHits<User> result = elasticsearchRestTemplate.search(query, User.class);
        System.out.println(JsonUtils.objectToJson(result));
    }
}

 

结果Json:

{
    "totalHits": 1,
    "totalHitsRelation": "EQUAL_TO",
    "maxScore": 1,
    "scrollId": null,
    "searchHits": [
        {
            "index": "index_user",
            "id": "3",
            "score": 1,
            "sortValues": [],
            "content": {
                "userId": "3",
                "name": "迪士尼在逃仙柔",
                "loginName": "dsnzxr",
                "age": 10,
                "birthday": 1308672000000,
                "desc": "我是一名五年级的小学生,每天专车接专车送,中午在学校入伙,食堂菜可好了,上学期期末考试我拿了三好学生奖",
                "headUrl": "https://www.zhuifengren.cn/img/dsnzxr.jpg"
            },
            "highlightFields": {},
            "innerHits": {},
            "nestedMetaData": null,
            "routing": null,
            "explanation": null,
            "matchedQueries": []
        }
    ],
    "aggregations": null,
    "empty": false
}

 

 3.10 高亮的实现

import cn.zhuifengren.myelasticsearch.pojo.User;
import cn.zhuifengren.myelasticsearch.utils.JsonUtils;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
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.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

@SpringBootTest(classes = MyelasticsearchApplication.class)
public class ElasticsearchTest {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Test
    public void highlight() {

        Pageable pageable = PageRequest.of(0,10);  // page 从第 0 页开始

        HighlightBuilder.Field highlightField = new HighlightBuilder.Field("desc")
                                            .preTags("<span>")
                                            .postTags("</span>");

        Query query = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchQuery("desc", "一名小学生"))
                .withHighlightFields(highlightField)
                .withPageable(pageable)
                .build();

        SearchHits<User> result = elasticsearchRestTemplate.search(query, User.class);
        System.out.println(JsonUtils.objectToJson(result));
    }
}

 

结果Json:

{
    "totalHits": 3,
    "totalHitsRelation": "EQUAL_TO",
    "maxScore": 3.0418546,
    "scrollId": null,
    "searchHits": [
        {
            "index": "index_user",
            "id": "3",
            "score": 3.0418546,
            "sortValues": [],
            "content": {
                "userId": "3",
                "name": "迪士尼在逃仙柔",
                "loginName": "dsnzxr",
                "age": 10,
                "birthday": 1308672000000,
                "desc": "我是一名五年级的小学生,每天专车接专车送,中午在学校入伙,食堂菜可好了,上学期期末考试我拿了三好学生奖",
                "headUrl": "https://www.zhuifengren.cn/img/dsnzxr.jpg"
            },
            "highlightFields": {
                "desc": [
                    "我是<span>一名</span>五年级的<span>小学生</span>,每天专车接专车送,中午在学校入伙,食堂菜可好了,上学期期末考试我拿了三好<span>学生</span>奖"
                ]
            },
            "innerHits": {},
            "nestedMetaData": null,
            "routing": null,
            "explanation": null,
            "matchedQueries": []
        },
        {
            "index": "index_user",
            "id": "1",
            "score": 0.5957724,
            "sortValues": [],
            "content": {
                "userId": "1",
                "name": "僵尸猎手",
                "loginName": "jsls",
                "age": 25,
                "birthday": 636220800000,
                "desc": "我是一名房产经纪人,现在转行了,目前是一名运输工人",
                "headUrl": "https://www.zhuifengren.cn/img/jsls.jpg"
            },
            "highlightFields": {
                "desc": [
                    "我是<span>一名</span>房产经纪人,现在转行了,目前是<span>一名</span>运输工人"
                ]
            },
            "innerHits": {},
            "nestedMetaData": null,
            "routing": null,
            "explanation": null,
            "matchedQueries": []
        },
        {
            "index": "index_user",
            "id": "2",
            "score": 0.46563908,
            "sortValues": [],
            "content": {
                "userId": "2",
                "name": "夏维尔",
                "loginName": "xwe",
                "age": 28,
                "birthday": 707760000000,
                "desc": "我是一名高级开发经理,每天坐地铁上班,在北京住,从不堵车",
                "headUrl": "https://www.zhuifengren.cn/img/xwe.jpg"
            },
            "highlightFields": {
                "desc": [
                    "我是<span>一名</span>高级开发经理,每天坐地铁上班,在北京住,从不堵车"
                ]
            },
            "innerHits": {},
            "nestedMetaData": null,
            "routing": null,
            "explanation": null,
            "matchedQueries": []
        }
    ],
    "aggregations": null,
    "empty": false
}

 

3.11 自定义排序的实现

import cn.zhuifengren.myelasticsearch.pojo.User;
import cn.zhuifengren.myelasticsearch.utils.JsonUtils;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
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.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

@SpringBootTest(classes = MyelasticsearchApplication.class)
public class ElasticsearchTest {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Test
    public void sort() {

        Pageable pageable = PageRequest.of(0,10);  // page 从第 0 页开始

        HighlightBuilder.Field highlightField = new HighlightBuilder.Field("desc")
                .preTags("<span>")
                .postTags("</span>");

        SortBuilder<FieldSortBuilder> sortBuilder
                            = new FieldSortBuilder("age").order(SortOrder.DESC);

        Query query = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchQuery("desc", "一名小学生"))
                .withHighlightFields(highlightField)
                .withSort(sortBuilder)  // 排序可加多个
                .withPageable(pageable)
                .build();

        SearchHits<User> result = elasticsearchRestTemplate.search(query, User.class);
        System.out.println(JsonUtils.objectToJson(result));
    }
}

  

4. 综述

今天聊了一下 Elasticsearch7.14.1(ES 7.14.1)与 springboot2.5.4 的整合,希望可以对大家的工作有所帮助。

欢迎帮忙点赞、评论、转发、加关注 :)

关注追风人聊Java,每天更新Java干货。

 

posted @ 2021-09-24 12:52  追风人聊Java  阅读(456)  评论(1编辑  收藏  举报