SpringBoot整合ES-Elasticsearch的实例

目录
  • 概述
  • 添加Maven依赖
  • 配置application.yml
  • 创建索引对象
  • SpringBoot操作ES数据的三种方式
    • 实现索引对应的Repository
  • 文档操作
    • 文档保存、查询、删除
  • 分页查询与滚动查询
  • ES深度分页 vs 滚动查询
  • SpringBoot集成ES基本使用
    • 在test中测试

概述

本文介绍 Spring Boot 项目中整合 ElasticSearch 并实现 CRUD 操作,包括分页、滚动等功能。

添加Maven依赖

<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

配置application.yml

spring:
  elasticsearch:
    rest:
      uris: 192.168.1.81:9200

创建索引对象

package com.practice.elkstudy.entity;
import cn.hutool.core.date.DateTime;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import java.util.Date;
/**
 * @Description : 文档模型
 * @Version : V1.0.0
 * @Date : 2021/12/22 14:08
 */
@Document(indexName = "article")
@Data
public class ArticleEntity {
    @Id
    private String id;
    private String title;
    private String content;
    private Integer userId;
    private Date createTime = DateTime.now();
}

SpringBoot操作ES数据的三种方式

  • 实现ElasticsearchRepository接口
  • 引入ElasticsearchRestTemplate
  • 引入ElasticsearchOperations

实现索引对应的Repository

package com.practice.elkstudy.repository;
import com.practice.elkstudy.entity.ArticleEntity;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
/**
 * @Description : article数据操作接口
 * @Version : V1.0.0
 * @Date : 2021/12/22 14:18
 */
public interface ArticleRepository extends ElasticsearchRepository<ArticleEntity,String> {
}

文档操作

下面可以使用这个 ArticleRepository 来操作 ES 中的 Article 数据。

我们这里没有手动创建这个 Article 对应的索引,由 elasticsearch 默认生成。

下面的接口,实现了 spring boot 中对 es 数据进行插入、更新、分页查询、滚动查询、删除等操作。可以作为一个参考。

其中,使用了 Repository 来获取、保存、删除 ES 数据;使用 ElasticsearchRestTemplate 或 ElasticsearchOperations 来进行分页/滚动查询。

文档保存、查询、删除

package com.practice.elkstudy.controller.controller;
import com.practice.elkstudy.entity.ArticleEntity;
import com.practice.elkstudy.repository.ArticleRepository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Optional;
/**
 * @Description : article控制类
 * @Version : V1.0.0
 * @Date : 2021/12/22 14:11
 */
@RestController
@RequestMapping("/elk")
public class ArticleController {
    @Resource
    private ArticleRepository articleRepository;
    /**
     * 根据文档id查询数据
     *
     * @param id 文档id
     * @return 文档详情
     */
    @GetMapping("/byId")
    public String findById(@RequestParam String id) {
        Optional<ArticleEntity> record = articleRepository.findById(id);
        return  record.toString();
    }
    /**
     * 保存文档信息
     *
     * @param article 文档详情
     * @return 保存的文档信息
     */
    @PostMapping("/saveArticle")
    public String saveArticle(@RequestBody ArticleEntity article) {
        ArticleEntity result = articleRepository.save(article);
        return result.toString();
    }
	@DeleteMapping("/deleteById")
    public String deleteArticle(@RequestParam String id) {
        articleRepository.deleteById(id);
        return "success";
    }
}

分页查询与滚动查询

package com.practice.elkstudy.controller.controller;
import com.practice.elkstudy.entity.ArticleEntity;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchHitsImpl;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
 * @Description : article高级查询
 * @Version : V1.0.0
 * @Date : 2021/12/22 15:10
 */
