ES 8.x 新版本中,Type 概念被弃用,所以新版 JavaAPI 也相应做出了改变,使用更加简便。ES 官方从 7.15 起开始建议使用新的 JavaAPI

1、依赖

<!-- elasticsearch-java --><dependency><groupId>co.elastic.clients</groupId><artifactId>elasticsearch-java</artifactId><version>8.1.1</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.3</version></dependency><dependency><groupId>jakarta.json</groupId><artifactId>jakarta.json-api</artifactId><version>2.0.1</version></dependency><dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId><version>2.13.3</version></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>2.6.7</version></dependency>
  • NoClassDefFoundError 异常

java.lang.NoClassDefFoundError: jakarta/json/JsonException

如果出现这个异常,要加入 jakarta.json-api 包,版本 2.x

  • LocalDateTime

如果实体类中有 LocalDateTime 字段,要加入 jackson-datatype-jsr310,可以实现序列化与反序列化;同时实体类中的 LocalDateTime 字段加如下注解:

@JsonDeserialize(using = LocalDateTimeDeserializer.class) // 反序列化
@JsonSerialize(using = LocalDateTimeSerializer.class) // 序列化
private LocalDateTime time;

2、实体类

2.1、文档实体类 EsDocument.java

如果有自定义的构造器,一定不要忘记把无参构造器补上;@NoArgsConstructor,否则无法反序列化。

package com.tuwer.pojo;import lombok.Data;
import lombok.NoArgsConstructor;import java.time.Instant;/*** <p>ES文档实体类</p>* ----------------------* 一定要有无参构造器;否则反序列化会失败* ----------------------** @author 土味儿* Date 2022/8/9* @version 1.0*/
@Data
@NoArgsConstructor
public class EsDocument {/*** 文档id*/private String id;/*** 文档类型* 公共 0、私有 1...*/private Integer type;/*** 文档标题*/private String title;/*** 文档内容*/private String content;/*** 文档属主* 公开文档没有属主*/private String owner;/*** 文档url*/private String url;/*** 时间(采集/更新)*///@JsonDeserialize(using = LocalDateTimeDeserializer.class) // 反序列化//@JsonSerialize(using = LocalDateTimeSerializer.class) // 序列化//private LocalDateTime time;private Long time;public EsDocument(String id, Integer type, String title, String content, String owner, String url) {this.id = id;this.type = type;this.title = title;this.content = content;this.owner = owner;this.url = url;//this.time = LocalDateTime.now();//this.time = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();// 当前时刻this.time = Instant.now().getEpochSecond();}
}

2.2、文档VO对象类

package com.tuwer.pojo.vo.es;import lombok.Data;/*** <p>文档视图层对象</p>* @author 土味儿* Date 2022/9/17* @version 1.0*/
@Data
public class EsDocVo {/*** 文档id*/private String id;/*** 类型*/private Integer type;/*** 文档标题*/private String title;/*** 文档内容*/private String content;/*** 文档url*/private String url;/*** 属主*/private String owner;/*** 时间* 1年前、5个月前、3星期前、5天前、8小时前、47分钟前、刚刚*/private String time;
}

2.3、分页对象类 ESPage.java

package com.tuwer.pojo;import lombok.Data;import java.util.List;/*** @author 土味儿* Date 2022/9/17* @version 1.0*/
@Data
public class EsPage {private String keyword;private Long total;private Integer current = 1;private Integer pageSize = 10;private List<EsDocVo> records;
}

3、工具类 EsUtil.java

重点类

  • 基础操作:获取客户端(同步/异步)、获取Transport、close();仅供内部调用

  • 索引操作类:封装在内部类 Index 中,crud 操作

  • 文档操作类:封装在内部类 Doc 中,crud 操作,分页、高亮…

