您的位置 首页 java

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

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

 <!-- 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/Json Exception

如果出现这个异常,要加入 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

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

 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;

import lombok.Data;

/**
 * <p>文档视图层对象</p>
 * @author 土味儿
 * Date 2022/9/17
 * @version 1.0
 */
@Data
public class EsDocVo {
 
    /**
     * 文档id
     */
    private String id;
    /**
     * 文档标题
     */
    private String title;
    /**
     * 文档内容
     */
    private String content;
    /**
     * 文档url
     */
    private String url;
    /**
     * 时间
     * 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. Elasticsearch AsyncClient;
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.MatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.MultiMatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core. exists Request;
import co.elastic.clients.elasticsearch.core.bulk.*;
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.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.tuwer.pojo.EsDocVo;
import com.tuwer.pojo.EsDocument;
import com.tuwer.pojo.EsPage;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
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>
 * -----------------------------
 * 调用异步客户端就是异步方法;默认异步
 * 索引名称/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 + "】创建失败!");
                }
            }

            return result;
        }

        /**
         * 查询索引(同步)
         *
         * @param indexName
         * @return
         */
        @SneakyThrows
        public Map<String, IndexState> querySync(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> query(String indexName) {
 
            // 转为小写
            String iName = indexName.toLowerCase(Locale.ROOT);
            //getEsAsyncClient().indices().get()
            return getEsAsyncClient().indices().get(req -> req.index(iName)).get().result();
        }

        /**
         * 查询全部索引
         *
         * @return 索引名称 Set 集合
         */
        @SneakyThrows
        public Set<String> all() {
 
            GetIndexResponse getIndexResponse = getEsAsyncClient().indices().get(req -> req.index("*")).get();

            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
            return getEsAsyncClient().index(indexRequest).get().result().jsonValue();
        }

        /**
         * 批量创建/更新文档(异步)
         * 存在就更新,不存在就创建
         *
         * @param indexName 索引名称
         * @param userMap   文档Map,格式:(文档id : 文档)
         * @return 成功操作的数量
         */
        @SneakyThrows
        public Integer createOrUpdateBth(
                String indexName,
                Map<String, EsDocument> userMap
        ) {
 
            // 转为小写
            String iName = indexName.toLowerCase(Locale.ROOT);

            // 批量操作对象集合
            List<BulkOperation> bs = new ArrayList<>();

            // 构建【批量操作对象】,并装入list集合中
            userMap.entrySet().stream().forEach(userEntry -> {
 
                // 操作对象(可新建/可更新)
                IndexOperation<EsDocument> idxOpe = new IndexOperation.Builder<EsDocument>()
                        // 文档id
                        .id(userEntry.getKey())
                        // 文档内容
                        .document(userEntry.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);

            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();
            return getEsAsyncClient().exists(existsRequest).get().value();
        }

        /**
         * 查询距离当前最近的文档的时间值
         *
         * @param indexName
         * @return
         */
        @SneakyThrows
        public Long lastTime(String indexName) {
 
            // 转为小写
            String iName = indexName.toLowerCase(Locale.ROOT);

            // 排序字段规则
            FieldSort fs = new FieldSort.Builder()
                    .field("time")
                    .order(SortOrder.Desc)
                    .build();

            // 排序操作项
            SortOptions so = new SortOptions.Builder()
                    .field(fs)
                    .build();

            // 查询请求对象
            SearchRequest searchRequest = new SearchRequest.Builder()
                    .index(iName)
                    // 可以接收多个值
                    .sort(so)
                    .size(1).build();

            // 异步查询
            SearchResponse<EsDocument> response = getEsAsyncClient().search(searchRequest, EsDocument.class).get();

            // 结果集
            List<Hit<EsDocument>> hits = response.hits().hits();

            // 时间最近的文档
            EsDocument doc = hits.stream().findFirst().get().source();

            // 返回时间值(秒)
            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<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());

            // 关闭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, 10);
        }

        /**
         * 【分页查找】根据关键字查文档
         * ---------------------------
         * 只要标题和内容中有一个匹配即可
         * 支持高亮
         * ---------------------------
         *
         * @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);

        /*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(keyword)
            )._toQuery();

            // 起始文档值(从0开始)
            Integer from = (current - 1) * pageSize;

            // 存放高亮的字段,默认与文档字段一致
            HighlightField hf = new HighlightField.Builder().build();

            Highlight highlight = new Highlight.Builder()
                    // 前后缀默认就是em,可省略
                    //.preTags("<em>")
                    //.postTags("</em>")
                    .fields("title", new HighlightField.Builder().build())
                    .fields("content", new HighlightField.Builder().build())
                    .requireFieldMatch(false)
                    .build();

            // 异步
            SearchResponse<EsDocument> response = getEsAsyncClient().search(s -> s
                            .index(iName)
                            .query(byKeyword)
                            .highlight(highlight)
                            .from(from).size(pageSize),
                    EsDocument.class
            ).get();

            // 构建EsPage
            EsPage esPage = new EsPage();
            esPage.setKeyword(keyword);
            esPage.setTotal(response.hits().total().value());
            esPage.setCurrent(current);
            esPage.setPageSize(pageSize);

            // 查询结果
            List<Hit<EsDocument>> hits = response.hits().hits();

            // 文档VO对象集合;实现高亮
            List<EsDocVo> docs = new ArrayList<>();

            // 流式遍历查询结果:用高亮字段替换原文档字段
            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);

            // ---------------- lambda表达式写法(嵌套搜索查询)------------------

            // 多条件查询(从title或content中查询keyword)
            Query byKeyword = MultiMatchQuery.of(m -> m
                    .fields("title", "content")
                    .query(keyword)
            )._toQuery();

            // 文档类型(公共文档)
            Query byType1 = TermQuery.of(m -> m
                    // EsDocument的内容字段名
                    .field("type")
                    .value(0)
            )._toQuery();

            // 文档类型(私有文档)
            Query byType2 = TermQuery.of(m -> m
                    // EsDocument的内容字段名
                    .field("type")
                    .value(1)
            )._toQuery();

            // 文档属主(属主名称完全匹配)
            Query byOwner = TermQuery.of(m -> m
                    // EsDocument的内容字段名
                    .field("owner")
                    .value(owner)
            )._toQuery();

            // 起始文档值(从0开始)
            Integer from = (current - 1) * pageSize;

            // 存放高亮的字段,默认与文档字段一致
            HighlightField hf = new HighlightField.Builder().build();

            Highlight highlight = new Highlight.Builder()
                    // 前后缀默认就是em,可省略
                    //.preTags("<em>")
                    //.postTags("</em>")
                    .fields("title", new HighlightField.Builder().build())
                    .fields("content", new HighlightField.Builder().build())
                    .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(pageSize),
                    EsDocument.class
            ).get();

            // 构建EsPage
            EsPage esPage = new EsPage();
            esPage.setKeyword(keyword);
            esPage.setTotal(response.hits().total().value());
            esPage.setCurrent(current);
            esPage.setPageSize(pageSize);

            // 查询结果
            List<Hit<EsDocument>> hits = response.hits().hits();

            // 文档VO对象集合;实现高亮
            List<EsDocVo> docs = new ArrayList<>();

            // 流式遍历查询结果:用高亮字段替换原文档字段
            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);

            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());

            // ------ 时间转换 ------
            // 当前时刻
            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 static ElasticsearchTransport transport;

    /**
     * 同步客户端;调用结束后,需调用close()关闭transport
     *
     * @return
     */
    private static ElasticsearchClient getEsClient() {
 
        ElasticsearchClient client = new ElasticsearchClient(getEsTransport());
        return client;
    }

    /**
     * 异步客户端
     *
     * @return
     */
    private static ElasticsearchAsyncClient getEsAsyncClient() {
 
        ElasticsearchAsyncClient asyncClient =
                new ElasticsearchAsyncClient(getEsTransport());
        return asyncClient;
    }

    /**
     * 获取Transport
     *
     * @return
     */
    private static ElasticsearchTransport getEsTransport() {
 
        RestClient restClient = RestClient.builder(
                new HttpHost("localhost", 9200)).build();

        // Create the transport with a Jackson mapper
        transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());

        return transport;
    }

    /**
     * 关闭transport
     */
    private static 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<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());
        }
    }

    @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<Hit<User>> 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);
    }
}  

文章来源:智云一二三科技

文章标题:ElasticSearch8.x Java API 实体类、工具类、测试类及常见问题

文章地址:https://www.zhihuclub.com/177157.shtml

关于作者: 智云科技

热门文章

网站地图