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

「爱情、让人受尽委屈。」 2021-09-25 06:26 1244阅读 0赞

本文目的记录下新版本的Elasticsearch API查询使用

目录

  • why elasticsearch
  • concept
  • how use elasticsearch
    • 版本说明
    • 安装说明
    • 创建索引和映射
    • 新建Repository
    • 新建 service接口和实现类
    • POJO 封装对象
    • 测试

why elasticsearch

Elasticsearch 不多说了:

  • 用途广泛,社区活跃,Apache开源许可免费
  • 分布式多用户能力的全文搜索引擎
  • 可RESTful web接口,可多语言API接口
  • 它是用Java语言开发的
  • 企业级搜索引擎

我这里主要用作全文搜索引擎,市面上也有不少其他的简单易用的搜索引擎,不过考虑社区活跃程度,大众认知程度最终还是选择elasticsearch ,主要它的组合ELK可以作为分布式日志管理工具。

concept

  1. Elasticsearch很多概念与MySQL类似的,对比关系:

























ES Mysql
索引库(indices) Databases 数据库
类型(type) Table 数据表
文档(Document) Databases 数据库
字段(Field) Column 列
  1. Es的映射配置(mappings)
    指设置字段的数据类型、属性、是否索引、是否存储等特性。
  2. 集群相关的概念:
    索引集(Indices,index的复数):逻辑上的完整索引
    分片(shard):数据拆分后的各个部分
    副本(replica):每个分片的复制
  3. 分布式说明
    Es本身分布式,即便你只有一个节点,默认也会对你的数据进行分片和副本操作,
    当你向集群添加新数据时,数据也会在新加入的节点中进行平衡(负载均衡)。

how use elasticsearch

版本说明

软件环境:
JDK1.8
SpringBoot 2.3.8

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>2.3.8.RELEASE</version>
  5. <relativePath/> <!-- lookup parent from repository -->
  6. </parent>

ES, 这里和SpringBoot 对应的默认版本是 7.6.2

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
  4. </dependency>

springboot yml 文件配置说明:

  1. spring:
  2. elasticsearch:
  3. rest:
  4. uris: http://你的es服务器地址:9200

安装说明

ES 的安装步骤省略…网上一搜一大把,这里是docker单机版本的

  1. docker run -p 9200:9200 -p 9300:9300 -d --name=es -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" -e "discovery.type=single-node" elasticsearch:7.11.2

这里重点说一下中文分词:
ElasticSearch 默认采用分词器, 单个字分词 ,效果很差。

下面是es中文分词库,与Elasticsearch一起维护升级,版本也保持一致
https://github.com/medcl/elasticsearch-analysis-ik/releases

下载 zip压缩文件,直接解压到 elasticsearch/plugins 目录下新建一个文件夹名称为 ik
在这里插入图片描述
进入config目录,修改一下 IKAnalyzer.cfg.xml 将扩展的分词库加入进去,然后重启es即可,不然像停顿词 “的地得” 还是会做倒排索引的
在这里插入图片描述

创建索引和映射

这里说一下es7.x 相对于 6.x ,很多api过时了,需要使用新的。

这里测试创建一个索引叫“documentlibrary” ,存放各类文档。

