首页 > 编程语言 >ES的java端API操作

ES的java端API操作

时间:2022-11-03 18:44:06浏览次数:50  
标签:java reportInfo EsTestInfo API query springframework org import ES

首先简单介绍下写这篇博文的背景,最近负责的一个聚合型的新项目要大量使用ES的检索功能,之前对es的了解还只是纯理论最多加个基于postman的索引创建操作,所以这次我得了解在java端如何编码实现;网上搜索是一搜一大堆,但是大部分都是些复制粘贴,毫无上下文可言,所以我是阵痛了好几天一点点的梳理整合,最后选择了基于springData封装的es操作,一点点编码测试最终满足了业务端的查询需求;下面就来从零开始介绍下如何在springboot项目中引入es并操控它;

ES简介

es是一个支持全文检索的开源的、高拓展的分布式搜索引擎,它基于Lucene而来,与solr这里也不比较了,这里更关注与java的整合,然后我们知道ES通过分词、倒排索引等技术能支持关系型数据库做不到或者说做不好的全文快速索引就行了,至于es的索引、文档、字段等关键属性这里也不介绍了,下面直接开始整合

项目搭建

一、整合springdata

需要注意的是springboot的版本不能太低,太低有些API不支持,我这里用的是2.6.8

1、引入依赖

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

2、创建索引实体

package com.darling.po;

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 java.util.Date;


@Data
@Document(indexName = "dll_index", createIndex=true)
public class EsTestInfo {

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

    @Field(type = FieldType.Text)
    private String name;


    @Field(type = FieldType.Text)
    private String desc;

    @Field(type = FieldType.Date)
    private Date publishDt;

}

上面的实体类就相当于索引的映射了,可以在里面根据业务需求设置索引名称、每个字段在es中的类型

3、创建DAO

package com.darling.repository;

import com.darling.po.EsTestInfo;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface EsReportInfoDao extends ElasticsearchRepository<EsTestInfo,Long> {
}

这里的写法类似于mybatisPlus,由springdata封装es的操作,后续针对单个索引的增删改查都会基于这个类

4、在项目的配置文件添加ES的配置信息

#配置ES的服务端地址和端口
elasticsearch.host=127.0.0.1
elasticsearch.port=9200

至此,配置基本完成,接下来启动项目就会自动根据实体的配置创建索引和映射了,非常方便;但是有两点需要注意:

  • 一是第一次启动时会创建,第二次启动时如果索引被删除了也会创建,但是如果索引没删除,只是改了索引实体里的配置,比方说把Text改成了Keyword,或者新增了映射,那么按上面的配置索引是不会自动修改的
  • 二是我自身理解的一个坑,刚开始我一直在纠结索引实体类应该放哪项目启动才会自动创建,或者说哪里有配置能指向索引实体所在的包,后来通过测试发现这里自动创建索引与实体类无关与DAO有关,只要你按要求创建了DAO并交给spring管理了,那么dao里泛型传入的实体类就会创建索引

二、增删改查操作

package com.darling.test;

