Springboot集成Elasticsearch High Level REST Client实现增删改查实战

获取源码🚩

需要完整代码资料,请一键三连后评论区留下邮箱,安排发送!!!🤖

什么是High Level REST Client?

Elasticsearch 的 High Level REST Client 是一个用于与 Elasticsearch 进行交互的 Java 客户端库,它提供了比低级别的 REST 客户端更高级别的抽象。High Level REST Client 使用了 OkHttp 库作为底层的 HTTP 客户端,并且提供了自动重试、连接管理和错误处理等功能。它允许开发人员以面向对象的方式构建请求和解析响应,从而简化了与 Elasticsearch 的 REST API 的交互过程。

✔️优点:

  • 易用性:High Level REST Client 提供了丰富的 API,使得常见的 Elasticsearch 操作变得简单直观。
  • 自动重试和错误处理:它能够处理网络故障和重试失败的请求,减少了开发人员需要处理的异常情况。
  • 封装了 JSON 处理:它自动处理了请求和响应的序列化和反序列化,使得开发人员无需直接处理 JSON 字符串。
  • 连接管理:它提供了连接池管理,可以复用连接,提高性能。

❌缺点:

  • 依赖较大:由于其提供了丰富的功能,因此其依赖库和整体大小相对较大。
  • 性能开销:高层面的抽象可能会引入额外的性能开销,尤其是在高并发场景下。

High Level REST Client的核心

索引管理

CreateIndexRequest 和 CreateIndexResponse:用于创建一个新的索引。

GetIndexRequest 和 GetIndexResponse:用于获取索引的元数据。

DeleteIndexRequest 和 DeleteIndexResponse:用于删除一个索引。

文档操作

IndexRequest 和 IndexResponse:用于索引(或更新)一个文档。

UpdateRequest 和 UpdateResponse:用于更新已存在的文档。

GetRequest 和 GetResponse:用于检索一个文档。

DeleteRequest 和 DeleteResponse:用于删除一个文档。

搜索和聚合

SearchRequest 和 SearchResponse:用于执行搜索查询和聚合。

CountRequest 和 CountResponse:用于计算满足给定条件的文档数量。

ScrollRequest 和 ScrollResponse:用于滚动搜索结果,获取大量数据。

批量操作

BulkRequest 和 BulkResponse:用于执行批量操作,如批量索引、更新或删除多个文档。

request.source()等于DSL中query{}

Sprinboot集成High Level REST Client

创建索引

可以使用postman或者谷歌浏览器插件Elasticvue 创建:

json 复制代码
{
  "article_doc": {
    "aliases": {},
    "mappings": {
      "properties": {
        "author": {
          "type": "keyword"
        },
        "category": {
          "type": "keyword"
        },
        "content": {
          "type": "text",
          "analyzer": "ik_max_word",
          "search_analyzer": "ik_smart"
        },
        "createTime": {
          "type": "date",
          "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
        },
        "id": {
          "type": "keyword"
        },
        "readCount": {
          "type": "integer"
        },
        "title": {
          "type": "text",
          "analyzer": "ik_max_word",
          "search_analyzer": "ik_smart"
        }
      }
    },
    "settings": {
      "index": {
        "routing": {
          "allocation": {
            "include": {
              "_tier_preference": "data_content"
            }
          }
        },
        "number_of_shards": "1",
        "provided_name": "article_doc",
        "creation_date": "1721725035763",
        "number_of_replicas": "1",
        "uuid": "r55CXBCLSxO8aFWPtL1Niw",
        "version": {
          "created": "7130299"
        }
      }
    }
  }
}

添加pom依赖

xml 复制代码
<dependency>
	<groupId>org.elasticsearch</groupId>
	<artifactId>elasticsearch</artifactId>
	<version>7.14.0</version>
</dependency>

<dependency>
	<groupId> org.elasticsearch.client</groupId>
	<artifactId>elasticsearch-rest-high-level-client</artifactId>
	<version>7.14.0</version>
</dependency>

编写配置类

java 复制代码
@Configuration
public class ElasticsearchConfig {
    @Value("${elasticsearch.host}")
    private String host;

