面试题答案
一键面试search_after工作原理
search_after 基于上一页的最后一条数据来确定下一页的起始点。它需要一个排序字段,当执行查询时,Elasticsearch 根据排序字段对结果进行排序,search_after 利用上一页最后一条数据的排序值来定位下一页的数据。与传统的 from + size 分页方式不同,search_after 不需要记住分页的偏移量,每次请求只依赖上一页的最后一条数据,从而避免了深度分页时随着偏移量增大而导致的性能问题。
使用search_after实现高效分页的具体步骤
- 确定排序字段:选择一个唯一且单调递增(或递减)的字段用于排序,比如时间戳字段(
@timestamp)或者唯一ID字段。 - 首次请求:在首次查询时,使用普通的
sort参数指定排序字段和排序顺序,同时设置size来指定每页返回的文档数量。不使用from参数,而是在查询体中加入search_after,首次查询时search_after为空数组。 - 后续请求:从首次请求的响应结果中获取最后一条文档的排序字段值,将这些值作为下一次请求中
search_after的参数值。同时保持sort和size参数不变,继续发送请求获取下一页数据。
简单场景下应用search_after进行分页操作示例
假设我们有一个索引 blog_posts,其中包含博客文章,每篇文章有 id(唯一标识符)和 published_date(发布日期)字段。我们希望按发布日期降序分页获取文章。
- 首次请求:
{
"size": 10,
"sort": [
{
"published_date": {
"order": "desc"
}
},
{
"id": {
"order": "desc"
}
}
],
"search_after": [],
"query": {
"match_all": {}
}
}
这里 size 设置为10,表示每页返回10条记录。按 published_date 降序排序,如果 published_date 相同则按 id 降序排序。search_after 为空数组用于首次请求。
- 后续请求:假设首次请求返回的最后一条文档的
published_date是2023-10-01T12:00:00Z,id是12345,那么下一次请求如下:
{
"size": 10,
"sort": [
{
"published_date": {
"order": "desc"
}
},
{
"id": {
"order": "desc"
}
}
],
"search_after": [
"2023-10-01T12:00:00Z",
"12345"
],
"query": {
"match_all": {}
}
}
后续每次请求都根据上一次返回结果的最后一条文档的排序字段值更新 search_after,从而实现高效分页。