import com.darling.model.PaginationModel;
import com.darling.model.TestQuery;
import com.darling.po.EsTestInfo;
import com.darling.repository.EsTestInfoDao;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.junit.Test;
import org.junit.platform.commons.util.StringUtils;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
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.test.context.junit4.SpringRunner;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class SpringDataESIndexTest {
    
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;


    @Autowired
    private EsTestInfoDao reportInfoDao;

    /**
     * 新增
     */
    @Test
    public void save(){
        EsTestInfo reportInfo = new EsTestInfo();
        reportInfo.setId("123");
        reportInfo.setName("张三01");
        reportInfo.setDesc("描述01");
        reportInfo.setCreateTime(new Date());
        reportInfoDao.save(reportInfo);
    }

    /**
     * 修改 ID不变 内容改变会自动修改
     */
    @Test
    public void update(){
        EsTestInfo reportInfo = new EsTestInfo();
        reportInfo.setId("123");
        reportInfo.setName("张三02");
        reportInfo.setDesc("描述02");
        reportInfo.setCreateTime(new Date());
        reportInfoDao.save(reportInfo);
    }

    /**
     * 根据id查询
     */
    @Test
    public void findById(){
        EsTestInfo reportInfo = reportInfoDao.findById(123L).get();
        System.out.println(reportInfo);
    }

    /**
     * 查询所有数据
     */
    @Test
    public void findAll(){
        Iterable<EsTestInfo> products = reportInfoDao.findAll();
        for (EsTestInfo reportInfo : products) {
            System.out.println(reportInfo);
        }
    }

    /**
     * 根据ID删除文档
     */
    @Test
    public void delete(){
        EsTestInfo reportInfo = new EsTestInfo();
        reportInfo.setId("222");
        reportInfoDao.delete(reportInfo);
    }

    /**
     * 批量新增
     */
    @Test
    public void saveAll(){
        List<EsTestInfo> productList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            EsTestInfo reportInfo = new EsTestInfo();
            reportInfo.setId("123");
            reportInfo.setName("张三01");
            reportInfo.setDesc("描述01");
            reportInfo.setCreateTime(new Date());
            productList.add(reportInfo);
        }
        reportInfoDao.saveAll(productList);
    }

    /**
     * 分页查询
     * 这里我没找如何添加条件进行分页查询所有在下面通过elasticsearchRestTemplate查了
     */
    @Test
    public void findByPageable(){
        //设置排序(排序方式,正序还是倒序,排序的id)
        Sort sort = Sort.by(Sort.Direction.DESC,"id");
        //当前期望查询的页码,第一页从0开始,1表示第二页
        int currentPage=0;
        int pageSize = 5;
        //设置查询分页
        PageRequest pageRequest = PageRequest.of(currentPage, pageSize,sort);
        //分页查询
        Page<EsTestInfo> productPage = reportInfoDao.findAll(pageRequest);
        for (EsTestInfo EsTestInfo : productPage.getContent()) {
            System.out.println(EsTestInfo);
        }
    }

    @Test
    public PaginationModel<EsTestInfo> queryTestDateQuery(TestQuery query){
        PaginationModel<EsTestInfo> res = new PaginationModel<>();
        int currentPage=query.getPageIndex()-1;
        int pageSize = query.getPageSize();
        PageRequest pageRequest = PageRequest.of(currentPage, pageSize);
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        BoolQueryBuilder textKeyBqb = new BoolQueryBuilder();
        if (StringUtils.isNotBlank(query.getTextKey())) {
            /**
             *  由于下面会用到must查询,所以此处用textKeyBqb再封装一个builder出来,否则
             *  和must同时查询此处会出现0匹配也返回结果的情况
             *  如果不想封装textKeyBqb,加上boolQueryBuilder.minimumShouldMatch(1)强制使es
             *  最少满足一个should子句才能返回结果也行
             */
            textKeyBqb.should(QueryBuilders.matchQuery("id", query.getTextKey()))
                    .should(QueryBuilders.matchQuery("name", query.getTextKey()))
                    .should(QueryBuilders.matchQuery("desc", query.getTextKey()))
                    .should(QueryBuilders.matchQuery("createTime", query.getTextKey()));
        }
        if (Objects.nonNull(query.getStartDate()) && Objects.nonNull(query.getEndDate())) {
            RangeQueryBuilder timeRangeQuery = QueryBuilders.rangeQuery("publishDt")
                    .gte(query.getStartDate().getTime())
                    .lte(query.getEndDate().getTime());
            boolQueryBuilder.must(timeRangeQuery);
        }
        if (Objects.nonNull(query.getRptStatus())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("rptStatus", query.getRptStatus()));
        }
        // 将上面封装的子句加入到主查询条件中
        boolQueryBuilder.must(textKeyBqb);
        log.info("<<<<<<<<<<<<<<<<<<boolQueryBuilder:{}",boolQueryBuilder);
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder).withPageable(pageRequest).build();
        SearchHits<EsTestInfo> search = elasticsearchRestTemplate.search(searchQuery, EsTestInfo.class);
        List<EsTestInfo> list = new ArrayList<>();
        for (SearchHit<EsTestInfo> productSearchHit : search) {
            EsTestInfo pro = productSearchHit.getContent();
            list.add(pro);
        }
        res.setList(list);
        res.setTotal(search.getTotalHits());
        res.setPageIndex(query.getPageIndex());
        res.setPageSize(query.getPageSize());
        return res;
    }

}