    @Value("${elasticsearch.port}")
    private int port;

    /*@Value("${elasticsearch.username}")
    private String userName;

    @Value("${elasticsearch.password}")
    private String password;*/

    /**
     * 如果@Bean没有指定bean的名称,那么这个bean的名称就是方法名
     */
    @Bean
    public RestHighLevelClient restHighLevelClient() {
        return new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost(host, port, "http")
                )
        );
    }


}

编写模型

java 复制代码
@Data
@TableName("tb_article")
public class Article {
    private String id;
    private String author;
    private String category;
    private String title;
    private String content;
    private Integer readCount;
    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
}

编写接口服务类

获取索引结构接口

http://localhost:9000/articleDoc/getMapping

数据同步接口

java 复制代码
/**
     *  批量从数据库导入
     * @throws IOException
     */
    public void importFromDb() throws IOException {
        List<Article> articleList = articleService.list();
        BulkRequest bulkRequest = new BulkRequest();
        for (Article doc : articleList) {
            String data = JSON.toJSONString(doc);
            IndexRequest indexRequest = new IndexRequest(Constant.ARTICLE_INDEX);
            indexRequest.id(doc.getId()).source(data, XContentType.JSON);
            bulkRequest.add(indexRequest);
        }
        BulkResponse response = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        log.info("批量导入状态:{}, 耗时{}", response.status(), response.getTook().getSeconds());
    }

精确查询

java 复制代码
public List<Article> searchByAuthor(String author) {
        try {
            // 构建查询条件(注意:termQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(QueryBuilders.termQuery("author", author));
            SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
            searchRequest.source(searchSourceBuilder);
            // 执行查询,然后处理响应结果
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            // 根据状态和数据条数验证是否返回了数据
            List<Article> articleList = new ArrayList<>();
            if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
                SearchHits hits = searchResponse.getHits();
                for (SearchHit hit : hits) {
                    Article doc = JSON.parseObject(hit.getSourceAsString(), Article.class);
                    articleList.add(doc);
                }
            }
            return articleList;
        } catch (IOException e) {
            log.error("精确查询异常:", e);
            return null;
        }
    }

全文查询

  • 查全部,
    java MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
    太简单,不再演示。
java 复制代码
/**
     *  全文查询
     */
    public List<Article> matchQuery(String keyword) {
        try {
            // 构建查询条件
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(QueryBuilders.multiMatchQuery(keyword,"title","content"));
            // 创建查询请求对象,将查询对象配置到其中
            SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
            searchRequest.source(searchSourceBuilder);
            // 执行查询,然后处理响应结果
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            return getArticleListFromSearchResponse(searchResponse);
        } catch (IOException e) {
            log.error("全文查询异常", e);
            return null;
        }
    }

分页查询(带排序)

java 复制代码
/**
     *  分页查询
     * @param page
     */
    public void pageSortQuery(Page<Article> page) {
        try {
            // 构建查询条件
            MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
            // 创建查询源构造器
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(matchAllQueryBuilder);
            // 设置分页
            searchSourceBuilder.from((int) ((page.getCurrent()-1)*page.getSize()));
            searchSourceBuilder.size((int) page.getSize());
            // 按照阅读量排序
            searchSourceBuilder.sort("readCount", SortOrder.DESC);
            // 创建查询请求对象,将查询对象配置到其中
            SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
            searchRequest.source(searchSourceBuilder);
            // 执行查询,然后处理响应结果
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            // 根据状态和数据条数验证是否返回了数据
            page.setTotal(getHitTotal(searchResponse));
            page.setRecords(getArticleListFromSearchResponse(searchResponse));
        } catch (IOException e) {
            log.error("分页查询失败", e);
        }
    }

高亮查询✨