package com.tuwer.util;import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldSort;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.ExistsRequest;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
import co.elastic.clients.elasticsearch.core.bulk.DeleteOperation;
import co.elastic.clients.elasticsearch.core.bulk.IndexOperation;
import co.elastic.clients.elasticsearch.core.search.Highlight;
import co.elastic.clients.elasticsearch.core.search.HighlightField;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.indices.*;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.tuwer.pojo.EsDocument;
import com.tuwer.pojo.vo.es.EsDocVo;
import com.tuwer.pojo.vo.es.EsPage;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import java.io.IOException;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;/*** <p>ES操作工具类</p>* -----------------------------* 调用异步客户端就是异步方法;* 异步方法也要关闭transport;* 如果连接太多,又没有及时关闭,会报异常** 默认:异步方法* 索引名称/Id:一律转为小写* 文档Id:可以为大写,无须转换* -----------------------------** @author 土味儿* Date 2022/8/9* @version 1.0*/
@Slf4j
@SuppressWarnings("all")
@Component
public class EsUtil {public Index index = new Index();public Doc doc = new Doc();// ===================== 索引操作(封装在Index内部类中) ============================public class Index {/*** 创建索引(同步)** @param indexName* @return true:成功,false:失败*/@SneakyThrowspublic Boolean createSync(String indexName) {// 索引名称转为小写String iName = indexName.toLowerCase(Locale.ROOT);//String iName = indexName;// 获取【索引客户端对象】ElasticsearchIndicesClient indexClient = getEsClient().indices();/*** ===== 判断目标索引是否存在(等价于下面的Lambda写法)=====* ---- 1、构建【存在请求对象】* ExistsRequest existsRequest = new ExistsRequest.Builder().index(indexName).build();* ---- 2、判断目标索引是否存在* boolean flag = indexClient.exists(existsRequest).value();*/boolean flag = indexClient.exists(req -> req.index(iName)).value();//CreateIndexResponse createIndexResponse = null;boolean result = false;if (flag) {// 目标索引已存在//System.out.println("索引【" + indexName + "】已存在!");log.info("索引【" + iName + "】已存在!");} else {// 不存在// 获取【创建索引请求对象】//CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder().index(indexName).build();// 创建索引,得到【创建索引响应对象】//CreateIndexResponse createIndexResponse = indexClient.create(createIndexRequest);//createIndexResponse = indexClient.create(req -> req.index(indexName));result = indexClient.create(req -> req.index(iName)).acknowledged();//System.out.println("创建索引响应对象:" + createIndexResponse);if (result) {log.info("索引【" + iName + "】创建成功!");} else {log.info("索引【" + iName + "】创建失败!");}}// 关闭transportclose();return result;}/*** 创建索引(异步)** @param indexName* @return*/@SneakyThrowspublic Boolean create(String indexName) {// 转为小写String iName = indexName.toLowerCase(Locale.ROOT);// 异步索引客户端ElasticsearchIndicesAsyncClient indexClient = getEsAsyncClient().indices();// 查询索引是否存在;get()方法阻塞boolean isExist = indexClient.exists(existsRequest -> existsRequest.index(iName)).get().value();// 创建索引boolean result = false;if (isExist) {log.info("索引【" + iName + "】已存在!");} else {// 当前索引不存在,创建索引result = indexClient.create(createIndexRequest -> createIndexRequest.index(iName)).get().acknowledged();if (result) {log.info("索引【" + iName + "】创建成功!");} else {log.info("索引【" + iName + "】创建失败!");}}// 关闭transportclose();return result;}/*** 查询索引(同步)** @param indexName* @return*/@SneakyThrowspublic Map<String, IndexState> query(String indexName) {// 转为小写String iName = indexName.toLowerCase(Locale.ROOT);// 获取【索引客户端对象】ElasticsearchIndicesClient indexClient = getEsClient().indices();// 查询结果;得到【查询索引响应对象】GetIndexRequest getIndexRequest = new GetIndexRequest.Builder().index(iName).build();//GetIndexRequest getIndexRequest = new GetIndexRequest.Builder().index("*").build();GetIndexResponse getIndexResponse = indexClient.get(getIndexRequest);//GetIndexResponse getIndexResponse = indexClient.get(req -> req.index(iName));// 关闭transportclose();return getIndexResponse.result();}/*** 查询索引(异步)** @param indexName* @return*/@SneakyThrowspublic Map<String, IndexState> queryAsync(String indexName) {// 转为小写String iName = indexName.toLowerCase(Locale.ROOT);//getEsAsyncClient().indices().get()Map<String, IndexState> result = getEsAsyncClient().indices().get(req -> req.index(iName)).get().result();// 关闭transportclose();return result;}/*** 查询索引是否存在** @param indexName* @return*/@SneakyThrowspublic Boolean isExist(String indexName) {// 转为小写String iName = indexName.toLowerCase(Locale.ROOT);// 请求对象co.elastic.clients.elasticsearch.indices.ExistsRequest existsRequest =new co.elastic.clients.elasticsearch.indices.ExistsRequest.Builder().index(iName).build();// 异步查询boolean b = getEsAsyncClient().indices().exists(existsRequest).get().value();// 关闭transportclose();return b;}/*** 查询全部索引** @return 索引名称 Set 集合*/@SneakyThrowspublic Set<String> all() {GetIndexResponse getIndexResponse = getEsClient().indices().get(req -> req.index("*"));close();return getIndexResponse.result().keySet();}/*** 删除索引** @param indexName* @return*/@SneakyThrowspublic Boolean del(String indexName) {// 转为小写String iName = indexName.toLowerCase(Locale.ROOT);// 获取【索引客户端对象】//ElasticsearchIndicesClient indexClient = getEsClient().indices();// 【删除索引响应对象】//DeleteIndexResponse deleteIndexResponse = getEsClient().indices().delete(req -> req.index(iName));DeleteIndexResponse deleteIndexResponse = getEsAsyncClient().indices().delete(req -> req.index(iName)).get();// 关闭transportclose();return deleteIndexResponse.acknowledged();}}// ===================== 文档异步操作(封装在Doc内部类中) ============================public class Doc {/*** 创建/更新文档(异步)* 存在:** @param indexName  索引名称* @param documentId 文档ID* @param esDocument 文档内容* @return 不存在:created、存在:updated*/@SneakyThrowspublic String createOrUpdate(String indexName,String docId,EsDocument esDocument) {// 转为小写String iName = indexName.toLowerCase(Locale.ROOT);// 可创建/可更新IndexRequest<EsDocument> indexRequest = new IndexRequest.Builder<EsDocument>().index(iName).id(docId).document(esDocument).build();// 不存在:created、存在:updatedString s = getEsAsyncClient().index(indexRequest).get().result().jsonValue();// 关闭transportclose();return s;}/*** 批量创建/更新文档(异步)* 存在就更新,不存在就创建** @param indexName 索引名称* @param userMap   文档Map,格式:(文档id : 文档)* @return 成功操作的数量*/@SneakyThrowspublic Integer createOrUpdateBth(String indexName,Map<String, EsDocument> docMap) {// 转为小写String iName = indexName.toLowerCase(Locale.ROOT);// 批量操作对象集合List<BulkOperation> bs = new ArrayList<>();// 构建【批量操作对象】,并装入list集合中docMap.entrySet().stream().forEach(docEntry -> {// 操作对象(可新建/可更新)IndexOperation<EsDocument> idxOpe = new IndexOperation.Builder<EsDocument>()// 文档id.id(docEntry.getKey())// 文档内容.document(docEntry.getValue()).build();// 构建【批量操作对象】BulkOperation opt = new BulkOperation.Builder().index(idxOpe).build();// 装入list集合bs.add(opt);});// 构建【批理请求对象】BulkRequest bulkRequest = new BulkRequest.Builder()// 索引.index(iName)// 批量操作对象集合.operations(bs).build();// 批量操作BulkResponse bulkResponse = getEsAsyncClient().bulk(bulkRequest).get();int i = bulkResponse.items().size();//log.info("成功处理 {} 份文档!", i);// 关闭transportclose();return i;}/*** 检测文档是否存在** @param indexName* @param documentId* @return*/@SneakyThrowspublic Boolean isExist(String indexName,String docId) {// 转为小写String iName = indexName.toLowerCase(Locale.ROOT);ExistsRequest existsRequest = new ExistsRequest.Builder().index(iName).id(docId).build();boolean b = getEsAsyncClient().exists(existsRequest).get().value();// 关闭transportclose();return b;}/*** 查询距离当前最近的文档的时间值* ----------------------------* -1:索存不存在;* 0:该类型的文档不存在* >1:该类型的文档中最近的时间(秒值)* ----------------------------** @param indexName 索引名称* @param docType   文档类型* @return*/@SneakyThrowspublic Long lastTime(String indexName, Integer docType) {// 转为小写String iName = indexName.toLowerCase(Locale.ROOT);// 先判断索引是否存在;如果不存在,直接返回-1if (!index.isExist(iName)) {return -1L;}// 排序字段规则FieldSort fs = new FieldSort.Builder().field("time").order(SortOrder.Desc).build();// 排序操作项SortOptions so = new SortOptions.Builder().field(fs).build();// 文档类型Query byType = TermQuery.of(m -> m// EsDocument的内容字段名.field("type").value(docType))._toQuery();// 查询请求对象SearchRequest searchRequest = new SearchRequest.Builder().index(iName).query(byType)// 可以接收多个值.sort(so).size(1).build();// 异步查询SearchResponse<EsDocument> response = getEsAsyncClient().search(searchRequest, EsDocument.class).get();// 结果集List<Hit<EsDocument>> hits = response.hits().hits();// 判断该类型的文档是否存在if (hits.size() < 1) {// 关闭transportclose();return 0L;}// 时间最近的文档EsDocument doc = hits.stream().findFirst().get().source();// 关闭transportclose();// 返回时间值(秒)return doc.getTime();}/*** 根据关键字查文档* ---------------------------* 只要标题和内容中有一个匹配即可* ---------------------------** @param indexName 索引名称* @param keyword   关键字* @return List 集合*/@SneakyThrowspublic List<EsDocVo> query(String indexName,String keyword) {// 转为小写String iName = indexName.toLowerCase(Locale.ROOT);/*MatchQuery matchQuery = new MatchQuery.Builder().field(fieldName).query(fieldValue).build();Query query = new Query.Builder().match(matchQuery).build();//SearchRequest searchRequest = new SearchRequest.Builder().index(indexName).query(query).build();SearchRequest searchRequest = new SearchRequest.Builder().index(indexName).query(query).build();SearchResponse<EsDocument> searchResponse = getEsClient().search(searchRequest, EsDocument.class);*/// ---------------- lambda表达式写法(嵌套搜索查询)------------------// 标题中查找Query byTitle = MatchQuery.of(m -> m// EsDocument的标题字段名.field("title").query(keyword))._toQuery();// 内容中查找Query byContent = MatchQuery.of(m -> m// EsDocument的内容字段名.field("content").query(keyword))._toQuery();// 异步SearchResponse<EsDocument> response = getEsAsyncClient().search(s -> s.index(iName).query(q -> q// boolean 嵌套搜索;must需同时满足,should一个满足即可.bool(b -> b////.must(byTitle )//.must(byContent ).should(byTitle).should(byContent))),EsDocument.class).get();List<Hit<EsDocument>> hits = response.hits().hits();// 转为 List<EsDocument>//List<EsDocument> docs = hits.stream().map(hit -> hit.source()).collect(Collectors.toList());//List<EsDocument> docs = hits.stream().map(Hit::source).collect(Collectors.toList());List<EsDocVo> docs = hits.stream().map(hit -> getEsDocVo(hit.source())).collect(Collectors.toList());// 关闭transportclose();return docs;}/*** 【分页查找】根据关键字查文档* ---------------------------* 只要标题和内容中有一个匹配即可* 默认当前页:1* 默认页面记录数:10* 支持高亮* ---------------------------** @param indexName 索引名称* @param keyword   关键字* @return List 集合*/@SneakyThrowspublic EsPage page(String indexName,String keyword) {return page(indexName, keyword, 1, 30);}/*** 【分页查找】根据关键字查文档* ---------------------------* 只要标题和内容中有一个匹配即可* 支持高亮* ---------------------------** @param indexName 索引名称* @param keyword   关键字* @param current   当前页* @param pageSize  页面记录数* @return EsPage 对象*/@SneakyThrowspublic EsPage page(String indexName,String keyword,Integer current,Integer pageSize) {// 转为小写String iName = indexName.toLowerCase(Locale.ROOT);int c = Objects.isNull(current) ? 1 : current;int p = Objects.isNull(pageSize) ? 30 : pageSize;// 构建EsPageEsPage esPage = new EsPage();esPage.setKeyword(keyword);esPage.setCurrent(c);esPage.setPageSize(p);// 文档VO对象集合;实现高亮List<EsDocVo> docs = new ArrayList<>();// 判断关键字if (StringUtils.isEmpty(keyword)) {// keyword为空,返回空esPageesPage.setKeyword("");esPage.setTotal(0L);esPage.setRecords(docs);return esPage;}String kw = keyword.trim();// 判断indexName是否存在if (!index.isExist(iName)) {// 索引不存在,返回空的EsPage对象esPage.setTotal(0L);esPage.setRecords(docs);return esPage;}/*MatchQuery matchQuery = new MatchQuery.Builder().field(fieldName).query(fieldValue).build();Query query = new Query.Builder().match(matchQuery).build();//SearchRequest searchRequest = new SearchRequest.Builder().index(indexName).query(query).build();SearchRequest searchRequest = new SearchRequest.Builder().index(indexName).query(query).build();SearchResponse<EsDocument> searchResponse = getEsClient().search(searchRequest, EsDocument.class);*/// ---------------- lambda表达式写法(嵌套搜索查询)------------------// 多条件查询(从title或content中查询keyword)Query byKeyword = MultiMatchQuery.of(m -> m.fields("title", "content")//.fields("title").query(kw))._toQuery();// 起始文档值(从0开始)Integer from = (c - 1) * p;// 存放高亮的字段,默认与文档字段一致HighlightField hf = new HighlightField.Builder().build();Highlight highlight = new Highlight.Builder()// 前后缀默认就是em,可省略//.preTags("<em>")//.postTags("</em>").fields("title", hf).fields("content", hf).requireFieldMatch(false).build();// 异步SearchResponse<EsDocument> response = getEsAsyncClient().search(s -> s.index(iName).query(byKeyword).highlight(highlight).from(from).size(p),EsDocument.class).get();// 构建EsPageesPage.setTotal(response.hits().total().value());// 查询结果List<Hit<EsDocument>> hits = response.hits().hits();// 流式遍历查询结果:用高亮字段替换原文档字段hits.stream().forEach(hit -> {// 原文档EsDocument doc = hit.source();// 高亮标题字段List<String> titles = hit.highlight().get("title");if (!CollectionUtils.isEmpty(titles)) {// 替换原标题doc.setTitle(titles.get(0));}// 高亮内容字段List<String> contents = hit.highlight().get("content");if (!CollectionUtils.isEmpty(contents)) {// 替换原内容doc.setContent(contents.get(0));}// 原文档转为VO,加入VO对象集合中docs.add(getEsDocVo(doc));});// VO对象集合注入page对象esPage.setRecords(docs);// 关闭transportclose();// 返回pagereturn esPage;}/*** 【分页查找】根据属主、文档类型、关键字查文档* 支持高亮* ---------------------------* 1、公共文档:类型 0;任何人都可以查询,不需要比对属主* 2、非公共文档:类型 1、2、3.;有限制查询,只有文档属主可以查询;如:tom的文档,只有tom可以查询* 3、关键字:只要标题和内容中有一个匹配即可* ---------------------------* 查询中文与英文的匹别:* 1、中文:单个汉字为一个词;如:中国,可以分为:中、国,有一个比对上就算成功* 2、英文:一个单词为一个词;* ---------------------------* 注意:* 属主名称选择时,不要用中文,全部用英文,且有固定格式,不可修改* ---------------------------** @param indexName 索引名称* @param keyword   关键字* @param owner     文档属主* @param current   当前页* @param pageSize  页面记录数* @return EsPage 对象*/@SneakyThrowspublic EsPage page(String indexName,String keyword,String owner,Integer current,Integer pageSize) {// 转为小写String iName = indexName.toLowerCase(Locale.ROOT);int c = Objects.isNull(current) ? 1 : current;int p = Objects.isNull(pageSize) ? 30 : pageSize;// 构建EsPageEsPage esPage = new EsPage();esPage.setKeyword(keyword);esPage.setCurrent(c);esPage.setPageSize(p);// 文档VO对象集合;实现高亮List<EsDocVo> docs = new ArrayList<>();// 判断关键字if (StringUtils.isEmpty(keyword)) {// keyword为空,返回空esPageesPage.setKeyword("");esPage.setTotal(0L);esPage.setRecords(docs);return esPage;}String kw = keyword.trim();// 判断indexName是否存在if (!index.isExist(iName)) {// 索引不存在,返回空的EsPage对象esPage.setTotal(0L);esPage.setRecords(docs);return esPage;}// ---------------- 查询字段 ---------------// 多条件查询(从title或content中查询keyword)lambda表达式写法(嵌套搜索查询Query byKeyword = MultiMatchQuery.of(m -> m.fields("title", "content").query(kw))._toQuery();// ----------- 文档类型(范围查找)-----------// gt 大于,gte 大于等于,lt 小于,lte 小于等于// 文档类型(公共文档)Query byType1 = RangeQuery.of(m -> m.field("type")// 类型小于1.lt(JsonData.of(1)))._toQuery();// 文档类型(非公共文档)Query byType2 = RangeQuery.of(m -> m.field("type")// 类型大于0.gt(JsonData.of(0)))._toQuery();// --------------- 文档属主 ---------------// 文档属主(属主名称完全匹配)Query byOwner = TermQuery.of(m -> m// EsDocument的内容字段名.field("owner").value(owner))._toQuery();// 起始文档值(从0开始)Integer from = (c - 1) * p;// --------------- 高亮显示 ---------------// 存放高亮的字段,默认与文档字段一致HighlightField hf = new HighlightField.Builder().build();Highlight highlight = new Highlight.Builder()// 前后缀默认就是em,可省略//.preTags("<em>")//.postTags("</em>").fields("title", hf).fields("content", hf).requireFieldMatch(false).build();// 异步查询SearchResponse<EsDocument> response = getEsAsyncClient().search(s -> s.index(iName).query(q -> q// 布尔比较:有一个条件满足即可.bool(b -> b// 条件一:must:两个子条件都满足时,条件才成立;【公共文档】.should(sq1 -> sq1.bool(sqb1 -> sqb1.must(byType1, byKeyword)))// 条件二:must:三个子条件都满足时,条件才成立;【私有文档】.should(sq2 -> sq2.bool(sqb2 -> sqb2.must(byType2, byOwner, byKeyword))))).highlight(highlight).from(from).size(p),EsDocument.class).get();// 查询结果List<Hit<EsDocument>> hits = response.hits().hits();// 构建EsPageesPage.setTotal(response.hits().total().value());// 流式遍历查询结果:用高亮字段替换原文档字段hits.stream().forEach(hit -> {// 原文档EsDocument doc = hit.source();// 高亮标题字段List<String> titles = hit.highlight().get("title");if (!CollectionUtils.isEmpty(titles)) {// 替换原标题doc.setTitle(titles.get(0));}// 高亮内容字段List<String> contents = hit.highlight().get("content");if (!CollectionUtils.isEmpty(contents)) {// 替换原内容doc.setContent(contents.get(0));}// 原文档转为VO,加入VO对象集合中docs.add(getEsDocVo(doc));});// VO对象集合注入page对象esPage.setRecords(docs);// 关闭transportclose();// 返回pagereturn esPage;}/*** 批量删除文档** @param indexName   索引名称* @param documentIds 文档ID集合* @return 成功删除数量*/@SneakyThrowspublic Integer del(String indexName,List<String> docIds) {// 转为小写String iName = indexName.toLowerCase(Locale.ROOT);// 批量操作对象集合List<BulkOperation> bs = new ArrayList<>();// 构建【批量操作对象】,并装入list集合中docIds.stream().forEach(docId -> {// 删除操作对象DeleteOperation delOpe = new DeleteOperation.Builder().id(docId).build();// 构建【批量操作对象】BulkOperation opt = new BulkOperation.Builder().delete(delOpe).build();// 装入list集合bs.add(opt);});// 构建【批理请求对象】BulkRequest bulkRequest = new BulkRequest.Builder()// 索引.index(iName)// 批量操作对象集合.operations(bs).build();// 批量操作BulkResponse bulkResponse = getEsAsyncClient().bulk(bulkRequest).get();int i = bulkResponse.items().size();log.info("成功处理 {} 份文档!", i);// 关闭transportclose();return i;}/*** 删除所有文档* 实际上删除的是索引** @param indexName* @return*/public Boolean delAll(String indexName) {return index.del(indexName);}private EsDocVo getEsDocVo(EsDocument esDocument) {EsDocVo esDocVo = new EsDocVo();esDocVo.setId(esDocument.getId());esDocVo.setTitle(esDocument.getTitle());esDocVo.setContent(esDocument.getContent());esDocVo.setUrl(esDocument.getUrl());// 类型和属主不向前台展示//esDocVo.setType(esDocument.getType());//esDocVo.setOwner(esDocument.getOwner());// ------ 时间转换 ------// 当前时刻Long now = Instant.now().getEpochSecond();Long n = now - esDocument.getTime();// 秒数Long secOfMinute = 60L;//Long secOfHour = secOfMinute * 60L;Long secOfHour = 3600L;//Long secOfDay = secOfHour * 24L;Long secOfDay = 86400L;//Long secOfWeek = secOfDay * 7L;Long secOfWeek = 604800L;//Long secOfMonth = secOfDay * 30L;Long secOfMonth = 2592000L;//Long secOfYear = secOfMonth * 12L;Long secOfYear = 31104000L;if (n > secOfYear) {Double floor = Math.floor(n / secOfYear);esDocVo.setTime(floor.intValue() + "年前");} else if (n > secOfMonth) {Double floor = Math.floor(n / secOfMonth);esDocVo.setTime(floor.intValue() + "个月前");} else if (n > secOfWeek) {Double floor = Math.floor(n / secOfWeek);esDocVo.setTime(floor.intValue() + "周前");} else if (n > secOfDay) {Double floor = Math.floor(n / secOfDay);esDocVo.setTime(floor.intValue() + "天前");} else if (n > secOfHour) {Double floor = Math.floor(n / secOfHour);esDocVo.setTime(floor.intValue() + "小时前");} else if (n > secOfMinute) {Double floor = Math.floor(n / secOfMinute);esDocVo.setTime(floor.intValue() + "分钟前");} else {esDocVo.setTime("刚刚");}return esDocVo;}}// ===================== 基础操作(仅供内部调用) ============================private ElasticsearchTransport transport;@Value("${es.host}")private String host;@Value("${es.port}")private Integer port;/*** 同步客户端;调用结束后,需调用close()关闭transport** @return*/private ElasticsearchClient getEsClient() {ElasticsearchClient client = new ElasticsearchClient(getEsTransport());return client;}/*** 异步客户端** @return*/private ElasticsearchAsyncClient getEsAsyncClient() {ElasticsearchAsyncClient asyncClient =new ElasticsearchAsyncClient(getEsTransport());return asyncClient;}/*** 获取Transport** @return*/private ElasticsearchTransport getEsTransport() {host = StringUtils.isEmpty(host) ? "localhost" : host;port = Objects.isNull(port) ? 9200 : port;RestClient restClient = RestClient.builder(new HttpHost(host, port)).build();// Create the transport with a Jackson mappertransport = new RestClientTransport(restClient, new JacksonJsonpMapper());return transport;}/*** 关闭transport*/private void close() {if (transport != null) {try {transport.close();} catch (IOException e) {e.printStackTrace();}}}
}

