Spring boot 2.5 集成 Elasticsearch 实现增删改查以及全文搜索

Spring boot 2.5 集成 Elasticsearch 实现增删改查以及全文搜索,第1张

Spring boot 2.5 集成 Elasticsearch 实现增删改查以及全文搜索

文章目录
      • 1 摘要
      • 2 核心 Maven 依赖
      • 3 核心代码
        • 3.1 application 配置
        • 3.2 实体类
        • 3.3 基础增删改查(CRUD)
        • 3.4 全文搜索(**重点**)
        • 3.5 SpringBoot 启动类
      • 4 注意事项
        • 4.1 SpringBoot properties/yml 配置
        • 4.2 字母大小写敏感问题
        • 4.3 分词问题
      • 5 推荐参考资料
      • 6 Github 源码

1 摘要

Elasticsearch 是一款基于 Apache Lucene 的优秀的搜索服务器。本文将介绍基于 SpringBoot 2.5 集成 Elasticsearch 7 实现基本增删改查以及全文搜索。

Elasticsearch 官网: https://www.elastic.co

Elasticsearch 安装教程:

centOS 7 Elasticsearch 7.16 安装使用教程

2 核心 Maven 依赖
./demo-elasticsearch/pom.xml
        
        
            org.springframework.data
            spring-data-elasticsearch
            ${elastic.springdata.version}
        

其中 ${elastic.springdata.version} 的版本为 4.3.0

其他相关依赖

        
        
            org.springframework.boot
            spring-boot-starter-web
            ${springboot.version}
        
        
        
            org.springframework.boot
            spring-boot-starter-validation
            ${springboot.version}
        
        
        
            cn.hutool
            hutool-all
            ${hutool.version}
        
        
        
            io.springfox
            springfox-boot-starter
            ${springfox-swagger3.version}
        

版本信息

        2.5.7
        5.7.17
        3.0.0

注意事项:

Swagger 3.0 不支持 SpringBoot 2.6+

Springfox 3.0.0 is not working with Spring Boot 2.6.0

3 核心代码 3.1 application 配置
./demo-elasticsearch/src/main/resources/application.yml
## spring
spring:
  application:
    name: demo-elasticsearch
  elasticsearch:
    rest:
      uris: 192.168.1.110:9200
      username: elastic
      password: elastic666
      connection-timeout: 10S
      read-timeout: 30S
      sniffer:
        interval: 5m
        delay-after-failure: 1m
3.2 实体类
./demo-elasticsearch/src/main/java/com/ljq/springboot/elasticsearch/model/entity/BlogEntity.java
package com.ljq.springboot.elasticsearch.model.entity;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.ToString;
import org.springframework.data.elasticsearch.annotations.document;


@Data
@ToString(callSuper = true)
@document(indexName = "blog")
@ApiModel(value = "博客信息实体类", description = "博客信息实体类")
public class BlogEntity extends baseEntity {

    private static final long serialVersionUID = -2124422309475024490L;

    
    @ApiModelProperty(value = "标题", name = "title")
    private String title;
    
    @ApiModelProperty(value = "作者", name = "author")
    private String author;
    
    @ApiModelProperty(value = "内容", name = "content")
    private String content;
    
    @ApiModelProperty(value = "阅读数量", name = "countRead")
    private Integer countRead;
    
    @ApiModelProperty(value = "点赞数量", name = "countLike")
    private Integer countLike;
    
    @ApiModelProperty(value = "客户端时间戳(精确到秒)", name = "clientTimestamp")
    private Integer clientTimestamp;

}
./demo-elasticsearch/src/main/java/com/ljq/springboot/elasticsearch/model/entity/baseEntity.java
package com.ljq.springboot.elasticsearch.model.entity;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedDate;

import java.io.Serializable;
import java.util.Date;


@Data
public class baseEntity implements Serializable {

    private static final long serialVersionUID = -3003658740476069858L;

    
    @Id
    @ApiModelProperty(value = "id,主键", name = "id")
    private String id;
    
    @CreatedDate
    @ApiModelProperty(value = "创建时间", name = "createTime")
    private Long createTime;
    
    @LastModifiedDate
    @ApiModelProperty(value = "修改时间", name = "updateTime")
    private Date updateTime;
}