java 复制代码
public List<Article> highlightQuery(String keyword) {
        try {
            MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title", keyword);
            HighlightBuilder highlightBuilder = new HighlightBuilder().field("title").preTags("<font color='red'>").postTags("</font>");
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(matchQueryBuilder);
            searchSourceBuilder.highlighter(highlightBuilder);
            searchSourceBuilder.size(100);
            SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            List<Article> articleList = new ArrayList<>();
            if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
                SearchHits hits = searchResponse.getHits();
                for (SearchHit hit : hits) {
                    Article doc = JSON.parseObject(hit.getSourceAsString(), Article.class);
                    // 获取高亮的数据
                    HighlightField highlightField = hit.getHighlightFields().get("title");
                    System.out.println("高亮名称:" + highlightField.getFragments()[0].string());
                    // 替换掉原来的数据
                    Text[] fragments = highlightField.getFragments();
                    if (fragments != null && fragments.length > 0) {
                        StringBuilder title = new StringBuilder();
                        for (Text fragment : fragments) {
                            title.append(fragment);
                        }
                        doc.setTitle(title.toString());
                    }
                    articleList.add(doc);
                }
            }
            return articleList;
        } catch (Exception e) {
            log.error("高亮查询失败", e);
            return null;
        }
    }

范围查询

java 复制代码
/**
     *  范围查询
     *  时间文档表达式:https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#date-math
     * @return
     */
    public List<Article> rangeQuery() {
        try {
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            //searchSourceBuilder.query(QueryBuilders.rangeQuery("price").gte(10000));
            searchSourceBuilder.query(QueryBuilders.rangeQuery("createTime")
                    .gte("now-2d").includeLower(true).includeUpper(true));
            SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            return getArticleListFromSearchResponse(searchResponse);
        } catch (Exception e) {
            log.error("范围查询失败", e);
            return null;
        }
    }

布尔查询(BooleanQuery)

bool 查询可以用来合并多个条件查询结果的布尔逻辑,它包含一下操作符:

must :多个查询条件必须完全匹配,相当于关系型数据库中的 and。
should :至少有一个查询条件匹配,相当于关系型数据库中的 or。
must_not : 多个查询条件的相反匹配,相当于关系型数据库中的 not。
filter :过滤满足条件的数据。
range :条件筛选范围。
gt :大于,相当于关系型数据库中的 >。
gte :大于等于,相当于关系型数据库中的 >=。
lt :小于,相当于关系型数据库中的 <。
lte:小于等于,相当于关系型数据库中的 <=。

java 复制代码
public List<Article> highlightQuery(String keyword) {
        try {
            MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title", keyword);
            HighlightBuilder highlightBuilder = new HighlightBuilder().field("title").preTags("<font color='red'>").postTags("</font>");
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(matchQueryBuilder);
            searchSourceBuilder.highlighter(highlightBuilder);
            searchSourceBuilder.size(100);
            SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            List<Article> articleList = new ArrayList<>();
            if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
                SearchHits hits = searchResponse.getHits();
                for (SearchHit hit : hits) {
                    Article doc = JSON.parseObject(hit.getSourceAsString(), Article.class);
                    // 获取高亮的数据
                    HighlightField highlightField = hit.getHighlightFields().get("title");
                    System.out.println("高亮名称:" + highlightField.getFragments()[0].string());
                    // 替换掉原来的数据
                    Text[] fragments = highlightField.getFragments();
                    if (fragments != null && fragments.length > 0) {
                        StringBuilder title = new StringBuilder();
                        for (Text fragment : fragments) {
                            title.append(fragment);
                        }
                        doc.setTitle(title.toString());
                    }
                    articleList.add(doc);
                }
            }
            return articleList;
        } catch (Exception e) {
            log.error("高亮查询失败", e);
            return null;
        }
    }

聚合查询

在使用Elasticsearch时,更多会用到聚合操作,它类似SQL中的groupby操作。ES的聚合查询是先查出结果,然后对结果使用聚合函数做处理,常用的操作有:avg:求平均、max:最大值、min:最小值、sum:求和等。

在ES中聚合分为指标聚合和分桶聚合:

Metric 指标聚合:指标聚合对一个数据集求最大、最小、和、平均值等

Bucket 分桶聚合:除了有上面的聚合函数外,还可以对查询出的数据进行分组group by,再在组上进行游标聚合。

  • Metric 指标聚合
