当前位置: 首页 > article >正文

Elasticsearch使用Easy-Es + RestHighLevelClient实现深度分页跳页

注意!!!博主只在测试环境试了一下,没有发到生产环境跑。因为代码还没写完客户说不用弄了( •̩̩̩̩_•̩̩̩̩ ) 也好,少个功能少点BUG

使用from + size的时候发现存在max_result_window=10000的限制,于是研究使用别的方法,最终想出个歪招来实现深度分页跳页。

1、三种分页方式比较

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、实现过程

  • BaseEsMapper构建查询条件
  • RestHighLevelClient实现scroll大分页滚动查询
  • 判断是否跨页,如果跨页滚动拿到两页数据
  • 根据前端分页参数进行数据截取返回

3、实现代码

引入依赖

<dependency>
    <groupId>org.dromara.easy-es</groupId>
    <artifactId>easy-es-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-client</artifactId>
    <version>7.6.2</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.6.2</version>
</dependency>

SearchResult

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Data
public class SearchResult<T> implements Serializable {

    private int total;

    private List<DocBaseEntity<T>> source = new ArrayList<>();

    private JSONObject aggregations;

    private String scrollId;

    public void addData(DocBaseEntity<T> obj){
        source.add(obj);
    }

    public List<T> getDatas(){
        return source.stream().map(DocBaseEntity::getDatas).collect(Collectors.toList());
    }

    public void addDatas(List<DocBaseEntity<T>> objs){
        source.addAll(objs);
    }

    public void setTotal(Object total){
        this.total = Integer.parseInt(String.valueOf(total));
    }

    public JSONObject toJSONObject(){
        return JSONUtil.parseObj(this,true);
    }
}

DocBaseEntity

import cn.hutool.json.JSONObject;
import lombok.Data;
import org.elasticsearch.search.SearchHit;
import java.io.Serializable;

@Data
public class DocBaseEntity<T> implements Serializable {

    private String _index;

    private String _type;

    private String _id;

    private T datas;

    public DocBaseEntity(SearchHit data) {
        this._index = data.getIndex();
        this._type = data.getType();
        this._id = data.getId();
    }

    public DocBaseEntity(JSONObject jsonHits){
        this._index = jsonHits.getStr("_index");
        this._type = jsonHits.getStr("_type");
        this._id = jsonHits.getStr("_id");
    }

    public T getDatas(){
        return datas;
    }

}

RestClientConfig

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RestClientConfig {
    @Value("${elasticsearch.clientIps}")
    private String clientIps;

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

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

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

    private HttpHost[] getHttpHosts(String clientIps, int esHttpPort) {
        String[] clientIpList = clientIps.split(",");
        HttpHost[] httpHosts = new HttpHost[clientIpList.length];
        for (int i = 0; i < clientIpList.length; i++) {
            httpHosts[i] = new HttpHost(clientIpList[i], esHttpPort, "http");
        }
        return httpHosts;
    }

    /**
     * 创建带HTTP Basic Auth认证rest客户端
     */
    @Bean
    public RestHighLevelClient restHighLevelClient() {
        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
        return new RestHighLevelClient(RestClient.builder(getHttpHosts(clientIps, httpPort)).setHttpClientConfigCallback((HttpAsyncClientBuilder httpAsyncClientBuilder) -> httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider)));
    }
}

滚动查询方法

@Autowired
private RestHighLevelClient restHighLevelClient;

/**
 * 滚动查询
 * @param indexName
 * @param pageNo
 * @param pageSize
 * @param scrollId
 * @param resultObj
 * @param wrapper
 * @param <T>
 * @return
 * @throws IOException
 */
public <T> SearchResult<T> scrollSearchElasticSearchDatas(String indexName,
                                                          int pageNo,
                                                          int pageSize,
                                                          String scrollId,
                                                          Class<T> resultObj,
                                                          LambdaEsQueryWrapper<ES实体类Bean> wrapper) throws IOException {
    SearchSourceBuilder searchSourceBuilder = rdsBookCommonDetailEsMapper.getSearchSourceBuilder(wrapper);
    searchSourceBuilder.size(pageSize);
    SearchRequest searchRequest = new SearchRequest(indexName);
    searchRequest.source(searchSourceBuilder);
    //设定scroll失效时长
    Scroll scroll = new Scroll(TimeValue.timeValueMinutes(3));
    searchRequest.scroll(scroll);
    SearchResponse searchResponse = null;
    if(StrUtil.isEmpty(scrollId)){
        searchResponse = executSearch(searchRequest);
        String tempscrollId = searchResponse.getScrollId();
        SearchScrollRequest searchScrollRequest = new SearchScrollRequest(tempscrollId);
        searchScrollRequest.scroll(scroll);
        for (int i = 0; i < (pageNo -1); i++) {
            searchResponse = scrollSearch(searchScrollRequest);
        }
        scrollId = tempscrollId;
    }else {
        SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
        searchResponse = scrollSearch(searchScrollRequest);
    }
    //构建结果
    SearchResult<T> result = createSearchResult(searchResponse,resultObj);
    result.setScrollId(scrollId);
    return result;
}