@RestController
@RequestMapping("/elk")
public class ArticleAdvanceController {
    @Autowired
    private ElasticsearchRestTemplate restTemplate;
    @Autowired
    private ElasticsearchOperations operations;
    /**
     * 分页查询
     *
     * @param pageNum  页码,从0开始
     * @param pageSize 分页大小
     * @return 查询结果
     */
    @GetMapping("/queryPage")
    public String queryPage(@RequestParam int pageNum, @RequestParam int pageSize) {
        NativeSearchQuery query = new NativeSearchQuery(new BoolQueryBuilder());
        query.setPageable(PageRequest.of(pageNum, pageSize));
        // 方法1
        SearchHits<ArticleEntity> search = restTemplate.search(query, ArticleEntity.class);
        // 方法2
        // SearchHits<ArticleEntity> search = operations.search(query, ArticleEntity.class);
        List<ArticleEntity> articles = search.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());
        return articles.toString();
    }
    /**
     * 滚动查询
     *
     * @param scrollId 滚动id
     * @param pageSize 分页大小
     * @return 查询结果
     */
    @GetMapping(value = "/scrollQuery")
    public String scroll(String scrollId, Integer pageSize) {
        if (pageSize == null || pageSize <= 0) {
            return "please input query page num";
        }
        NativeSearchQuery query = new NativeSearchQuery(new BoolQueryBuilder());
        query.setPageable(PageRequest.of(0, pageSize));
        SearchHits<ArticleEntity> searchHits;
        if (StringUtils.isEmpty(scrollId) || scrollId.equals("0")) {
            // 开启一个滚动查询,设置该scroll上下文存在60s
            // 同一个scroll上下文,只需要设置一次query(查询条件)
            searchHits = restTemplate.searchScrollStart(60000, query, ArticleEntity.class, IndexCoordinates.of("article"));
            if (searchHits instanceof SearchHitsImpl) {
                scrollId = ((SearchHitsImpl) searchHits).getScrollId();
            }
        } else {
            // 继续滚动
            searchHits = restTemplate.searchScrollContinue(scrollId, 60000, ArticleEntity.class, IndexCoordinates.of("article"));
        }
        List<ArticleEntity> articles = searchHits.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());
        if (articles.size() == 0) {
            // 结束滚动
            restTemplate.searchScrollClear(Collections.singletonList(scrollId));
            scrollId = null;
        }
        if (Objects.isNull(scrollId)) {
            Map<String, String> result = new HashMap<>(2);
            result.put("articles", articles.toString());
            result.put("message", "已到末尾");
            return result.toString();
        } else {
            Map<String, String> result = new HashMap<>();
            result.put("count", String.valueOf(searchHits.getTotalHits()));
            result.put("pageSize", String.valueOf(articles.size()));
            result.put("articles", articles.toString());
            result.put("scrollId", scrollId);
            return result.toString();
        }
    }
}

ES深度分页 vs 滚动查询

之前遇到的一个问题,日志检索的接口太慢了。

开始使用的是深度分页,即1,2,3…10,这样的分页查询,查询条件较多(十多个参数)、查询数据量较大(单个日志索引约2亿条数据)。

分页查询速度慢的原因在于:ES的分页查询,如查询第100页数据,每页10条,是先从每个分区(shard,一个索引默认是5个shard)中把命中的前100*10条数据查出来,然后协调节点进行合并操作,最后给出100页的数据。也就是说,实际被加载到内存的数据远远超过理想情况。

这样,索引分片数越多,查询页数越多,查询速度就越慢。ES默认的max_result_window是10000条,也就是正常情况下,用分页查询到10000条数据时,就不会在返回下一页数据了。

如果不需要进行跳页,比如直接查询第100页数据,或者数据量非常大,那么可以考虑用scroll查询。在scroll查询下,第1次需要根据查询参数开启一个scroll上下文,设置上下文缓存时间。以后的滚动只需要根据第一次返回的scrollId来进行即可。

scroll只支持往下滚动,如果想要往前滚动,还可以根据scrollId缓存查询结果,这样就可以实现上下文滚动查询了一一就像大家经常使用的淘宝商品检索时上下滚动一样。

SpringBoot集成ES基本使用

#配置es
#Liunx 上的ip地址和配置端口号
spring.elasticsearch.rest.uris=192.168.113.129:9200

在test中测试

import com.alibaba.fastjson.JSON;
import com.hzx.pojo.User;
import com.hzx.utils.ESconst;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
@Autowired
private RestHighLevelClient client;
@Test
void contextLoads() throws IOException {
    //创建索引请求
    CreateIndexRequest request = new CreateIndexRequest("hong_index");
    //客户端执行请求 IndicesClient  create创建请求  RequestOptions.DEFAULT默认请求参数
    CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
    //获取返回的参数
    System.out.println(createIndexResponse);
}
@Test
void test2() throws IOException {
    //获取指定索引库
    GetIndexRequest request = new GetIndexRequest("hong_index2");
    //判断获取索引是否存在
    boolean exists = client.indices().exists(request,RequestOptions.DEFAULT);
    //如果索引存在就返回为true  或者 为false
    System.out.println(exists);
}
@Test
void test3() throws IOException {
    //删除指定索引库
    DeleteIndexRequest request = new DeleteIndexRequest("hong_index");
    //获取删除索引
    AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
    //检查索引是否被删除
    System.out.println(delete.isAcknowledged());
}
    //测试添加文档
    @Test
    void test4() throws IOException {
        //创建对象
        User user = new User("枣信",18);
        //创建索引库
        IndexRequest request = new IndexRequest("hong_index");
        //规则 为 put /hong_index/_doc/1
        //创建的id
        request.id("1");
        //创建的时间
        request.timeout(TimeValue.timeValueSeconds(1));
//        request.timeout("1s");
        //将数据放入到请求     JSON.toJSONString(user)将对象转换为json
        request.source(JSON.toJSONString(user), XContentType.JSON);
        //客户端发送请求   向索引中添加数据
        IndexResponse indices = client.index(request, RequestOptions.DEFAULT);
        //获取返回的json对象
        System.out.println(indices.toString());
        //获取发送请求的状态 添加为CREATED  更新为OK
        System.out.println(indices.status());
    }
