Elasticsearch

予早 2025-08-31 14:59:18
Categories: Tags:

配置

https://www.cnblogs.com/ginb/p/7026253.html

聚合

桶聚合

terms 桶聚合

对 batch_id 进行聚合,按照 batch_id 降序排序,然后返回前 3 条数据

桶聚合本身并不支持分页,可以基于 桶排序聚合 实现受限的分页(不能通过size 和from,这是对source的分页,现在是对聚合结果——桶的分页)

 POST ds_data_change_log/_search
 {
   "size": 0,
   "query": {
     "match_all": {}
   },
   "aggs": {
     "batch_id_agg": {
       "terms": {
         "field": "batch_id",
         "order": {
           "_key": "desc"
         },
         "size": 3
       }
     }
   }
 }
 {
   "took" : 172,
   "timed_out" : false,
   "_shards" : {
     "total" : 1,
     "successful" : 1,
     "skipped" : 0,
     "failed" : 0
   },
   "hits" : {
     "total" : {
       "value" : 10000,
       "relation" : "gte"
     },
     "max_score" : null,
     "hits" : [ ]
   },
   "aggregations" : {
     "batch_id_agg" : {
       "doc_count_error_upper_bound" : 0,
       "sum_other_doc_count" : 1969937,
       "buckets" : [
         {
           "key" : 1855,
           "doc_count" : 5
         },
         {
           "key" : 1854,
           "doc_count" : 5
         },
         {
           "key" : 1853,
           "doc_count" : 28
         }
       ]
     }
   }
 }

composite 桶聚合

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-composite-aggregation.html

桶排序聚合

image-20240615161605370

两次聚合,第一次 进行 terms 聚合,对 batch_id 聚合并 batch_id 对降序排序,返回结果的前1条数据

第二次,对第一次的结果(3个桶)进行排序,然后分页,当然还没有总数量,可以使用 cardinality aggregation

 POST ds_data_change_log/_search
 {
   "size": 0,
   "query": {
     "match_all": {}
   },
   "aggs": {
     "batch_id_agg": {
       "terms": {
         "field": "batch_id",
         "order": {
           "_key": "desc"
         },
         "size": 3
       },
       "aggs": {
         "batch_id_desc": {
           "bucket_sort": {
             "from": 0,
             "size": 5,
             "sort": []
           }
         }
       }
     }
   }
 }
 {
   "took" : 107,
   "timed_out" : false,
   "_shards" : {
     "total" : 1,
     "successful" : 1,
     "skipped" : 0,
     "failed" : 0
   },
   "hits" : {
     "total" : {
       "value" : 10000,
       "relation" : "gte"
     },
     "max_score" : null,
     "hits" : [ ]
   },
   "aggregations" : {
     "batch_id_agg" : {
       "doc_count_error_upper_bound" : 0,
       "sum_other_doc_count" : 1969937,
       "buckets" : [
         {
           "key" : 1855,
           "doc_count" : 5
         },
         {
           "key" : 1854,
           "doc_count" : 5
         },
         {
           "key" : 1853,
           "doc_count" : 28
         }
       ]
     }
   }
 }

指标聚合

cardinality aggregation

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-cardinality-aggregation.html

total_agg与batch_id_agg不相关,实现了一个不优雅的聚合字段的分页效果

 POST ds_data_change_log/_search
 {
   "size": 0,
   "query": {
     "match_all": {}
   },
   "aggs": {
     "batch_id_agg": {
       "terms": {
         "field": "batch_id",
         "order": {
           "_key": "desc"
         },
         "size": 3
       },
       "aggs": {
         "batch_id_desc": {
           "bucket_sort": {
             "from": 0,
             "size": 5,
             "sort": []
           }
         }
       }
     },
     "total_agg": {
       "cardinality": {
         "field": "batch_id"
       }
     }
   }
 }
 {
   "took" : 440,
   "timed_out" : false,
   "_shards" : {
     "total" : 1,
     "successful" : 1,
     "skipped" : 0,
     "failed" : 0
   },
   "hits" : {
     "total" : {
       "value" : 10000,
       "relation" : "gte"
     },
     "max_score" : null,
     "hits" : [ ]
   },
   "aggregations" : {
     "total_agg" : {
       "value" : 163
     },
     "batch_id_agg" : {
       "doc_count_error_upper_bound" : 0,
       "sum_other_doc_count" : 1969937,
       "buckets" : [
         {
           "key" : 1855,
           "doc_count" : 5
         },
         {
           "key" : 1854,
           "doc_count" : 5
         },
         {
           "key" : 1853,
           "doc_count" : 28
         }
       ]
     }
   }
 }

Top hits aggregation

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-top-hits-aggregation.html

composite 支持after

但search after是另一种语法

https://www.cnblogs.com/leeSmall/p/9215909.html

https://tower.im/teams/257331/repository_documents/96573/

分页

ES评分

https://juejin.cn/post/7010660177791680520

https://blog.csdn.net/u010454030/article/details/134697579

https://www.elastic.co/cn/blog/practical-bm25-part-2-the-bm25-algorithm-and-its-variables

对聚合分组的结果进行分页

 {
   
     "aggs": {
         "groupTicketId": {
             "terms": {
                "field": "ticketId" // 按照 ticketId 进行分组
             }, 
             "aggs": {
                
                  "page": {
                     "bucket_sort": {
                         
                         "from": 0,
                         "size": 2
                     }
                 }
             }
         }
     }
 }