重点说明:

  1. 如果是单机版本的一台服务器上的 es,那么 创建的index replicas 必须为0
  2. 因为es的分片副本不可和原分片在同一个节点上(即同一台服务或同一个虚拟操作系统中)
  3. import io.swagger.annotations.ApiModelProperty;
  4. import lombok.Data;
  5. import org.springframework.data.annotation.Id;
  6. import org.springframework.data.elasticsearch.annotations.Document;
  7. import org.springframework.data.elasticsearch.annotations.Field;
  8. import org.springframework.data.elasticsearch.annotations.FieldType;
  9. import java.io.Serializable;
  10. /** * 针对文档库中的文档存储在 es中的对象信息 * * @author admin */
  11. @Document(indexName = "documentlibrary", shards = 2, replicas = 0)
  12. @Data
  13. public class DocumentLibrary implements Serializable {
  14. private static final long serialVersionUID = 1L;
  15. /** * 主键 */
  16. @ApiModelProperty(value = "主键")
  17. @Id
  18. private Long id;
  19. /** * t_oss_upload_record 表id */
  20. @ApiModelProperty(value = "t_oss_upload_record 表id")
  21. @Field(name = "ossUploadRecord", type = FieldType.Long)
  22. private Long ossUploadRecord;
  23. /** * 文件分类 */
  24. @ApiModelProperty(value = "文件分类")
  25. @Field(name = "categories", type = FieldType.Keyword)
  26. private String categories;
  27. /** * 原始文件名称 */
  28. @ApiModelProperty(value = "原始文件名称")
  29. @Field(name = "originalFileName", type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
  30. private String originalFileName;
  31. /** * 上传服务器的最终地址 */
  32. @ApiModelProperty(value = "上传服务器的最终地址")
  33. @Field(name = "resultPath", type = FieldType.Keyword)
  34. private String resultPath;
  35. /** * 摘要 */
  36. @ApiModelProperty(value = "摘要")
  37. @Field(name = "summary", type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
  38. private String summary;
  39. /** * 整体内容抽取 */
  40. @ApiModelProperty(value = "【一般不展示该字段,只存储使用】整体内容抽取")
  41. @Field(name = "allContent", type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
  42. private String allContent;
  43. /** * 版本 */
  44. @ApiModelProperty(value = "版本")
  45. @Field(name = "rversion", type = FieldType.Keyword)
  46. private String rversion;
  47. /** * 创建人 */
  48. @ApiModelProperty(value = "创建人")
  49. @Field(name = "createUser", type = FieldType.Keyword)
  50. private String createUser;
  51. /** * 创建日期 */
  52. @ApiModelProperty(value = "创建日期")
  53. @Field(name = "createTime", type = FieldType.Text, fielddata = true)
  54. private String createTime;
  55. /** * 文件类型后缀 */
  56. @ApiModelProperty(value = "文件类型后缀")
  57. @Field(name = "fileType", type = FieldType.Keyword)
  58. private String fileType;
  59. /** * 关键字1 */
  60. @ApiModelProperty("关键字1")
  61. @Field(name = "Keyword1", type = FieldType.Keyword)
  62. private String Keyword1;
  63. /** * 关键字1 */
  64. @ApiModelProperty("关键字2")
  65. @Field(name = "Keyword2", type = FieldType.Keyword)
  66. private String Keyword2;
  67. /** * 关键字1 */
  68. @ApiModelProperty("关键字1")
  69. @Field(name = "Keyword3", type = FieldType.Keyword)
  70. private String Keyword3;
  71. /** * 关键字4 */
  72. @ApiModelProperty("关键字4")
  73. @Field(name = "Keyword4", type = FieldType.Keyword)
  74. private String Keyword4;
  75. /** * 关键字5 */
  76. @ApiModelProperty("关键字5")
  77. @Field(name = "Keyword5", type = FieldType.Keyword)
  78. private String Keyword5;
  79. }

新建Repository

  1. import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
  2. /** * 针对文档库中的文档存储在 es中的对象信息 * * @author guzt */
  3. public interface DocumentLibraryRepository extends ElasticsearchRepository<DocumentLibrary, Long> {
  4. }

新建 service接口和实现类

  1. import com.middol.colburn.oss.model.client.es.DocumentLibrary;
  2. import com.middol.colburn.oss.model.client.pojo.query.DocumentLibraryQuery;
  3. import com.middol.starter.common.pojo.vo.PageVO;
  4. /** * ES 服务类 * * @author admin */
  5. public interface DocumentLibraryService {
  6. /** * 保存一条数据类型 * * @param entity DocumentLibrary */
  7. void save(DocumentLibrary entity);
  8. /** * 详细条件全文检索 * * @param query DocumentLibraryQuery * @return PageInfo */
  9. PageVO<DocumentLibrary> search(DocumentLibraryQuery query);
  10. }

下面的search 实现的查询功能如下:
如果传递了 keywordFilterTxt 参数则做全文所有字段检索,如果传递其他字段则另外还进行过滤操作

  • addFields 指的是查询出哪些字段
  • StrUtil 是hutools里面的工具类
  • init 是初始化创建索引和映射

    import cn.hutool.core.util.StrUtil;
    import com.middol.colburn.oss.model.client.es.DocumentLibraryRepository;
    import com.middol.colburn.oss.model.client.es.DocumentLibrary;
    import com.middol.colburn.oss.model.client.pojo.query.DocumentLibraryQuery;
    import com.middol.colburn.oss.model.client.service.DocumentLibraryService;
    import com.middol.starter.common.pojo.vo.PageVO;
    import org.elasticsearch.index.query.BoolQueryBuilder;
    import org.elasticsearch.index.query.QueryBuilders;
    import org.elasticsearch.search.sort.SortBuilders;
    import org.elasticsearch.search.sort.SortOrder;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.data.domain.PageRequest;
    import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
    import org.springframework.data.elasticsearch.core.IndexOperations;
    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.stereotype.Service;

    import javax.annotation.PostConstruct;
    import java.util.List;
    import java.util.stream.Collectors;

    /* ES 服务类 @author admin */
    @Service(“documentLibraryServiceImpl”)
    @ConditionalOnClass({ ElasticsearchRestTemplate.class})
    public class DocumentLibraryServiceImpl implements DocumentLibraryService {

    1. protected Logger logger = LoggerFactory.getLogger(this.getClass());
    2. final
    3. ElasticsearchRestTemplate elasticsearchRestTemplate;
    4. final
    5. DocumentLibraryRepository documentLibraryRepository;
    6. public DocumentLibraryServiceImpl(ElasticsearchRestTemplate elasticsearchRestTemplate, DocumentLibraryRepository documentLibraryRepository) {
    7. this.elasticsearchRestTemplate = elasticsearchRestTemplate;
    8. this.documentLibraryRepository = documentLibraryRepository;
    9. }
  1. @PostConstruct
  2. public void init() {
  3. IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(DocumentLibrary.class);
  4. if (!indexOperations.exists()) {
  5. // 创建索引,会根据Item类的@Document注解信息来创建
  6. boolean result = indexOperations.create();
  7. logger.info("创建 elasticsearch 索引 DocumentLibrary, 创建结果={}", result);
  8. if (!result) {
  9. throw new RuntimeException("创建 elasticsearch 索引失败");
  10. } else {
  11. // 配置映射,会根据Item类中的id、Field等字段来自动完成映射
  12. indexOperations.createMapping();
  13. }
  14. }
  15. }
  16. @Override
  17. public void save(DocumentLibrary entity) {
  18. elasticsearchRestTemplate.save(entity);
  19. }
  20. @Override
  21. public PageVO<DocumentLibrary> search(DocumentLibraryQuery query) {
  22. PageVO<DocumentLibrary> pageVO = new PageVO<>();
  23. if (query.getPageNum() == null || query.getPageNum().equals(0)) {
  24. query.setPageNum(1);
  25. }
  26. if (query.getPageSize() == null || query.getPageSize().equals(0)) {
  27. query.setPageNum(10);
  28. }
  29. // 构建查询条件
  30. NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
  31. BoolQueryBuilder filter = QueryBuilders.boolQuery();
  32. // 添加基本分词查询
  33. if (StrUtil.isNotBlank(query.getCategories())) {
  34. filter.must(QueryBuilders.termQuery("categories", query.getCategories()));
  35. }
  36. if (StrUtil.isNotBlank(query.getFileType())) {
  37. filter.must(QueryBuilders.termQuery("fileType", query.getFileType()));
  38. }
  39. if (StrUtil.isNotBlank(query.getOriginalFileName())) {
  40. filter.must(QueryBuilders.matchQuery("originalFileName", query.getOriginalFileName()));
  41. }
  42. if (StrUtil.isNotBlank(query.getRversion())) {
  43. filter.must(QueryBuilders.termQuery("rversion", query.getRversion()));
  44. }
  45. if (StrUtil.isNotBlank(query.getCreateBeginTime())) {
  46. filter.must(QueryBuilders.rangeQuery("createTime").gte(query.getCreateBeginTime() + " 00:00:00"));
  47. }
  48. if (StrUtil.isNotBlank(query.getCreateEndTime())) {
  49. filter.must(QueryBuilders.rangeQuery("createTime").lte(query.getCreateBeginTime() + " 23:59:59"));
  50. }
  51. if (StrUtil.isNotBlank(query.getKeywordFilterTxt())) {
  52. queryBuilder.withQuery(QueryBuilders.queryStringQuery(query.getKeywordFilterTxt()));
  53. }
  54. queryBuilder.withFilter(filter);
  55. queryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC));
  56. queryBuilder.withPageable(PageRequest.of(query.getPageNum() - 1, query.getPageSize()));
  57. NativeSearchQuery nativeSearchQuery = queryBuilder.build();
  58. nativeSearchQuery.addFields("id", "ossUploadRecord", "categories","originalFileName",
  59. "resultPath","summary","rversion","createUser","createTime","fileType",
  60. "Keyword1","Keyword2","Keyword3","Keyword4","Keyword5","Keyword6","Keyword7","Keyword8");
  61. // 使用ElasticsearchRestTemplate进行复杂查询
  62. SearchHits<DocumentLibrary> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, DocumentLibrary.class);
  63. if (searchHits.getTotalHits() > 0) {
  64. List<DocumentLibrary> searchProductList = searchHits.stream().map(SearchHit::getContent).collect(Collectors.toList());
  65. pageVO.setPageNum(query.getPageNum());
  66. pageVO.setPageSize(query.getPageSize());
  67. pageVO.setRows(searchProductList);
  68. pageVO.setTotal(searchHits.getTotalHits());
  69. pageVO.setPages((int) Math.ceil((double) pageVO.getTotal() / query.getPageSize()));
  70. }
  71. return pageVO;
  72. }
  73. }