//获取文档信息
@Test
void test6() throws IOException {
    //根据索引传入的id获取
    GetRequest getRequest = new GetRequest("hong_index","1");
    //通过get获取信息
    GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
    //根据指定的Source获取对应内容
    System.out.println(getResponse.getSourceAsString());
    //打印json对象
    System.out.println(getResponse);
}
//更新 修改信息
@Test
void test7() throws IOException {
    //根据索引库传入的id更新
    UpdateRequest updateRequest = new UpdateRequest("hong_index","1");
    //更新时间
    updateRequest.timeout("1s");
    //创建对象
    User user = new User("李四", 26);
    //更新    将对象转换为json
    updateRequest.doc(JSON.toJSONString(user),XContentType.JSON);
    //客户端发送请求,进行更新
    UpdateResponse update = client.update(updateRequest, RequestOptions.DEFAULT);
    //获取更新状态
    System.out.println(update.status());
}
//删除文档信息
@Test
void test8() throws IOException {
    //根据传入的索引id进行删除
    DeleteRequest request = new DeleteRequest("hong_index","1");
    //发送请求,删除
    DeleteResponse delete = client.delete(request, RequestOptions.DEFAULT);
    //获取删除的状态   没有删除成功为NOT_FOUND 删除成功为OK
    System.out.println(delete.status());
}
//批量添加数据
@Test
void test9() throws IOException {
    //创建批量添加
    BulkRequest bulkRequest = new BulkRequest();
    //添加时间
    bulkRequest.timeout("8s");
    //创建一个arraylist集合
    ArrayList<User> userList = new ArrayList<>();
    userList.add(new User("李四",19));
    userList.add(new User("王五",25));
    userList.add(new User("赵刚",30));
    userList.add(new User("张三",21));
    userList.add(new User("赵六",36));
    userList.add(new User("小武",20));
    //批量处理请求
    for (int i = 0; i < userList.size(); i++) {
        //批量更新和删除 在这修改对应的请求即可       不添加id(""+(i+1)) 会默认随机id,在大数据情况下,让他默认随机id
        bulkRequest.add(new IndexRequest("hong_index").id(""+(i+1)).source(JSON.toJSONString(userList.get(i)),XContentType.JSON));
    }
    //批量添加发送请求
    BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
    //获取批量添加的状态 返回false代表添加成功
    System.out.println(bulk.hasFailures());
}
//查询索引信息
@Test
void test10() throws IOException {
    //查询
    SearchRequest searchRequest = new SearchRequest(ESconst.ES_INDEX);
    //构建搜索条件
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
    //查询条件,可以使用QueryBuilders工具来实现
    // QueryBuilders.termQuery精确查询
    // QueryBuilders.matchQuery()查询所有
    TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "李四");
    //查询的时间
    sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
    //将查询的sourceBuilder放入searchRequest中
    searchRequest.source(sourceBuilder);
    //发送请求
    SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
    //获取信息
    System.out.println(JSON.toJSONString(search.getHits()));
    //循环变量出信息
    for(SearchHit documentFields : search.getHits().getHits()){
        //获取所有信息
        System.out.println(documentFields.getSourceAsMap());
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • SpringBoot 如何整合 ES 实现 CRUD 操作

    本文介绍 Spring Boot 项目中整合 ElasticSearch 并实现 CRUD 操作,包括分页.滚动等功能. 之前在公司使用 ES,一直用的是前辈封装好的包,最近希望能够从原生的 Spring Boot/ES 语法角度来学习 ES 的相关技术.希望对大家有所帮助. 本文为 spring-boot-examples 系列文章节选,示例代码已上传至 https://github.com/laolunsi/spring-boot-examples 安装 ES 与可视化工具 前往 ES 官方

  • SpringBoot整合ES高级查询方式

    目录 1.配置 2.API操作ES 2.1 查询索引列表 2.2 TermsQuery 2.3 WildcardQuery 2.4 RangeQuery 2.5 MatchQuery 2.6 MultiMatchQuery 2.7 ExistsQuery 2.8 BoolQuery 2.9 排序 2.10 结果字段过滤 2.11 分页 2.22 聚合 springboot版本:2.0.5.RELEASE elasticsearch版本:7.9.1 1.配置 引入依赖: <dependency>

  • Spring Boot2.0整合ES5实现文章内容搜索实战

    一.文章内容搜索思路 上一篇讲了在怎么在 Spring Boot 2.0 上整合 ES 5 ,这一篇聊聊具体实战.简单讲下如何实现文章.问答这些内容搜索的具体实现.实现思路很简单: 基于「短语匹配」并设置最小匹配权重值 哪来的短语,利用 IK 分词器分词 基于 Fiter 实现筛选 基于 Pageable 实现分页排序 这里直接调用搜索的话,容易搜出不尽人意的东西.因为内容搜索关注内容的连接性.所以这里处理方法比较 low ,希望多交流一起实现更好的搜索方法.就是通过分词得到很多短语,然后利用短

  • springboot集成es详解

    1.导入 maven依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-dataelasticsearch</artifactId> <dependency> 注意 保持版本一致 我用的是7.6.2版本的 <properties> <java.version>1.8</jav

  • 使用SpringBoot整合ssm项目的实例详解

    SpringBoot是什么? Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程. Spring Boot 现在已经成为 Java 开发领域的一颗璀璨明珠,它本身是包容万象的,可以跟各种技术集成.成为 SpringBoot 全家桶,成为一把万能钥匙. SpringBoot的特点 1.创建独立的 Spring 应用程序 2.嵌入的 Tomcat ,无需部署 WAR 文件 3.简化 Maven 配置 4.自动配置 Spr

  • Springboot整合Shiro的代码实例

    这篇文章主要介绍了Springboot整合Shiro的代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.导入依赖 <!--shiro--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</versio

  • SpringBoot 整合 ElasticSearch操作各种高级查询搜索

    目录 一.简介 二.代码实践 三.小结 一.简介 在上篇​SpringBoot 整合 ElasticSearch​​​文章中,我们详细的介绍了 ElasticSearch 的索引和文档的基本增删改查的操作方法! 本文将重点介绍 ES 的各种高级查询写法和使用. 废话不多说,直接上代码! 二.代码实践 本文采用的SpringBoot版本号是2.1.0.RELEASE,服务端 es 的版本号是6.8.2,客户端采用的是官方推荐的Elastic Java High Level Rest Client版

  • 利用 SpringBoot 在 ES 中实现类似连表查询功能

    目录 一.摘要 二.项目实践 2.1添加依赖 2.2配置 es 客户端 2.3初始化索引结构 2.4向 es 中同步文档数据 2.5内嵌对象查询 三.小结 一.摘要 在上篇文章中,我们详细的介绍了如何在 ES 中精准的实现嵌套json对象查询? 那么问题来了,我们如何在后端通过技术方式快速的实现 es 中内嵌对象的数据查询呢? 为了方便更容易掌握技术,本文主要以上篇文章中介绍的通过商品找订单为案例,利用 SpringBoot 整合 ES 实现这个业务需求,向大家介绍具体的技术实践方案,存入es中

  • es(elasticsearch)整合SpringCloud(SpringBoot)搭建教程详解

    注意:适用于springboot或者springcloud框架 1.首先下载相关文件 2.然后需要去启动相关的启动文件 3.导入相关jar包(如果有相关的依赖包不需要导入)以及配置配置文件,并且写一个dao接口继承一个类,在启动类上标注地址 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> &l

  • SpringBoot 整合Jest实例代码讲解

    [1]添加Elasticsearch-starter pom文件添加starter如下: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> SpringBoot默认支持两种技术和Elasticsearch进行交互:Sp

  • SpringBoot整合Elasticsearch并实现CRUD操作

     配置准备 在build.gradle文件中添加如下依赖: compile "org.elasticsearch.client:transport:5.5.2" compile "org.elasticsearch:elasticsearch:5.5.2" //es 5.x的内部使用的 apache log4日志 compile "org.apache.logging.log4j:log4j-core:2.7" compile "org

  • SpringBoot整合Elasticsearch游标查询的示例代码(scroll)

    游标查询(scroll)简介 scroll 查询 可以用来对 Elasticsearch 有效地执行大批量的文档查询,而又不用付出深度分页那种代价. 游标查询会取某个时间点的快照数据. 查询初始化之后索引上的任何变化会被它忽略. 它通过保存旧的数据文件来实现这个特性,结果就像保留初始化时的索引 视图 一样. 启用游标查询可以通过在查询的时候设置参数 scroll 的值为我们期望的游标查询的过期时间. 游标查询的过期时间会在每次做查询的时候刷新,所以这个时间只需要足够处理当前批的结果就可以了,而不

随机推荐