java 复制代码
/**
     *  指标查询
     */
    public int metricQuery() {
        try {
            MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(matchAllQueryBuilder);
            AggregationBuilder mostPopular = AggregationBuilders.max("mostPopular").field("readCount");
            searchSourceBuilder.aggregation(mostPopular);
            SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//            log.info("查询结果:{}", JSON.toJSONString(searchResponse));
            Aggregations aggregations = searchResponse.getAggregations();
            ParsedMax max = aggregations.get("mostPopular");
            return (int) max.getValue();
        } catch (Exception e) {
            log.error("指标查询失败", e);
            return -1;
        }
    }
  • Bucket 分桶聚合

根据文章分类,统计文章数量。

java 复制代码
/**
     *  分桶聚合
     * @return
     */
    public Map<String,Object> bucketQuery() {
        try {
            // 构建查询条件
            MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
            // 创建查询源构造器
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(matchAllQueryBuilder);

            // 根据商品分类进行分组查询
            TermsAggregationBuilder categoryGroup = AggregationBuilders.terms("categoryGroup").field("category");
            searchSourceBuilder.aggregation(categoryGroup);

            // 创建查询请求对象,将查询对象配置到其中
            SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
            searchRequest.source(searchSourceBuilder);
            // 执行查询,然后处理响应结果
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            Aggregations aggregations = searchResponse.getAggregations();
            ParsedStringTerms agg = aggregations.get("categoryGroup");
            Map<String,Object> resultMap = new HashMap<>();
            for (Terms.Bucket bucket : agg.getBuckets()) {
                resultMap.put(bucket.getKeyAsString(), bucket.getDocCount());
            }
            return resultMap;
        } catch (Exception e) {
            log.error("分桶聚合查询失败", e);
            return null;
        }
    }

为什么8.0版本要弃用?

在 Elasticsearch 8.0 版本中,High Level REST Client 被标记为弃用,主要是因为以下几个原因:

  • 维护成本:High Level REST Client 的维护成本较高,因为它需要随着 Elasticsearch 的发展而持续更新,以保持与 REST API 的兼容性。
  • 性能考量:为了提高性能和减少内存消耗,Elasticsearch 开发团队决定移除一些高成本的组件,High Level REST Client 因其封装层次较多而成为目标之一。
  • 替代方案:Elasticsearch 推出了新的 Java API Client,这是一个更轻量级、更高效的选择,它直接使用了 Elasticsearch 的内部协议,从而提供了更好的性能和更低的延迟。
    新的 Java API Client 能够更好地利用 Elasticsearch 的内部机制,减少序列化和反序列化的开销,同时也提供了更细粒度的控制和更高的灵活性。因此,Elasticsearch 推荐用户迁移到新的 Java API Client 上。

所以后续我会再整理个新的Java API Client 用法。与时俱进。

点赞收藏加评论

用到的时候再来看,收获更大。

相关推荐
RainbowSea1 小时前
问题:后端由于字符内容过长,前端展示精度丢失修复
java·spring boot·后端
风象南1 小时前
SpringBoot 控制器的动态注册与卸载
java·spring boot·后端
我是一只代码狗2 小时前
springboot中使用线程池
java·spring boot·后端
hello早上好2 小时前
JDK 代理原理
java·spring boot·spring
PanZonghui2 小时前
Centos项目部署之运行SpringBoot打包后的jar文件
linux·spring boot
沉着的码农3 小时前
【设计模式】基于责任链模式的参数校验
java·spring boot·分布式
zyxzyx6663 小时前
Flyway 介绍以及与 Spring Boot 集成指南
spring boot·笔记
一头生产的驴5 小时前
java整合itext pdf实现自定义PDF文件格式导出
java·spring boot·pdf·itextpdf
程序员张37 小时前
SpringBoot计时一次请求耗时
java·spring boot·后端
麦兜*14 小时前
Spring Boot启动优化7板斧(延迟初始化、组件扫描精准打击、JVM参数调优):砍掉70%启动时间的魔鬼实践
java·jvm·spring boot·后端·spring·spring cloud·系统架构