org.springframework.data.elasticsearch.annotations.document 用于标注索引,类似于关系型数据库中的表名

org.springframework.data.annotation.Id 用于指定主键

org.springframework.data.elasticsearch.annotations.Field 用于指定字段,默认实体类中所有字段都会保存到 elasticsearch 中,所以可以不在字段上写这个注解

3.3 基础增删改查(CRUD)
./demo-elasticsearch/src/main/java/com/ljq/springboot/elasticsearch/service/impl/BlogServiceImpl.java
package com.ljq.springboot.elasticsearch.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.ljq.springboot.elasticsearch.common.api.ApiMsgEnum;
import com.ljq.springboot.elasticsearch.common.api.ApiResult;
import com.ljq.springboot.elasticsearch.model.entity.BlogEntity;
import com.ljq.springboot.elasticsearch.model.param.*;
import com.ljq.springboot.elasticsearch.service.BlogService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
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.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.stereotype.Service;

import java.util.Objects;
import java.util.stream.Collectors;


@Slf4j
@Service("blogService")
public class BlogServiceImpl implements BlogService {

    @Autowired
    private ElasticsearchRestTemplate elasticTemplate;

    
    @Override
    public ApiResult save(BlogAddParam addParam) {
        BlogEntity blogEntity = new BlogEntity();
        BeanUtil.copyProperties(addParam, blogEntity, CopyOptions.create().ignoreError().ignoreNullValue());
        elasticTemplate.save(blogEntity);
        return ApiResult.success(blogEntity);
    }

    
    @Override
    public ApiResult queryOne(BlogQueryOneParam queryOneParam) {
        return ApiResult.success(elasticTemplate.get(queryOneParam.getId(), BlogEntity.class));
    }

    
    @Override
    public ApiResult update(BlogUpdateParam updateParam) {
        BlogEntity blogEntity = elasticTemplate.get(updateParam.getId(), BlogEntity.class);
        if (Objects.isNull(blogEntity)) {
            return ApiResult.fail(ApiMsgEnum.BLOG_NOT_EXIST);
        }
        BeanUtil.copyProperties(updateParam, blogEntity, CopyOptions.create().ignoreError().ignoreNullValue());
        elasticTemplate.save(blogEntity);
        return ApiResult.success(blogEntity);
    }

    
    @Override
    public ApiResult delete(BlogDeleteOneParam deleteOneParam) {
        BlogEntity blogEntity = elasticTemplate.get(deleteOneParam.getId(), BlogEntity.class);
        if (Objects.isNull(blogEntity)) {
            return ApiResult.fail(ApiMsgEnum.BLOG_NOT_EXIST);
        }
        elasticTemplate.delete(deleteOneParam.getId(), BlogEntity.class);
        return ApiResult.success();
    }


}

3.4 全文搜索(重点)
./demo-elasticsearch/src/main/java/com/ljq/springboot/elasticsearch/service/impl/BlogServiceImpl.java
    
    @Override
    public ApiResult> queryPage(BlogQueryPageParam queryPageParam) {
        Pageable pageable = PageRequest.of(queryPageParam.getCurrentPage() - 1, queryPageParam.getPageSize());
        // 构建查询条件
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder filter = QueryBuilders.boolQuery();
        // id-精确查询-idsQuery
        if (StrUtil.isNotBlank(queryPageParam.getId())) {
           filter.must(QueryBuilders.idsQuery().addIds(queryPageParam.getId()));
        }
        // 标题-模糊查询-matchQuery
        if (StrUtil.isNotBlank(queryPageParam.getTitle())) {
            filter.must(QueryBuilders.matchQuery("title",
                            queryPageParam.getTitle()).operator(Operator.OR).fuzziness(Fuzziness.AUTO));
        }
        // 作者-精确查询-queryStringQuery
        if (StrUtil.isNotBlank(queryPageParam.getAuthor())) {
            filter.must(QueryBuilders.queryStringQuery(
                    queryPageParam.getAuthor()).defaultField("author").fuzziness(Fuzziness.AUTO));
        }
        // 内容-模糊查询-fuzzyQuery
        if (StrUtil.isNotBlank(queryPageParam.getContent())) {
            filter.must(QueryBuilders.fuzzyQuery("content",
                            queryPageParam.getContent().toLowerCase()).fuzziness(Fuzziness.AUTO));
        }
        // 全文查询-模糊查询-multiMatchQuery
        if (StrUtil.isNotBlank(queryPageParam.getKeyword())) {
            filter.must(QueryBuilders.multiMatchQuery(
                    queryPageParam.getKeyword(),"title", "author", "content").fuzziness(Fuzziness.AUTO));
        }
        // 客户端时间戳-范围查询-rangeQuery
        if (Objects.nonNull(queryPageParam.getMinClientTimestamp())) {
            filter.must(QueryBuilders.rangeQuery("clientTimestamp")
                    .gte(queryPageParam.getMinClientTimestamp()));
        }
        if (Objects.nonNull(queryPageParam.getMaxClientTimestamp())) {
            filter.must(QueryBuilders.rangeQuery("clientTimestamp")
                    .lte(queryPageParam.getMaxClientTimestamp()));
        }

        searchQueryBuilder.withFilter(filter);
        // 分页信息
        searchQueryBuilder.withPageable(pageable);

        NativeSearchQuery query = searchQueryBuilder.build();
        SearchHits searchHits = elasticTemplate.search(query, BlogEntity.class);
        log.info("搜索结果: {}n{}", searchHits, JSONUtil.toJsonStr(searchHits.getSearchHits()));
        Page page = PageableExecutionUtils.getPage(searchHits.getSearchHits().stream()
                .map(SearchHit::getContent).collect(Collectors.toList()),pageable, searchHits::getTotalHits);
        return ApiResult.success(page);
    }
