第九节 SpringBoot集成Elasticsearch

亮子 2021-06-17 02:15:29 22969 1 1 0

1)、添加依赖

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

也可以在创建工程的时候选择依赖:

图片alt

版本控制
演示代码使用的是7.8.1这个版本

    <properties>
        <java.version>1.8</java.version>
        <elasticsearch.version>7.8.1</elasticsearch.version>
    </properties>

如果以上方法无法生效,那可能需要按下面的代码来修改了:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>transport</artifactId>
                    <groupId>org.elasticsearch.client</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--引入es的RestHighLevelClient依赖:-->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.8.1</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-client</artifactId>
            <version>7.8.1</version>
        </dependency>

2)、创建实体类

package com.shenmazong.demoestemplate.pojo;

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;

/**
 * @author 军哥
 * @version 1.0
 * @description: TODO
 * @date 2021/7/4 13:34
 */

@Data
@Document(indexName = "tb_poem")
public class TbPoem {
    @Id
    private Long id;

    @Field(type = FieldType.Text, analyzer = "ik_smart")
    private String name;

    @Field(type = FieldType.Keyword)
    private String dynasty;

    @Field(type = FieldType.Keyword)
    private String author;

    @Field(type = FieldType.Text, analyzer = "ik_smart")
    private String content;
}

3)、索引操作

(1)创建索引

@SpringBootTest
class DemoEsTemplateApplicationTests {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Test
    void testCreate() {
        if(!elasticsearchRestTemplate.indexOps(TbPoem.class).exists()) {
            elasticsearchRestTemplate.indexOps(TbPoem.class).create();
            System.out.println("create success");
            return;
        }

        System.out.println("exist....");
        return;
    }
}

注意:创建索引时,也许是ElasticsearchRestTemplate的一个bug,无法把映射同时创建,后期有待调试,通过一下代码来自定义映射:

    @Test
    public void testCreate() {

        //-- 删除
        if(elasticsearchRestTemplate.indexOps(TbPoem.class).exists()) {
            elasticsearchRestTemplate.indexOps(TbPoem.class).delete();
        }

        //-- 新建
        elasticsearchRestTemplate.indexOps(TbPoem.class).create();

        //-- 设置mapping
        Document mapping = elasticsearchRestTemplate.indexOps(TbPoem.class).createMapping();
        System.out.println(mapping);
        elasticsearchRestTemplate.indexOps(TbPoem.class).putMapping(mapping);
    }

(2)删除索引

    @Test
    void testDelete() {
        if(!elasticsearchRestTemplate.indexOps(TbPoem.class).exists()) {
            elasticsearchRestTemplate.indexOps(TbPoem.class).delete();
            System.out.println("delete success");
            return;
        }

        System.out.println("not exist....");
        return;
    }

4)、文档操作

(1)添加单个文档

    @Test
    void testAdd() {
        TbPoem tbPoem = new TbPoem();
        tbPoem.setId(1L);
        tbPoem.setName("听雨");
        tbPoem.setAuthor("蒋捷");
        tbPoem.setDynasty("唐朝");
        tbPoem.setContent("少年听雨阁楼上,红烛昏罗帐。");

        elasticsearchRestTemplate.save(tbPoem);
    }

(2)添加多个文档

    @Test
    void testBulkAdd() {
        ArrayList<TbPoem> tbPoems = new ArrayList<>();

        // 添加多个对象
        // ... 略

        elasticsearchRestTemplate.save(tbPoems);
    }

(3)获取一个文档

    @Test
    void testGetDoc() {
        TbPoem tbPoem = elasticsearchRestTemplate.get("1", TbPoem.class);
        System.out.println(tbPoem);
    }

(4)更新文档

    @Test
    void updateDoc() {
        TbPoem tbPoem = elasticsearchRestTemplate.get("1", TbPoem.class);
        tbPoem.setContent("少年听雨阁楼上,红烛昏罗帐。中年听雨客舟,");

        elasticsearchRestTemplate.save(tbPoem);
    }

(5)删除文档

    @Test
    void testDeleteDoc() {
        TbPoem tbPoem = new TbPoem();
        tbPoem.setId(1L);
        //-- 方法1
        elasticsearchRestTemplate.delete(tbPoem);

        //-- 方法2
        elasticsearchRestTemplate.get("1", TbPoem.class);
    }

5)搜索文档

(1)搜索说明

  • 单个匹配termQuery