/**
 * 执行查询
 */
private SearchResponse executSearch(SearchRequest searchRequest)     {
    SearchResponse searchResponse = null;
    try{
        searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
    }catch(Exception e){
        //异常处理
    }
    return searchResponse;
}

/**
 * 滚动查询执行
 * @param searchScrollRequest
 * @return
 */
private  SearchResponse scrollSearch(SearchScrollRequest searchScrollRequest){
    SearchResponse searchResponse = null;
    try{
        searchResponse = restHighLevelClient.scroll(searchScrollRequest,RequestOptions.DEFAULT);
    }catch(Exception e){
        //异常处理
    }
    return searchResponse;
}

/**
 * 构建目标结果
 * @param response 返回参数
 * @param resultObj 类对象
 * @param <T>
 * @return
 */
private <T> SearchResult<T> createSearchResult(SearchResponse response,Class<T> resultObj){
    SearchResult<T> resultMap = new SearchResult<>();
    SearchHit[] datas = response.getHits().getHits();
    for(SearchHit data:datas){
        DocBaseEntity<T> temp = new DocBaseEntity<>(data);
        temp.setDatas(JSONUtil.toBean(JSONUtil.parseObj(data.getSourceAsMap()),resultObj));
        resultMap.addData(temp);
    }
    resultMap.setTotal(response.getHits().getTotalHits().value);
    return resultMap;
}

/**
 * 关闭scroll
 * @param scrollId
 * @throws IOException
 */
private void clearScrollSession(String scrollId) throws IOException {
    if (scrollId != null) {
        ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
        clearScrollRequest.addScrollId(scrollId);
        ClearScrollResponse clearScrollResponse = restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
        clearScrollResponse.isSucceeded();
    }
}

使用

LambdaEsQueryWrapper<ES实体类Bean> wrapper = new LambdaEsQueryWrapper<>();
wrapper.eqxxx
...构建各种查询条件
// 游标id
String scrollId = "";
// 第一次查询
SearchResult<ES实体类Bean> firstSearchResult = null;
// 第二次查询
SearchResult<ES实体类Bean> secondSearchResult = null;
// 每次滚动查询5000条数据,根据前端传的分页参数决定要滚动到哪一页
int maxPage = ((searchCommonDetaiVo.getCurrentPage() * searchCommonDetaiVo.getPageSize()) / 5000) + 1;
try {
	firstSearchResult = scrollSearchElasticSearchDatas("IndexName", maxPage, 5000, scrollId, ES实体类Bean.class, wrapper);
	scrollId = firstSearchResult.getScrollId();
} catch (IOException e) {
	e.printStackTrace();
}
// 判断是否跨页
if (searchCommonDetaiVo.getPageSize() > (searchCommonDetaiVo.getCurrentPage() * searchCommonDetaiVo.getPageSize()) % 5000) {
	try {
		secondSearchResult = scrollSearchElasticSearchDatas("IndexName", maxPage-1, 5000, scrollId, ES实体类Bean.class, wrapper);
	} catch (IOException e) {
		e.printStackTrace();
	}
}
// TODO 根据前端分页参数截取数据返回
try {
	clearScrollSession(scrollId);
} catch (IOException e) {
	log.info("游标清理失败");
	e.printStackTrace();
}

http://www.kler.cn/news/330740.html

相关文章:

  • 【热门主题】000002 案例 JavaScript网页设计案例
  • 如何在 Kubernetes 上部署和配置开源数据集成平台 Airbyte?
  • LampSecurityCTF7 靶机渗透 (sql 注入, 文件上传, 密码喷射)
  • vue单点登录异步执行请求https://xxx.com获取并处理数据
  • 博文汇总目录
  • C语言自定义类型结构体
  • 茴香豆 + Qwen-7B-Chat-Int8
  • 高级架构师面试题
  • 第24天sql注入(小迪安全学习)
  • GPT与大模型行业落地实践探索
  • OpenCV视频I/O(13)视频采集类VideoCapture之等待多个视频流中的任意一个变为可用状态函数waitAny()的使用
  • HKMG工艺为什么要用金属栅极?
  • 【Android】浅析六大设计原则
  • C++中的类型推导:auto 和 decltype 介绍
  • 如何理解矩阵的复数特征值和特征向量?
  • 【60天备战2024年11月软考高级系统架构设计师——第35天:系统安全设计——安全设计模式】
  • quiz: python网络爬虫之规则1
  • 【H2O2|全栈】关于CSS(8)CSS3扩充了哪些新鲜的东西?
  • ISP下载,IAP,ICP,USB转TTL下载SWIM、DAP-link、CMSIS-DAP、ST-LINK,SPI(通信方式),
  • 计算机视觉学习--目标检测Java开发案例