配置
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 桶聚合
桶排序聚合

两次聚合,第一次 进行 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
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
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深度分页问题