//不分词查询 参数1: 字段名,参数2:字段查询值,因为不分词,所以汉字只能查询一个字,英语是一个单词.
QueryBuilder queryBuilder=QueryBuilders.termQuery("fieldName", "fieldlValue");
  • 分词查询,采用默认的分词器
QueryBuilder queryBuilder2 = QueryBuilders.matchQuery("fieldName", "fieldlValue");
  • 多个匹配
//不分词查询,参数1: 字段名,参数2:多个字段查询值,因为不分词,所以汉字只能查询一个字,英语是一个单词.
QueryBuilder queryBuilder=QueryBuilders.termsQuery("fieldName", "fieldlValue1","fieldlValue2...");
  • 分词查询,采用默认的分词器
QueryBuilder queryBuilder= QueryBuilders.multiMatchQuery("fieldlValue", "fieldName1", "fieldName2", "fieldName3");
  • 匹配所有文件,相当于就没有设置查询条件
QueryBuilder queryBuilder=QueryBuilders.matchAllQuery();

(2)termQuery

    @PostMapping(value = "/term")
    public ResultResponse term(
            @RequestParam("keyWord") String keyWord,
            @RequestParam("pageNum") Integer pageNum,
            @RequestParam("pageSize") Integer pageSize
            ) {

        //--1 构造分页
        if(pageNum > 0) {
            pageNum = pageNum -1;
        }
        PageRequest pageRequest = PageRequest.of(pageNum, pageSize);

        //--2 builder
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        //--3 构造查询
        NativeSearchQuery query = builder.withPageable(pageRequest)
                .withQuery(QueryBuilders.termQuery("name", keyWord))
                .build();

        //--4 执行查询
        long count = elasticsearchRestTemplate.count(query, TbHero.class);
        SearchHits<TbHero> searchHits = elasticsearchRestTemplate.search(query, TbHero.class);

        //--5 获取查询的结果
        List<SearchHit<TbHero>> hitsSearchHits = searchHits.getSearchHits();
        hitsSearchHits.forEach(item -> {
            TbHero tbHero = item.getContent();
            System.out.println(tbHero);
        });

        //--6 手动分页
        ArrayList<TbHero> tbHeroes = new ArrayList<>();
        hitsSearchHits.forEach(item -> {
            TbHero tbHero = item.getContent();
            tbHeroes.add(tbHero);
        });

        HashMap<String, Object> map = new HashMap<>();
        map.put("records", tbHeroes);
        map.put("pageNum", pageNum+1);
        map.put("pageSize", pageSize);
        map.put("count", count);

        return ResultResponse.SUCCESS(map);
    }

(3)termsQuery

查询关键字之间是的关系

    @Test
    void testTermsQuery() {
        Pageable pageable = PageRequest.of(0, 10);
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        NativeSearchQuery searchQuery = builder.withPageable(pageable)
                .withQuery(QueryBuilders.termsQuery("content", "宫女", "日月"))
                .build();

        SearchHits<TbPoem> search = elasticsearchRestTemplate.search(searchQuery, TbPoem.class);
        List<SearchHit<TbPoem>> searchHits = search.getSearchHits();
        for (SearchHit<TbPoem> searchHit : searchHits) {
            TbPoem tbPoem = searchHit.getContent();
            System.out.println(tbPoem);
        }
    }

(4)matchQuery

    @Test
    void testMatchQuery() {
        Pageable pageable = PageRequest.of(0, 10);
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        NativeSearchQuery searchQuery = builder.withPageable(pageable)
                .withQuery(QueryBuilders.matchQuery("content", "宫 女"))
                .build();

        SearchHits<TbPoem> search = elasticsearchRestTemplate.search(searchQuery, TbPoem.class);
        List<SearchHit<TbPoem>> searchHits = search.getSearchHits();
        for (SearchHit<TbPoem> searchHit : searchHits) {
            TbPoem tbPoem = searchHit.getContent();
            System.out.println(tbPoem);
        }
    }

(5)multiMatchQuery

    @Test
    void testMultiMatchQuery() {
        Pageable pageable = PageRequest.of(0, 10);
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        NativeSearchQuery searchQuery = builder.withPageable(pageable)
                .withQuery(QueryBuilders.multiMatchQuery("宫 女", "content", "name"))
                .build();

        SearchHits<TbPoem> search = elasticsearchRestTemplate.search(searchQuery, TbPoem.class);
        List<SearchHit<TbPoem>> searchHits = search.getSearchHits();
        for (SearchHit<TbPoem> searchHit : searchHits) {
            TbPoem tbPoem = searchHit.getContent();
            System.out.println(tbPoem);
        }
    }