滚动查询,适用于分批获取大批量数据

 scanResp = helpers.scan(es, _body, scroll= "10m", index= _index, doc_type= _doc_type, timeout="10m")
    
 for resp in scanResp:
    print resp

其他es资料

https://www.cnblogs.com/hello-shf/category/1550315.html

Elasticsearch 深度分页问题

from + size

Elasticsearch 中基本分页由 from size 控制

 GET /student/student/_search
 {
   "query":{
     "match_all": {}
   },
   "from":5000,
   "size":10
 }

意味着 es 需要在各个分片上匹配排序并得到5010条数据,协调节点拿到这些数据再进行排序等处理,然后结果集中取最后10条数据返回。

我们会发现这样的深度分页将会使得效率非常低,因为我只需要查询10条数据,而es则需要执行from+size条数据然后处理后返回。

其次:es为了性能,限制了我们分页的深度,es默认的最大的 max_result_window = 10000;也就是说我们不能分页到10000条数据以上。

 index.max_result_window =10000
 

默认情况下,结果集中最大返回10000条数据, from + size <= 10000 条件满足时查询依然可行,当超过 10000 条,查询直接会失败

scroll

在es中如果我们分页要请求大数据集或者一次请求要获取较大的数据集,scroll都是一个非常好的解决方案。

使用scroll滚动搜索,可以先搜索一批数据,然后下次再搜索一批数据,以此类推,直到搜索出全部的数据来scroll搜索会在第一次搜索的时候,保存一个当时的视图快照,之后只会基于该旧的视图快照提供数据搜索,如果这个期间数据变更,是不会让用户看到的。每次发送scroll请求,我们还需要指定一个scroll参数,指定一个时间窗口,每次搜索请求只要在这个时间窗口内能完成就可以了。

一个滚屏搜索允许我们做一个初始阶段搜索并且持续批量从Elasticsearch里拉取结果直到没有结果剩下。这有点像传统数据库里的cursors(游标)。

滚屏搜索会及时制作快照。这个快照不会包含任何在初始阶段搜索请求后对index做的修改。它通过将旧的数据文件保存在手边,所以可以保护index的样子看起来像搜索开始时的样子。这样将使得我们无法得到用户最近的更新行为。

以滚动方式查询数据,每次滚动返回2条数据,滚动窗口持续5分钟

 GET /student/student/_search?scroll=5m
 {
   "query": {
     "match_all": {}
   },
   "size": 2
 }
 {
   "_scroll_id" : "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAC0YFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtGRZpZVI1dUEyMlNuVzBwU3JNVzR6RVlBAAAAAAAALRsWaWVSNXVBMjJTblcwcFNyTVc0ekVZQQAAAAAAAC0aFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtHBZpZVI1dUEyMlNuVzBwU3JNVzR6RVlB",
   "took" : 0,
   "timed_out" : false,
   "_shards" : {
     "total" : 5,
     "successful" : 5,
     "skipped" : 0,
     "failed" : 0
   },
   "hits" : {
     "total" : 6,
     "max_score" : 1.0,
     "hits" : [
       {
         "_index" : "student",
         "_type" : "student",
         "_id" : "5",
         "_score" : 1.0,
         "_source" : {
           "name" : "fucheng",
           "age" : 23,
           "class" : "2-3"
         }
       },
       {
         "_index" : "student",
         "_type" : "student",
         "_id" : "2",
         "_score" : 1.0,
         "_source" : {
           "name" : "xiaoming",
           "age" : 25,
           "class" : "2-1"
         }
       }
     ]
   }
 }

第二次及以后使用scroll_id进行查询,当查询结果为空时说明所有满足条件的数据已经查询完毕

  GET /_search/scroll
 2 {
 3   "scroll":"5m",
 4   "scroll_id":"DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAC0YFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtGRZpZVI1dUEyMlNuVzBwU3JNVzR6RVlBAAAAAAAALRsWaWVSNXVBMjJTblcwcFNyTVc0ekVZQQAAAAAAAC0aFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtHBZpZVI1dUEyMlNuVzBwU3JNVzR6RVlB"
 5 }

search_after

本质是游标分页,核心是有序的scroll_id

文档中有一个唯一性字段uid,本次查询使用上次最小的id进行查询

 GET /student/student/_search
 {
   "query":{
     "match_all": {}
   },
   "size":2,
   "search_after":[1005],
   "sort":[
     {
       "uid": "desc"
     }
   ]
 }
 GET twitter/_search
 {
     "size": 10,
     "query": {
         "match" : {
             "title" : "elasticsearch"
         }
     },
     "search_after": [1463538857, "654323"],
     "sort": [
         {"date": "asc"},
         {"_id": "desc"}
     ]
 }
分页方式 性能 优点 缺点 场景
from + size 灵活性好,实现简单 深度分页问题 数据量比较小,能容忍深度分页问题
scroll 解决了深度分页问题 无法反应数据的实时性(快照版本)维护成本高,需要维护一个 scroll_id 海量数据的导出(比如笔者刚遇到的将es中20w的数据导入到excel)需要查询海量结果集的数据
search_after 性能最好不存在深度分页问题能够反映数据的实时变更 实现复杂,需要有一个全局唯一的字段连续分页的实现会比较复杂,因为每一次查询都需要上次查询的结果 海量数据的分页

elasticsearch深度分页问题

https://www.cnblogs.com/hello-shf/p/11543453.html

https://www.cnblogs.com/RainSail/p/13850693.html