ES的java端API操作
首先简单介绍下写这篇博文的背景,最近负责的一个聚合型的新项目要大量使用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修改文档 推荐使用此方法修改数据
*/
public void updateRecommend() {
String id = "123";
//构建文档对象
Document document = Document.create();
// 需要修改的字段
document.putIfAbsent("likeStatus",1);
// 文档ID
document.setId(id);
//构建UpdateQuery
UpdateQuery build = UpdateQuery.builder(id).withDocument(document) .withScriptedUpsert(true) .build();
// 执行修改操作
elasticsearchRestTemplate.update(build, IndexCoordinates.of("index_name"));
}
/**
* 根据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字句一个都不匹配
也会有结果返回;所以需要进行相应设置,具体代码里的注释有写供参考;
ES的java端API操作的更多相关文章
- es使用java的api操作
基本环境的创建 pom依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns=&q ...
- ES系列十五、ES常用Java Client API
一.简介 1.先看ES的架构图 二.ES支持的客户端连接方式 1.REST API http请求,例如,浏览器请求get方法:利用Postman等工具发起REST请求:java 发起httpClien ...
- 读《分布式一致性原理》JAVA客户端API操作3
更新数据 客户端可以通过zookeeper的API来更新一个节点的数据内容,有如下两个接口: public Stat setData(final String path, byte data[], i ...
- 读《分布式一致性原理》JAVA客户端API操作2
创建节点 通过客户端API来创建一个数据节点,有一下两个接口: public String create(final String path, byte data[], List<ACL> ...
- Java原生API操作XML
使用Java操作XML的开源框架比较多,如著名的Dom4J.JDOM等,但个人认为不管你用那个框架都要对JDK原生的API有所了解才能更得心应手的应用.本篇就来简单了解下原生的XML API. JAV ...
- zookeeper的Java端API应用
1. 基本使用 org.apache.zookeeper.Zookeeper是客户端入口主类,负责建立与server的会话.它提供了表1所示几类主要方法: 功能 描述 create 在本地目录树中创建 ...
- 读《分布式一致性原理》JAVA客户端API操作
创建会话 客户端可以通过创建一个Zookeeper实例来连接服务器.4种构造方法如下 ZooKeeper(connectString, sessionTimeout, watcher): ZooKee ...
- MongoDB -- JAVA基本API操作
package com.example.mongodb.mongodb.demo; import com.mongodb.MongoClient; import com.mongodb.client. ...
- 1.java soap api操作和发送soap消息
转自:https://blog.csdn.net/lbinzhang/article/details/84721359 1. /** * soap请求 * * @return * @throws Ex ...
随机推荐
- Semaphore-停车场
模拟20辆车进停车场 停车场容纳总停车量5. 当一辆车进入停车场后,显示牌的剩余车位数响应的减1. 每有一辆车驶出停车场后,显示牌的剩余车位数响应的加1. 停车场剩余车位不足时,车辆只能在外面等待 p ...
- HDU2065 “红色病毒”问题 (指数型母函数经典板题)
题面 医学界发现的新病毒因其蔓延速度和Internet上传播的"红色病毒"不相上下,被称为"红色病毒",经研究发现,该病毒及其变种的DNA的一条单链中,胞嘧啶, ...
- tomcat 10无法使用jstl 如何添加依赖
以Tomcat 10.0.23 idea 2021.1版本为例需要添加依赖 <dependency> <groupId>org.glassfish.web</grou ...
- 并发与并行,同步和异步,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang并发编程之GoroutineEP13
如果说Go lang是静态语言中的皇冠,那么,Goroutine就是并发编程方式中的钻石.Goroutine是Go语言设计体系中最核心的精华,它非常轻量,一个 Goroutine 只占几 KB,并且这 ...
- day29--Java泛型02
Java泛型02 5.自定义泛型 5.1自定义泛型类 基本语法: class 类名<T,R...>{//-表示可以有多个泛型 成员 } 注意细节: 普通成员可以使用泛型(属性.方法) 使用 ...
- Spring(四)-声明式事务
Spring-04 声明式事务 1.事务的定义 事务就是由一组逻辑上紧密关联的多个工作单元(数据库操作)而合并成一个整体,这些操作要么都执行,要么都不执行. 2.事务的特性:ACID 1)原子性A : ...
- 什么?MySQL 8.0 会同时修改两个ib_logfilesN 文件?
GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. GreatSQL是MySQL的国产分支版本,使用上与MySQL一致. 作者介绍:孙黎,GreatDB 认证DBA 问题现象 ...
- LSB隐写术
此为北京理工大学某专业某学期某课程的某次作业 一.项目背景 1.隐写术 隐写术是一门关于信息隐藏的技巧与科学,所谓信息隐藏指的是不让除预期的接收者之外的任何人知晓信息的传递事件或者信息的内容. 2.L ...
- dp-位移模型(数字三角形演变)
由数字三角形问题演变而来下面的题: https://www.cnblogs.com/sxq-study/p/12303589.html 一:规定位移方向 题目: Hello Kitty想摘点花生送给她 ...
- [DOM]获取元素:根据ID、标签名、HTML5新增的方法、特殊元素获取
目录 [DOM]获取元素:根据ID.标签名.HTML5新增的方法.特殊元素获取 1.根据 ID 获取[.getElementById( )] 2.根据标签名获取[.getElementsByTagNa ...