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

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

1、依赖


        <dependency>
            <groupId>co.elastic.clientsgroupId>
            <artifactId>elasticsearch-javaartifactId>
            <version>8.1.1version>
        dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.coregroupId>
            <artifactId>jackson-databindartifactId>
            <version>2.13.3version>
        dependency>
        <dependency>
            <groupId>jakarta.jsongroupId>
            <artifactId>jakarta.json-apiartifactId>
            <version>2.0.1version>
        dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatypegroupId>
            <artifactId>jackson-datatype-jsr310artifactId>
            <version>2.13.3version>
        dependency>
        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.24version>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <version>2.6.7version>
        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;

/**
 * 

ES文档实体类

* ---------------------- * 一定要有无参构造器;否则反序列化会失败 * ---------------------- * * @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;

/**
 * 

文档视图层对象

* @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;

/**
 * 

ES操作工具类

* ----------------------------- * 调用异步客户端就是异步方法; * 异步方法也要关闭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:失败 */ @SneakyThrows public 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 + "】创建失败!"); } } // 关闭transport close(); return result; } /** * 创建索引(异步) * * @param indexName * @return */ @SneakyThrows public 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 + "】创建失败!"); } } // 关闭transport close(); return result; } /** * 查询索引(同步) * * @param indexName * @return */ @SneakyThrows public 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)); // 关闭transport close(); return getIndexResponse.result(); } /** * 查询索引(异步) * * @param indexName * @return */ @SneakyThrows public 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(); // 关闭transport close(); return result; } /** * 查询索引是否存在 * * @param indexName * @return */ @SneakyThrows public 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(); // 关闭transport close(); return b; } /** * 查询全部索引 * * @return 索引名称 Set 集合 */ @SneakyThrows public Set<String> all() { GetIndexResponse getIndexResponse = getEsClient().indices().get(req -> req.index("*")); close(); return getIndexResponse.result().keySet(); } /** * 删除索引 * * @param indexName * @return */ @SneakyThrows public 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(); // 关闭transport close(); return deleteIndexResponse.acknowledged(); } } // ===================== 文档异步操作(封装在Doc内部类中) ============================ public class Doc { /** * 创建/更新文档(异步) * 存在: * * @param indexName 索引名称 * @param documentId 文档ID * @param esDocument 文档内容 * @return 不存在:created、存在:updated */ @SneakyThrows public 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、存在:updated String s = getEsAsyncClient().index(indexRequest).get().result().jsonValue(); // 关闭transport close(); return s; } /** * 批量创建/更新文档(异步) * 存在就更新,不存在就创建 * * @param indexName 索引名称 * @param userMap 文档Map,格式:(文档id : 文档) * @return 成功操作的数量 */ @SneakyThrows public 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); // 关闭transport close(); return i; } /** * 检测文档是否存在 * * @param indexName * @param documentId * @return */ @SneakyThrows public 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(); // 关闭transport close(); return b; } /** * 查询距离当前最近的文档的时间值 * ---------------------------- * -1:索存不存在; * 0:该类型的文档不存在 * >1:该类型的文档中最近的时间(秒值) * ---------------------------- * * @param indexName 索引名称 * @param docType 文档类型 * @return */ @SneakyThrows public Long lastTime(String indexName, Integer docType) { // 转为小写 String iName = indexName.toLowerCase(Locale.ROOT); // 先判断索引是否存在;如果不存在,直接返回-1 if (!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) { // 关闭transport close(); return 0L; } // 时间最近的文档 EsDocument doc = hits.stream().findFirst().get().source(); // 关闭transport close(); // 返回时间值(秒) return doc.getTime(); } /** * 根据关键字查文档 * --------------------------- * 只要标题和内容中有一个匹配即可 * --------------------------- * * @param indexName 索引名称 * @param keyword 关键字 * @return List 集合 */ @SneakyThrows public 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 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 //List docs = hits.stream().map(hit -> hit.source()).collect(Collectors.toList()); //List docs = hits.stream().map(Hit::source).collect(Collectors.toList()); List<EsDocVo> docs = hits.stream().map(hit -> getEsDocVo(hit.source())).collect(Collectors.toList()); // 关闭transport close(); return docs; } /** * 【分页查找】根据关键字查文档 * --------------------------- * 只要标题和内容中有一个匹配即可 * 默认当前页:1 * 默认页面记录数:10 * 支持高亮 * --------------------------- * * @param indexName 索引名称 * @param keyword 关键字 * @return List 集合 */ @SneakyThrows public EsPage page( String indexName, String keyword ) { return page(indexName, keyword, 1, 30); } /** * 【分页查找】根据关键字查文档 * --------------------------- * 只要标题和内容中有一个匹配即可 * 支持高亮 * --------------------------- * * @param indexName 索引名称 * @param keyword 关键字 * @param current 当前页 * @param pageSize 页面记录数 * @return EsPage 对象 */ @SneakyThrows public 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; // 构建EsPage EsPage esPage = new EsPage(); esPage.setKeyword(keyword); esPage.setCurrent(c); esPage.setPageSize(p); // 文档VO对象集合;实现高亮 List<EsDocVo> docs = new ArrayList<>(); // 判断关键字 if (StringUtils.isEmpty(keyword)) { // keyword为空,返回空esPage esPage.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 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("") //.postTags("") .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(); // 构建EsPage esPage.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); // 关闭transport close(); // 返回page return esPage; } /** * 【分页查找】根据属主、文档类型、关键字查文档 * 支持高亮 * --------------------------- * 1、公共文档:类型 0;任何人都可以查询,不需要比对属主 * 2、非公共文档:类型 1、2、3.;有限制查询,只有文档属主可以查询;如:tom的文档,只有tom可以查询 * 3、关键字:只要标题和内容中有一个匹配即可 * --------------------------- * 查询中文与英文的匹别: * 1、中文:单个汉字为一个词;如:中国,可以分为:中、国,有一个比对上就算成功 * 2、英文:一个单词为一个词; * --------------------------- * 注意: * 属主名称选择时,不要用中文,全部用英文,且有固定格式,不可修改 * --------------------------- * * @param indexName 索引名称 * @param keyword 关键字 * @param owner 文档属主 * @param current 当前页 * @param pageSize 页面记录数 * @return EsPage 对象 */ @SneakyThrows public 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; // 构建EsPage EsPage esPage = new EsPage(); esPage.setKeyword(keyword); esPage.setCurrent(c); esPage.setPageSize(p); // 文档VO对象集合;实现高亮 List<EsDocVo> docs = new ArrayList<>(); // 判断关键字 if (StringUtils.isEmpty(keyword)) { // keyword为空,返回空esPage esPage.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("") //.postTags("") .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(); // 构建EsPage esPage.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); // 关闭transport close(); // 返回page return esPage; } /** * 批量删除文档 * * @param indexName 索引名称 * @param documentIds 文档ID集合 * @return 成功删除数量 */ @SneakyThrows public 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); // 关闭transport close(); 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 mapper transport = 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 {
    @Autowired
    EsUtil esUtil;

    // 目标索引
    String indexName = "tuwer_index001";
    

    // --------------------------- 工具类方法 ---------------------------------
    // -----索引-----
    @Test
    public void testCreateIndexByUtil() {
        //System.out.println(EsClientUtil.createIndex(indexName));
        //EsClientUtil.createIndex("INDEX_abc");
        esUtil.index.create("INDEX_abc123");
    }

    @Test
    public void testQueryIndexByUtil() {
        Map<String, IndexState> result = esUtil.index.query("tuwer_index");
        //Map result = EsClientUtil.indexQueryAsync("tuwer_index");
/*        for (Map.Entry entry : result.entrySet()) {
            System.out.println(entry.getKey() + " : " + entry.getValue());
        }*/
        for (String s : result.keySet()) {
            System.out.println(result.get(s).dataStream());
        }
    }

    @Test
    public void testGetAllIndex(){
        Set<String> idxs = esUtil.index.all();
        for (String idx : idxs) {
            System.out.println(idx);
        }
    }

    @Test
    public void testDeleteIndexByUtil() {
        boolean b = esUtil.index.del("tuwer_index001");
        System.out.println(b);
    }

    // -----文档-----
    @Test
    public void testCreateDocument() {
        EsDocument esDocument = new EsDocument("123",0,"标题","测试123","admin","abc123");
        String res = esUtil.doc.createOrUpdate(indexName, "ABC", esDocument);
        System.out.println(res);
    }

    @Test
    public 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);
    }

    @Test
    public void testDocIsExist(){
        //System.out.println(EsClientUtil.docIsExist(indexName, "8001"));
        System.out.println(esUtil.doc.isExist("tuwer_IndeX001", "8001"));
    }

    @Test
    public 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);
    }

    @Test
    public void testDocDelAll(){
        esUtil.doc.delAll(indexName);
    }

    @Test
    public void testQueryDocument() {
        List<EsDocVo> docs = esUtil.doc.query(indexName, "中");
        //List> users = EsClientUtil.queryDocumentByField(indexName, "name", "test_6001");

        for (EsDocVo doc : docs) {
            System.out.println(doc);
        }
    }

    @Test
    public 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);
        }
    }

    @Test
    public 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);
    }
}


你可能感兴趣的:(ElasticSearch,java,elasticsearch,开发语言)