结果分页

在 UDB-SX 中,您可以使用以下方法对搜索结果进行分页:

  1. fromsize 参数

  2. 滚动搜索 操作

  3. search_after 参数

  4. 使用 search_after 的时间点搜索

fromsize 参数

fromsize 参数用于逐页返回结果。

from 参数指定您希望从哪个文档编号开始显示结果。size 参数指定您希望显示的结果数量。两者结合,可以返回搜索结果的子集。

例如,如果 size 值为 10,from 值为 0,您将看到前 10 条结果。如果将 from 值改为 10,您将看到接下来的 10 条结果(因为结果从零开始编号)。所以,如果您想从第 11 条结果开始查看,from 必须为 10。

GET shakespeare/_search
{
  "from": 0,
  "size": 10,
  "query": {
    "match": {
      "play_name": "Hamlet"
    }
  }
}

可以使用以下公式计算相对于页码的 from 参数:

from = size * (page_number - 1)

每次用户选择查看结果的下一页时,您的应用程序都需要运行相同的搜索查询,并将 from 值递增。

您也可以在搜索 URI 中指定 fromsize 参数:

GET shakespeare/_search?from=0&size=10

如果只指定 size 参数,from 参数默认为 0。

查询结果中较深页码会对性能产生显著影响,因此 UDB-SX 将此方法限制为 10,000 条结果。

fromsize 参数是无状态的,因此结果基于最新可用数据。 这可能导致分页不一致。 例如,假设用户停留在第一页结果,然后导航到第二页。在此期间,一个与用户搜索相关的新文档被索引,并出现在第一页。在这种情况下,第一页的最后一条结果会被推到第二页,用户将看到重复的结果(即第一页和第二页都显示该最后一条结果)。

如需实现一致的分页,请使用 scroll 操作。scroll 操作会在一定时间内保持搜索上下文开放。在此期间,任何数据更改都不会影响结果。

滚动搜索

fromsize 参数允许您对搜索结果进行分页,但每次最多只能处理 10,000 条结果。

如果您需要从(例如)机器学习作业请求超过 1 PB 的数据量,请改用 scroll 操作。scroll 操作允许您请求无限数量的结果。

要使用滚动操作,请在请求头中添加一个 scroll 参数,并附带一个搜索上下文,告知 UDB-SX 您需要保持滚动多长时间。此搜索上下文需要足够长以处理单批结果。

要设置每批返回的结果数量,请使用 size 参数:

GET shakespeare/_search?scroll=10m
{
  "size": 10000
}

UDB-SX 会缓存结果并返回一个滚动 ID,您可以使用它分批访问结果:

"_scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAAUWdmpUZDhnRFBUcWFtV21nMmFwUGJEQQ=="

将此滚动 ID 传递给 scroll 操作以获取下一批结果:

GET _search/scroll
{
  "scroll": "10m",
  "scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAAUWdmpUZDhnRFBUcWFtV21nMmFwUGJEQQ=="
}

使用此滚动 ID,只要搜索上下文仍然开放,您就可以分批获取 10,000 条结果。通常,滚动 ID 在请求之间不会改变,但它可能改变,因此请确保始终使用最新的滚动 ID。如果您未在设定的搜索上下文时间内发送下一个滚动请求,scroll 操作将不会返回任何结果。

如果您预期有数十亿条结果,请使用切片滚动。切片允许您对同一请求执行多个滚动操作,但并行进行。 为滚动设置 ID 和最大切片数:

GET shakespeare/_search?scroll=10m
{
  "slice": {
    "id": 0,
    "max": 10
  },
  "query": {
    "match_all": {}
  }
}

使用单个滚动 ID,您将获得 10 条结果。 最多可以有 10 个 ID。 使用 ID 等于 1 执行相同的命令:

GET shakespeare/_search?scroll=10m
{
  "slice": {
    "id": 1,
    "max": 10
  },
  "query": {
    "match_all": {}
  }
}

滚动完成后请关闭搜索上下文,因为它在超时前会持续消耗计算资源:

DELETE _search/scroll/DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAAcWdmpUZDhnRFBUcWFtV21nMmFwUGJEQQ==

示例响应

{
  "succeeded": true,
  "num_freed": 1
}

使用以下请求关闭所有开放的滚动上下文:

DELETE _search/scroll/_all

scroll 操作对应于一个特定的时间戳。它不考虑该时间戳之后添加的文档作为潜在结果。

由于开放的搜索上下文会消耗大量内存,我们建议不要对不需要保持搜索上下文开放的用户频繁查询使用 scroll 操作。相反,对于用户查询的滚动响应,请使用 sort 参数配合 search_after 参数。

search_after 参数

search_after 参数提供了一个实时游标,它利用上一页的结果来获取下一页的结果。它与 scroll 操作类似,都旨在并行滚动多个查询。只有在应用了排序时才能使用 search_after

例如,以下查询按台词编号和 ID 对戏剧《哈姆雷特》的所有台词进行排序,并检索前三个结果:

GET shakespeare/_search
{
  "size": 3,
  "query": {
    "match": {
      "play_name": "Hamlet"
    }
  },
  "sort": [
    { "speech_number": "asc" },
    { "_id": "asc" } 
  ]
}

响应包含每个文档的 sort 值数组:

{
  "took" : 7,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 4244,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [
      {
        "_index" : "shakespeare",
        "_id" : "32435",
        "_score" : null,
        "_source" : {
          "type" : "line",
          "line_id" : 32436,
          "play_name" : "Hamlet",
          "speech_number" : 1,
          "line_number" : "1.1.1",
          "speaker" : "BERNARDO",
          "text_entry" : "Whos there?"
        },
        "sort" : [
          1,
          "32435"
        ]
      },
      {
        "_index" : "shakespeare",
        "_id" : "32634",
        "_score" : null,
        "_source" : {
          "type" : "line",
          "line_id" : 32635,
          "play_name" : "Hamlet",
          "speech_number" : 1,
          "line_number" : "1.2.1",
          "speaker" : "KING CLAUDIUS",
          "text_entry" : "Though yet of Hamlet our dear brothers death"
        },
        "sort" : [
          1,
          "32634"
        ]
      },
      {
        "_index" : "shakespeare",
        "_id" : "32635",
        "_score" : null,
        "_source" : {
          "type" : "line",
          "line_id" : 32636,
          "play_name" : "Hamlet",
          "speech_number" : 1,
          "line_number" : "1.2.2",
          "speaker" : "KING CLAUDIUS",
          "text_entry" : "The memory be green, and that it us befitted"
        },
        "sort" : [
          1,
          "32635"
        ]
      }
    ]
  }
}

您可以使用最后一个结果的 sort 值,通过 search_after 参数来检索下一个结果:

GET shakespeare/_search
{
  "size": 10,
  "query": {
    "match": {
      "play_name": "Hamlet"
    }
  },
  "search_after": [ 1, "32635"],
  "sort": [
    { "speech_number": "asc" },
    { "_id": "asc" } 
  ]
}

scroll 操作不同,search_after 参数是无状态的,因此文档顺序可能因文档被索引或删除而改变。

使用 search_after 的时间点搜索

使用 search_after 的时间点搜索是 UDB-SX 中首选的分页方法,尤其适用于深度分页。它绕过了所有其他方法的限制,因为它操作的是一个在时间上冻结的数据集,它不受查询限制,并且支持一致的前后分页。要了解更多信息,请参阅时间点搜索