POJO 封装对象

这里的 POJO,query 和 pageVO 代码如下:

  1. import io.swagger.annotations.ApiModelProperty;
  2. import java.util.List;
  3. /** * 数据库分页查询列表封装对象 * * @param <T> 实体类对象 * @author admin */
  4. @Data
  5. public class PageVO<T> implements Serializable {
  6. private static final long serialVersionUID = 1L;
  7. /** * 总记录数 */
  8. @ApiModelProperty(value = "总记录数")
  9. private Long total;
  10. /** * 当前页 */
  11. @ApiModelProperty(value = "当前页")
  12. private Integer pageNum;
  13. /** * 每页的数量 */
  14. @ApiModelProperty(value = "每页的数量")
  15. private Integer pageSize;
  16. /** * 结果集 */
  17. @ApiModelProperty(value = "结果集")
  18. private List<T> rows;
  19. /** * 总页数 */
  20. @ApiModelProperty(value = "总页数")
  21. private Integer pages;
  22. }
  23. import io.swagger.annotations.ApiModelProperty;
  24. import lombok.Data;
  25. import java.io.Serializable;
  26. /** * 文件上传记录(OssUploadRecord)表查询条件封装对象 * * @author admin */
  27. @Data
  28. public class DocumentLibraryQuery implements Serializable {
  29. private static final long serialVersionUID = 1L;
  30. @ApiModelProperty("当前页码,从1开始")
  31. private Integer pageNum = 1;
  32. @ApiModelProperty("每页大小,默认10")
  33. private Integer pageSize = 10;
  34. @ApiModelProperty(value = "文件分类")
  35. private String categories;
  36. @ApiModelProperty(value = "文件名称全文检索")
  37. private String originalFileName;
  38. @ApiModelProperty(value = "关键字全文检索")
  39. private String keywordFilterTxt;
  40. @ApiModelProperty(value = "版本")
  41. private String rversion;
  42. @ApiModelProperty(value = "创建日期 yyyy-mm-dd")
  43. private String createBeginTime;
  44. @ApiModelProperty(value = "结束日期 yyyy-mm-dd")
  45. private String createEndTime;
  46. @ApiModelProperty(value = "文件类型后缀")
  47. private String fileType;
  48. }

测试

我这里写了一个controller方法:

  1. @ApiOperation(value = "测试ES", notes = "通过pagehelper插件进行物理分页")
  2. @ApiImplicitParam(name = "query", value = "前端参数封装", dataType = "DocumentLibraryQuery")
  3. @PostMapping(value = "testEs")
  4. public ResponseVO<PageVO<DocumentLibrary>> testEs(@RequestBody @Validated DocumentLibraryQuery query) {
  5. return ResponseVO.success(documentLibraryService.search(query));
  6. }

在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,1244人围观)

还没有评论,来说两句吧...

相关阅读