分页搜索

参考:https://www.elastic.co/guide/en/elasticsearch/reference/7.8/paginate-search-results.html

默认情况下搜索API返回前10个匹配的文档。

To paginate through a larger set of results, you can use the search API’s size and from parameters. The size parameter is the number of matching documents to return. The from parameter is a zero-indexed offset from the beginning of the complete result set that indicates the document you want to start with.

可以通过size and from 两个参数对更多结果进行分页。 size表示返回的文档数, from表示结果的偏移量。

以下搜索表示结果偏移5个文档返回的文档数为20,也就是说从第6个文档开始返回20个文档。

GET /_search
{
  "from": 5,
  "size": 20,
  "query": {
    "match": {
      "user.id": "kimchy"
    }
  }
}

深度分页

默认情况下,不能使用fromsize参数分页浏览超过10,000个文档。 但是可以使用[index.max_result_window](https://www.elastic.co/guide/zh-CN/elasticsearch/reference/7.8/index-modules.html#index-max-result-window)索引来修改该限制。

深度分页或一次请求许多结果可能会导致搜索缓慢。 结果在返回之前先进行排序。 由于搜索请求通常跨越多个分片,因此每个分片会先生成独立的排序结果然后对这些单独的结果进行合并和排序,以确保总体排序顺序正确。

深度分页通常的解决方案是使用scrollsearch_after参数。

Scroll

当“搜索”请求返回单个“页面”结果时,scroll可用于从单个搜索请求中检索大量结果(甚至所有结果),其方式与传统数据库上的游标类似。

滚动并非用于实时用户请求,而是用于处理大量数据,例如将一个索引的内容重新索引为具有不同配置的新索引。

官方对PerlPythonJavaScript提供了帮助程序,以帮助您进行滚动搜索以及将文档从一个索引重新索引到另一个索引

滚动搜索对索引的修改仅会影响之后的搜索不会影响本次搜索

为了使用滚动,初始搜索请求应在查询字符串中指定scroll参数,该参数告诉Elasticsearch它应将“搜索上下文”保持活动状态的时间,例如?scroll = 1m

POST /my-index-000001/_search?scroll=1m
{
  "size": 100,
  "query": {
    "match": {
      "message": "foo"
    }
  }
}

上述请求的结果包括一个scroll_id,应将其作为下次滚动的参数以便检索下一批结果。scroll参数告知es为上下文搜索保持1分钟的存活时间,也就是需要在1分钟内进行下一次滚动,如果超出时间则搜索上下文将作为该scroll请求的一部分被释放。时间单位可以参考(https://www.elastic.co/guide/en/elasticsearch/reference/7.8/common-options.html#time-units)

POST /_search/scroll                                                               
{
  "scroll" : "1m",                                                                 
  "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==" 
}

使用size参数可以配置每批结果返回的最大匹配数。 每次滚动搜索都会返回下一批结果,直到没有剩余要返回的结果为止,即hits数组为空。

初始搜索请求和每个随后的滚动请求均返回一个scroll_id。 尽管scroll_id有可能会和上次返回的一致,但在任何情况下,都应使用最后一次接收到的scroll_id

如果请求中包含了聚合请求,则仅第一次的搜索返回会包含聚合结果。

滚动搜索内部的优化逻辑对使用_doc排序时速度会更快。 如果要遍历所有文档而不考虑顺序,这是最有效的选择:

GET /_search?scroll=1m
{
  "sort": [
    "_doc"
  ]
}

通常,后台合并过程通过将较小的片段合并在一起以创建新的较大的片段来优化索引。 一旦不再需要较小的,则将其删除。 在滚动过程中,此过程也是这样的,但是会防止旧片段被删除,因为它们仍在使用中。

使较旧的片段保持活动状态意味着需要更多的磁盘空间和文件句柄。 确保已将节点配置为具有足够的空闲空间。

此外,如果某个片段包含已删除或更新的文档,则搜索上下文必须跟踪该片段中的每个文档在初始搜索请求时是否处于活动状态。 如果索引上有正在进行的滚动搜索,这些索引会不断进行删除或更新,请确保节点具有足够的内存空间。

为了防止由于打开过多的滚动条而引起的问题,不允许用户打开超过特定限制的滚动条。 默认情况下,最多500个滚动搜索同时进行。可以使用search.max_open_scroll_context集群设置来更新此限制。

您可以使用[nodes stats API](https://www.elastic.co/guide/en/elasticsearch/reference/7.8/cluster-nodes-stats.html)检查scroll_current查看当前的有多少个滚动搜索:

GET /_nodes/stats/indices/search
清除滚动

Search context are automatically removed when the scroll timeout has been exceeded. However keeping scrolls open has a cost, as discussed in the previous section so scrolls should be explicitly cleared as soon as the scroll is not being used anymore using the clear-scroll API:

当超过“滚动”超时时间时,搜索上下文将自动删除。 但是,保持滚动搜索是有代价的,因此,一旦不再滚动搜索了就应该使用clear-scroll API,清除滚动:

DELETE /_search/scroll
{
  "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ=="
}

可以使用数组的形式对多个滚动搜索进行删除

DELETE /_search/scroll
{
  "scroll_id" : [
    "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==",
    "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAABFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAAAxZrUllkUVlCa1NqNmRMaUhiQlZkMWFBAAAAAAAAAAIWa1JZZFFZQmtTajZkTGlIYkJWZDFhQQAAAAAAAAAFFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAABBZrUllkUVlCa1NqNmRMaUhiQlZkMWFB"
  ]
}

或者 scroll_id可以在uri上作为字符串参数,多个scroll_id可以用逗号分隔传递对多个滚动搜索进行删除:

DELETE /_search/scroll/DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==,DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAABFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAAAxZrUllkUVlCa1NqNmRMaUhiQlZkMWFBAAAAAAAAAAIWa1JZZFFZQmtTajZkTGlIYkJWZDFhQQAAAAAAAAAFFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAABBZrUllkUVlCa1NqNmRMaUhiQlZkMWFB

也可以使用_all参数清除所有滚动搜索:

DELETE /_search/scroll/_all
切片滚动搜索

对于返回大量文档的滚动查询,可以将滚动分为多个切片,这些切片可以独立使用:

GET /my-index-000001/_search?scroll=1m
{
  "slice": {
    "id": 0,                      
    "max": 2                      
  },
  "query": {
    "match": {
      "message": "foo"
    }
  }
}
GET /my-index-000001/_search?scroll=1m
{
  "slice": {
    "id": 1,
    "max": 2
  },
  "query": {
    "match": {
      "message": "foo"
    }
  }
}

id表示片段编号

max表示最大切片数

第一个请求的结果返回了属于第一片(id:0)的文档,第二个请求的结果返回了属于第二片的文档。 由于切片的最大数量设置为2,所以这两个请求的结果和并在一起等效于不切片的滚动查询的结果。 默认情况下,首先使用_id字段对碎片进行拆分,然后使用以下公式在每个碎片上进行本地拆分:_

slice(doc)= floorMod(hashCode(doc._id),max)

例如,如果碎片数量 等于2,并且用户请求了4个切片,则将切片0和2分配给第一个分片,并将切片1和3分配给第二个分片。

每个滚动都是独立的,可以对多个滚动请求并行处理。

如果切片的数量大于分片的数量,则切片过滤器在第一次调用时非常慢,它的复杂度为O(N),内存成本等于每个切片N位,其中N是文档总数在碎片中。 几次调用后会将过滤结果缓存起来,随后的调用会加快,但应限制并行执行的切片查询的数量,以避免内存爆炸。

为了完全避免上述情况,可以使用另一个字段的“ doc_values”进行切片,但是用户必须确保该字段具有以下属性:

​ -该字段为数字。
​ -在该字段上启用了“ doc_values”
​ -每个文档应包含一个值。 如果文档的指定字段具有多个值,则使用第一个值。
​ -每个文档的值应在创建文档时设置一次,并且永远不要更新。 这样可以确保每个切片得到确定的结果。
​ -该字段的基数应该很高。 这样可以确保每个切片获得的文档数量大致相同。

GET /my-index-000001/_search?scroll=1m
{
  "slice": {
    "field": "@timestamp",
    "id": 0,
    "max": 10
  },
  "query": {
    "match": {
      "message": "foo"
    }
  }
}

对于仅追加基于时间的索引,可以安全地使用“时间戳”字段。

默认情况下,每个滚动所允许的最大切片数限制为1024。您可以更新index.max_slices_per_scroll索引设置以绕过此限制。

Search after

可以使用fromsize进行结果分页,但是当达到深度分页时,成本变得过高。 默认为10,000的index.max_result_window是一种安全措施,搜索请求占用的堆内存和时间与from+size成正比。 建议使用滚动搜索,但有效的深度滚动对资源的占用也是大量的,不建议将其用于实时搜索。 search_after参数通过提供实时游标来解决此问题。使用上一页的结果来帮助下一页检索。

假设检索第一页的查询如下所示:

GET my-index-000001/_search
{
  "size": 10,
  "query": {
    "match" : {
      "message" : "foo"
    }
  },
  "sort": [
    {"@timestamp": "asc"},
    {"tie_breaker_id": "asc"}      
  ]
}

每个文档需要有一个字段具有唯一值作为排序依据。否则,具有相同排序值的文档的排序顺序将不确定,并可能导致结果丢失或重复。 _id字段是每个文档都有唯一的值,但不建议使用它直接作为排序依据。请注意,search_after会寻找第一个与提供的值完全或部分相匹配的文档。因此,举例如果文档的排序字段的值为654323,而您的search_after654,它将仍然与该文档匹配并返回在文档之后找到的结果。因此对其进行排序需要在内存中加载大量数据。相反,建议复制_id字段到另一个具有 doc_values字段的文档上并使用此新字段作为排序依据。

上述请求的结果包括每个文档的“排序值”数组。 这些“排序值”可以与“ search_after”参数一起使用,以在结果列表中的任何文档“之后”开始返回结果。 例如,我们可以使用上一个文档的“排序值”并将其传递给“ search_after”以检索结果的下一页:

GET my-index-000001/_search
{
  "size": 10,
  "query": {
    "match" : {
      "message" : "foo"
    }
  },
  "search_after": [1463538857, "654323"],
  "sort": [
    {"@timestamp": "asc"},
    {"tie_breaker_id": "asc"}
  ]
}

使用search_after时,必须将参数from设置为0(或-1)。search_after不是自由跳转到随机页面而是并行滚动许多查询的解决方案。 它与scroll 非常相似,但与之不同的是,search_after参数是无状态的,它始终针对最新版本的搜索器进行解析。 因此,排序顺序可能会在执行程中更改,具体取决于索引的更新和删除。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值