3.5 SpringBoot 启动类
./demo-elasticsearch/src/main/java/com/ljq/springboot/elasticsearch/DemoElasticsearchApplication.java
package com.ljq.springboot.elasticsearch;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.elasticsearch.config.EnableElasticsearchAuditing;


@EnableElasticsearchAuditing
@SpringBootApplication(scanbasePackages = "com.ljq.springboot.elasticsearch")
public class DemoElasticsearchApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoElasticsearchApplication.class, args);
    }

}
4 注意事项 4.1 SpringBoot properties/yml 配置

SpringBoot elasticsearch 自动化配置类

org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration

SpringBoot 2.5.7 :

支持的配置类配置前缀org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientPropertiesspring.elasticsearch.rest

SpringBoot 2.6.1 :

支持的配置类是否过时配置前缀org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties否spring.elasticsearchorg.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientProperties否spring.elasticsearch.restorg.springframework.boot.autoconfigure.elasticsearch.DeprecatedElasticsearchRestClientProperties是spring.elasticsearch.rest

兼容性配置示例:

## spring
spring:
  application:
    name: demo-elasticsearch
  elasticsearch:
    rest:
      uris: 192.168.1.110:9200
      username: elastic
      password: elastic666
      connection-timeout: 10S
      read-timeout: 30S
      sniffer:
        interval: 5m
        delay-after-failure: 1m

4.2 字母大小写敏感问题

termQuery fuzzyQuery 区分大小写,推荐 *** 作方案: (1)在应用程序中将用户输入的搜索词统一转化为小写;(2) 在Elasticsearch 中添加转小写过滤器

spring-boot - 如何在 Elasticsearch 中搜索不匹配大小写的精确文本

4.3 分词问题

matchQuery 只能匹配一个完整的单词,如果数据库中有 「spring」 这个单词,搜索条件为 「sprin」 则无法搜索到,而 fuzzyQuery 可以

elasticsearch 默认的中文分词是按照一个字一个字进行分词,英文分词则是按照一个完整的单词进行分词,如"黄鹤楼" 可以分出 “黄”,“鹤”,"楼"三个词;“springboot” 则就是一个单词

5 推荐参考资料

Spring Data Elasticsearch - Reference documentation

Elasticsearch实战篇——Spring Boot整合ElasticSearch

Introduction to Spring Data Elasticsearch – Baeldung

SpringBoot Elasticsearch 7.x 多条件分页查询

ElasticSearch集成SpringData史上最全查询教程

Elasticsearch Queries with Spring Data

SpringBoot + ElasticSearch系列: springboot中使用QueryBuilders、NativeSearchQuery ElasticsearchRestTemplate 实现复杂查询

6 Github 源码

Gtihub 源码地址 : https://github.com/Flying9001/springBootDemo

个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5672764.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存