结果分页
在 UDB-SX 中,您可以使用以下方法对搜索结果进行分页:
from 和 size 参数
from 和 size 参数用于逐页返回结果。
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 中指定 from 和 size 参数:
GET shakespeare/_search?from=0&size=10
如果只指定 size 参数,from 参数默认为 0。
查询结果中较深页码会对性能产生显著影响,因此 UDB-SX 将此方法限制为 10,000 条结果。
from 和 size 参数是无状态的,因此结果基于最新可用数据。
这可能导致分页不一致。
例如,假设用户停留在第一页结果,然后导航到第二页。在此期间,一个与用户搜索相关的新文档被索引,并出现在第一页。在这种情况下,第一页的最后一条结果会被推到第二页,用户将看到重复的结果(即第一页和第二页都显示该最后一条结果)。
如需实现一致的分页,请使用 scroll 操作。scroll 操作会在一定时间内保持搜索上下文开放。在此期间,任何数据更改都不会影响结果。
滚动搜索
from 和 size 参数允许您对搜索结果进行分页,但每次最多只能处理 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 中首选的分页方法,尤其适用于深度分页。它绕过了所有其他方法的限制,因为它操作的是一个在时间上冻结的数据集,它不受查询限制,并且支持一致的前后分页。要了解更多信息,请参阅时间点搜索。