4、测试类

package com.tuwer;import co.elastic.clients.elasticsearch.indices.*;
import com.tuwer.pojo.EsDocVo;
import com.tuwer.pojo.EsDocument;
import com.tuwer.pojo.EsPage;
import com.tuwer.util.EsUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resources;
import java.util.*;/*** @author 土味儿* Date 2022/8/9* @version 1.0*/
@SpringBootTest
public class MyTest {@AutowiredEsUtil esUtil;// 目标索引String indexName = "tuwer_index001";// --------------------------- 工具类方法 ---------------------------------// -----索引-----@Testpublic void testCreateIndexByUtil() {//System.out.println(EsClientUtil.createIndex(indexName));//EsClientUtil.createIndex("INDEX_abc");esUtil.index.create("INDEX_abc123");}@Testpublic void testQueryIndexByUtil() {Map<String, IndexState> result = esUtil.index.query("tuwer_index");//Map<String, IndexState> result = EsClientUtil.indexQueryAsync("tuwer_index");
/*        for (Map.Entry<String, IndexState> entry : result.entrySet()) {System.out.println(entry.getKey() + " : " + entry.getValue());}*/for (String s : result.keySet()) {System.out.println(result.get(s).dataStream());}}@Testpublic void testGetAllIndex(){Set<String> idxs = esUtil.index.all();for (String idx : idxs) {System.out.println(idx);}}@Testpublic void testDeleteIndexByUtil() {boolean b = esUtil.index.del("tuwer_index001");System.out.println(b);}// -----文档-----@Testpublic void testCreateDocument() {EsDocument esDocument = new EsDocument("123",0,"标题","测试123","admin","abc123");String res = esUtil.doc.createOrUpdate(indexName, "ABC", esDocument);System.out.println(res);}@Testpublic void testBatchCreateDocument() {Map<String, EsDocument> userMap = new HashMap<>();//for (int i = 0; i < 3; i++) {EsDocument doc1 = new EsDocument("11",0,"中","没123世213界人","","abc123");userMap.put(doc1.getId(), doc1);EsDocument doc2 = new EsDocument("12",0,"世","河231人测123南测试中","","abc123");userMap.put(doc2.getId(), doc2);EsDocument doc3 = new EsDocument("13",0,"原中","天大1231去131南","","abc123");userMap.put(doc3.getId(), doc3);EsDocument doc4 = new EsDocument("21",1,"中","没123世213界人","admin","abc123");userMap.put(doc4.getId(), doc4);EsDocument doc5 = new EsDocument("22",1,"世","河231人测123南测试中","34admin","abc123");userMap.put(doc5.getId(), doc5);EsDocument doc6 = new EsDocument("23",1,"原中","天大1231去131南","admin67","abc123");userMap.put(doc6.getId(), doc6);//}int i  = esUtil.doc.createOrUpdateBth(indexName, userMap);/*for (BulkResponseItem item : bulkResponse.items()) {System.out.println(item.id());}*/System.out.println(i);}@Testpublic void testDocIsExist(){//System.out.println(EsClientUtil.docIsExist(indexName, "8001"));System.out.println(esUtil.doc.isExist("tuwer_IndeX001", "8001"));}@Testpublic void testDeleteDocument() {List<String> documentIds = new ArrayList<>();documentIds.add("101");documentIds.add("102");documentIds.add("100");documentIds.add("201");documentIds.add("202");documentIds.add("203");documentIds.add("ABC");documentIds.add("_search");int i = esUtil.doc.del(indexName, documentIds);System.out.println(i);}@Testpublic void testDocDelAll(){esUtil.doc.delAll(indexName);}@Testpublic void testQueryDocument() {List<EsDocVo> docs = esUtil.doc.query(indexName, "中");//List<Hit<User>> users = EsClientUtil.queryDocumentByField(indexName, "name", "test_6001");for (EsDocVo doc : docs) {System.out.println(doc);}}@Testpublic void testDocPage(){//EsPage p = esUtil.doc.page(indexName, "中", 1, 5);EsPage p = esUtil.doc.page(indexName, "世", 1, 20);//esUtil.doc.page(indexName, "中", 1, 20);//EsPage p = esUtil.doc.page(indexName, "世", "admin67",1, 20);//EsPage p = esUtil.doc.page(indexName, "中");//System.out.println(p);System.out.println("--------------");for (EsDocVo record : p.getRecords()) {System.out.println(record);}}@Testpublic void testDocLastTime(){//EsPage p = esUtil.doc.page(indexName, "中", 1, 5);//EsPage p = esUtil.doc.page(indexName, "中", "admin",1, 5);//EsPage p = esUtil.doc.page(indexName, "中");Long lastTime = esUtil.doc.lastTime(indexName);System.out.println(lastTime);}
}