(5)查询结果排序

    @Test
    void testMultiMatchQuery() {
        Pageable pageable = PageRequest.of(0, 10);
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        // 使用排序
        SortBuilder order = new FieldSortBuilder("author").order(SortOrder.DESC);

        NativeSearchQuery searchQuery = builder.withPageable(pageable)
                .withQuery(QueryBuilders.multiMatchQuery("宫 女", "content", "name"))
                .withSort(order)
                .build();

        SearchHits<TbPoem> search = elasticsearchRestTemplate.search(searchQuery, TbPoem.class);
        List<SearchHit<TbPoem>> searchHits = search.getSearchHits();
        for (SearchHit<TbPoem> searchHit : searchHits) {
            TbPoem tbPoem = searchHit.getContent();
            System.out.println(tbPoem);
        }
    }

(6)查询结果高亮

    @PostMapping(value = "/highlight")
    public ResultResponse highlight(
            @RequestParam("keyWord") String keyWord,
            @RequestParam("pageNum") Integer pageNum,
            @RequestParam("pageSize") Integer pageSize
    ) {

        //--1 构造分页
        if(pageNum > 0) {
            pageNum = pageNum -1;
        }
        PageRequest pageRequest = PageRequest.of(pageNum, pageSize);

        //--2 builder
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font color='red>").postTags("</font>").field("name");

        //--3 构造查询
        NativeSearchQuery query = builder.withPageable(pageRequest)
                .withQuery(QueryBuilders.matchQuery("name", keyWord))
                .withHighlightBuilder(highlightBuilder)
                .build();

        //--4 执行查询
        long count = elasticsearchRestTemplate.count(query, TbHero.class);
        SearchHits<TbHero> searchHits = elasticsearchRestTemplate.search(query, TbHero.class);

        //--5 获取查询的结果
        List<SearchHit<TbHero>> hitsSearchHits = searchHits.getSearchHits();
        hitsSearchHits.forEach(item -> {
            TbHero tbHero = item.getContent();
            System.out.println(tbHero);
        });

        //--6 手动分页
        ArrayList<TbHero> tbHeroes = new ArrayList<>();
        hitsSearchHits.forEach(item -> {
            TbHero tbHero = item.getContent();

            List<String> names = item.getHighlightField("name");
            if(names != null && names.size()>0) {
                String s = names.get(0);
                tbHero.setName(s);
            }

            tbHeroes.add(tbHero);
        });

        HashMap<String, Object> map = new HashMap<>();
        map.put("records", tbHeroes);
        map.put("pageNum", pageNum+1);
        map.put("pageSize", pageSize);
        map.put("count", count);

        return ResultResponse.SUCCESS(map);
    }

6)、ElasticSearch实体类定义参考

(1) 参考1

/**
 * 用在类上作用:将Emp的对象映射成ES中一条json格式文档
 * indexName: 用来指定这个对象的转为json文档存入那个索引中 要求:ES服务器中之前不能存在此索引名
 * type     : 用来指定在当前这个索引下创建的类型名称
 *
 * @Author Christy
 * @Date 2021/4/29 21:22
 */
@Data
@Document(indexName = "christy",type = "user")
public class User {
    @Id //用来将对象中id属性与文档中_id 一一对应
    private String id;

    // 用在属性上 代表mapping中一个属性 一个字段 type:属性 用来指定字段类型 analyzer:指定分词器
    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String name;

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

    @Field(type = FieldType.Date)
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date bir;

    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String content;

    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String address;
}

(2)参考2

package com.shenmazong.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

/**
 * 小说表
 * @TableName tb_story
 */
@TableName(value ="tb_story")
@Data
@Document(indexName = "tb_story", shards = 1, replicas = 1)
public class TbStory implements Serializable {
    /**
     * 小说ID
     */
    @TableId(type = IdType.AUTO)
    @Id
    private Integer id;

    /**
     * 小说名字
     */
    @Field(type = FieldType.Text, analyzer = "ik_smart")
    private String name;

    /**
     * 小说作者
     */
    @Field(type = FieldType.Keyword)
    private String author;

    /**
     * 小说内容
     */
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String content;

    /**
     * 点赞状态:0支持1反对
     */

    @Field(type = FieldType.Integer)
    private Integer status;

    /**
     * 软删除:0正常1删除
     */
    @Field(type = FieldType.Integer)
    private Integer deleted;

    /**
     * 创建时间
     */
    @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
    private Date createTime;

    /**
     * 修改时间
     */
    @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
    private Date updateTime;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}

参考网址: