You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Elasticsearch+Kendo UI Grid:虚拟滚动替代无限滚动的实现疑问

嘿,刚好我之前在Kendo UI Grid里结合Elasticsearch做虚拟滚动的时候踩过类似的坑,给你分享几个实用的解决方案:

方案1:用Point in Time (PIT) + Search After 替代Scroll API

这是Elasticsearch 7.10+推出的特性,完美解决Scroll API无法灵活控制请求范围的问题,同时还能保持会话级的数据一致性(就像Scroll的快照机制一样)。

  • 核心逻辑:先创建一个PIT(索引数据的快照会话),之后所有查询都绑定到这个PIT上,用search_after参数基于上一页最后一条数据的排序值来获取下一页,也可以结合size控制每页数量。相比Scroll,它支持你在同一个会话里灵活调整查询范围,甚至实现跳页(只要你能拿到对应位置的排序值)。
  • 具体步骤:
    1. 初始化时创建PIT,设置keep_alive(会话存活时间,比如1分钟):
      POST /your_index/_pit?keep_alive=1m
      
      接口会返回一个pit_id,后续所有查询都要带上这个ID。
    2. 第一次查询数据,指定size和唯一排序字段(比如业务ID,避免排序冲突):
      GET /_search
      {
        "size": 50,
        "query": { "match": { "your_field": "your_value" } },
        "sort": [ { "id": "asc" } ],
        "pit": { "id": "YOUR_PIT_ID", "keep_alive": "1m" }
      }
      
    3. 拿到返回结果后,记录最后一条数据的sort值,下次查询用search_after参数定位:
      GET /_search
      {
        "size": 50,
        "query": { "match": { "your_field": "your_value" } },
        "sort": [ { "id": "asc" } ],
        "pit": { "id": "YOUR_PIT_ID", "keep_alive": "1m" },
        "search_after": [LAST_RECORD_SORT_VALUE]
      }
      
    4. 会话结束后记得删除PIT,避免占用ES资源:
      DELETE /_pit
      {
        "id": "YOUR_PIT_ID"
      }
      
  • 结合Kendo UI Grid虚拟滚动:你可以在Grid的dataSource.transport.read方法里处理PIT的创建、查询参数拼接,以及search_after的更新。Grid的虚拟滚动会自动触发数据加载事件,你只需要根据当前需要加载的位置传递对应参数即可。
方案2:直接用from + size(适合小数据量场景)

如果你的数据总量不大(比如少于10000条),直接用ES的from和size参数是最简单的方案,完全不需要额外的会话机制。

  • 核心逻辑:Kendo UI Grid的虚拟滚动会自动传递skip(对应ES的from)和take(对应ES的size)参数,你只需要把这两个参数映射到ES的查询里就行。
  • 示例代码(Kendo UI部分):
    $("#yourGrid").kendoGrid({
      virtual: true,
      scrollable: { virtual: true },
      dataSource: {
        transport: {
          read: function(options) {
            const esQuery = {
              from: options.data.skip,
              size: options.data.take,
              query: { "match_all": {} } // 替换成你的实际查询条件
            };
            // 发送请求到ES
            fetch('/_search', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify(esQuery)
            })
            .then(res => res.json())
            .then(response => {
              options.success({
                data: response.hits.hits.map(hit => hit._source),
                total: response.hits.total.value
              });
            });
          }
        },
        schema: {
          data: 'data',
          total: 'total'
        },
        pageSize: 50
      }
    });
    
  • 注意事项:ES默认限制from + size不能超过10000,如果你的数据量超过这个值,要么修改索引的index.max_result_window参数(不推荐大数据量修改,会影响查询性能),要么就改用方案1。
为什么不推荐继续用Scroll API?

Scroll API的设计初衷是批量导出全量数据,它创建快照后只能按顺序遍历,无法跳转到指定位置(也就是没法控制from),完全不适合虚拟滚动场景——用户可能直接拖动滚动条到中间位置,这时候Scroll API根本没法快速定位到对应数据段。

总结一下:如果数据量大或者需要严格的数据一致性,优先选PIT + Search After;如果数据量小,直接用from + size最省事。

内容的提问来源于stack exchange,提问作者Meyra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:43:12