提示:代码中的PaginationModel为分页出参封装类、TestQuery为分页查询条件,这里就不贴出来了

三、总结

其实上面的基于dao的增删改查很简单,关键在于分页查询这块,特别是当BoolQueryBuilder 的should和must同时使用时把我一顿坑,下面简单介绍下

  • must表示返回的结果必须满足must子句的条件,并且参与计算分值;
  • should表示返回的结果可能满足should子句的条件.在一个bool查询中,如果没有must,有一个或者多个should子句,那么只要满足一个就可以返回.但是如果既有must,又有should,然后不做特殊设置的情况下,可能即使should字句一个都不匹配
    也会有结果返回;所以需要进行相应设置,具体代码里的注释有写供参考;

标签:java,reportInfo,EsTestInfo,API,query,springframework,org,import,ES
From: https://www.cnblogs.com/darling2047/p/16855481.html

相关文章

  • CodeForces - 204A Little Elephant and Interval
    题意:给定区间[l,r],问区间内有多少第一个数字和末尾数字一样的数。解:练习一些数位dp。先从高到低dp[pos][fir]表示dp到第pos个位置,以fir开头的串的数量,其中fir不为0。这道......
  • Redis Desktop Manager(Redis可视化工具)安装及使用教程
    1、https://blog.csdn.net/m0_55070913/article/details/123677891RedisDesktopManager(Redis可视化工具)安装及使用教程2、一、工具/材料官网下载:https://redisdesk......
  • Java 语音基础知识点 笔记
    Java语音基础知识点笔记(1)什么是变量?变量分为哪几类?String是最基本的数据类型吗?char型变量中能不能储存一个中文汉字?为什么?赋值语句“floatf=3.4;"是否正确?(2)Java中有没......
  • ThreeJS 创建水面报错 /textures/water/Water_1_M_Normal.jpg 404 解决方法
    threeJS创建水面报错 /textures/water/Water_1_M_Normal.jpg404根据路径找不到水面相关图片,原因是npm包中未包含图片,并且图片路径指向当前开发环境的src目录下,解决......
  • pytest运行测试用例时,控制台不打印警告信息
    使用pytest执行用例,控制台显示警告信息: 解决方法:新增pytest.ini文件,文件内容:[pytest]addopts=-pno:warnings 再次运行测试用例,控制台打印无警告信息......
  • Docker Desktop笔记3(VS Code-dev-containers)
    1、VSCode创建开发容器指定基础镜像:(README:​​vscode-dev-containers/containers/python-3atv0.202.3·microsoft/vscode-dev-containers·GitHub​​)FROMmcr.micro......
  • .net 6 api引入EF (DB first)
    项目添加:Microsoft.EntityFrameworkCore.ToolsPomelo.EntityFrameworkCore.MySql(这个是第三方的efmysql中间件)​Scaffold-DbContext-Force"Server=localhost;U......
  • javascript - 练习题:浅层克隆和深层克隆
    浅层克隆问:把obj 对象的内容,克隆到ojb1 上去。 varobj={name:'abc',age:123,sex:"female"}varobj1={}分析:这个没有引用值的对象,可以使用浅层克隆......
  • esp8266闪存文件系统<FS.h>
        1.存储的文件是编译后的;用闪存文件系统时要申请空间   2.SPIFFS.begin();启用闪存文件系统;3. SPIFFS.format();格式化SPIFFS.open(file_name......
  • 使用docker搭建一个WordPress网站
     【整体说明】网站需要三个容器:WordPress、MariaDB、Nginx,他们的关系如下图。这是一个典型的网站,mariadb作为后方的关系型数据库,端口号是3306;wordpress是中间的应用服务......