【六】ElasticSearch8.x Java API 实体类、工具类、测试类及常见问题相关推荐

  1. Java基础---键盘录入工具(Scanner类)

    键盘录入工具(Scanner类) 由jdk的类库提供,在类库中的java.util包下,键盘录入步骤是: 1.导包:import 包名.类名 即 import java.util.Scanner; 2 ...

  2. spark Java oracle,spark2.x由浅入深深到底系列六之RDD java api用JdbcRDD读取关系型数据库...

    课程咨询以及领取大额优惠请加微信:bigdatatang01 以下是用spark RDD java api实现从关系型数据库中读取数据,这里使用的是derby本地数据库,当然可以是mysql或者ora ...

  3. Java API接口统一格式返回结果类

    废话不多说,直接正文. 首先,我们需要定义规则(返回的格式).如http的返回状态码 404,500,200等.我们都知道404是找不到资源,200是正常的.由此可以知道,每一个状态码都有一个对应的说 ...

  4. 体质测试java代码_求java代码,要求做一个测试类,实现以下功能之一。最好三个功能都有。...

    展开全部 import java.util.*; public class Admin {//管理类 Scanner in=new Scanner(System.in); String msg=&qu ...

  5. 在java项目中咋样测试接口_请问java程序的接口中的测试类该怎么写?有什么用呢?...

    可使用android自带的httpclient框架实现. 1. GET 方式传递参数 //先将参数放入List,再对参数进行URL编码 List params = new LinkedList(); ...

  6. 玩家类pk{游戏}测试类

    package cn.hp.demo3;/**玩家类*属性:名字,类型,血量,防御,攻击* 行为:自我介绍,pk**/ public class Player {//封装,将属性设置为private, ...

  7. java 写 xml 文件 工具类_XML读写工具类

    摘要:①读取XML文件,生成pojo对象:②将对象信息保存到xml中. 步骤: ①新建一个普通的java类BasePage: packagecom.test.selenium.pages;import ...

  8. 追新求快的时代,别让 Java Web 开发必备工具 Tomcat 变成“熟悉的陌生人”!

    作者 | 码哥字节 来源 | MageByte技术团队 Tomcat 发展这么多年,已经比较成熟稳定.在如今『追新求快』的时代,Tomcat 作为 Java Web 开发必备的工具似乎变成了『熟悉的陌 ...

  9. Java运行非Web的Springboot项目(测试类或启动主类两种方法)

    Java运行非Web的Springboot项目(测试类或启动主类两种方法) 前言 一.创建打开一个Springboot项目 二.运行项目 1.Test测试类运行 2.启动主类运行 三.持续运行不停止( ...

最新文章

  1. python项目开发案例-Python项目开发案例集锦 PDF 全彩超清版
  2. 浅析SAP EWM与WMS的差异
  3. LeetCode 140. 单词拆分 II(DP+回溯)
  4. 【Elasticsearch】 es 搜索 返回信息 字段 解释
  5. 使用根轨迹分析的动态补偿设计
  6. 【半原创】将js和css文件装入localStorage加速程序执行
  7. django组件-cookie与session
  8. 批量快速截图方法(snipaste)
  9. php跨域请求post请求失败,nginx + php 跨域问题,GET可以跨域成功,POST失败
  10. 企业文化海报设计模板,企业文化经典标语挂图素材
  11. 电子表格文档控件DevExpress Office File API v21.1 - 增强PDF文档API
  12. Excel中万能的查询函数——VLOOKUP
  13. 使用java将word文档docx,doc(包含图形,文本框)完美转换成所有格式图片(pdf,png,gif,jpeg等等)
  14. VisionPro 工具
  15. 智能家居之红外遥控---手机万能红外遥控器
  16. 国密算法SM2实现 vue+C#
  17. SpringBoot集成支付宝沙箱手机网站支付详细流程和踩坑分享
  18. CentOS下连VisualSVN服务器 (windows的svn服务器)
  19. 是否该读博士(ZZ)
  20. 面试华为测试岗后感想,真的很后悔这5年一直都干的是基础测试....

热门文章

  1. Linux - uptime命令平均负载详解
  2. SpringCloud 学习(一)---- 微服务的概念
  3. Parrot推出4K迷你版无人机Anafi,仅重320g
  4. 基于Java+JSP+MySQL共享单车管理系统的设计与实现-计算机毕业设计
  5. 如何拍出更好看的照片,那是有技巧的
  6. 全面屏虚拟按键高度适配
  7. linux虚拟机专用,虚拟机专用win7
  8. 软件测试课堂练习题答案 中国大学mooc
  9. 华中师范大学计算机考研874攻略
  10. SN550+雷电3安装WTG