分页是任何搜索引擎的重要功能,使用户能够高效地浏览大量结果集。Manticore 搜索提供了几种强大的分页方法,每种方法都有其优缺点。本文探讨了 Manticore 搜索中可用的不同分页选项,并提供实用的 SQL 示例,帮助您实现最适合您用例的解决方案。
目录
分页简介
处理大型数据集时,一次性返回所有匹配的结果是不可行的。分页通过将结果划分为可管理的块或“页面”来解决这个问题。Manticore 搜索提供了多种分页方法,以适应从简单列表导航到无限滚动界面的不同用例。
在本文中,我们将探讨三种主要的分页方法:
我们还将讨论它们通过 SQL 和 HTTP JSON 接口的实现、性能考虑以及实际应用。
设置测试环境
在深入分页示例之前,让我们借助 Manticore Load 工具创建一个带有示例数据的测试环境。此工具使生成和加载测试数据以进行基准测试和实验变得简单:
# 创建一个产品表并用 10,000 条包含“智能手机”标题的记录填充它
manticore-load --quiet \
--batch-size=1000 \
--threads=4 \
--total=10000 \
--drop \
--init="CREATE TABLE products(title text, description text, price int, category string, rating float, stock int)" \
--load="INSERT INTO products(id, title, description, price, category, rating, stock) VALUES(<increment>, '<text/3/5> smartphone', '<text/20/50>', <int/10/1000>, '<string/5/10>', <float/1/5>, <int/0/100>)"
输出:
有了这个测试环境,我们现在可以探索不同的分页方法。
传统的基于偏移的分页
Manticore 搜索中最直接的分页方法使用 LIMIT
子句与偏移和计数参数。这种方法对任何与 SQL 数据库打过交道的人都很熟悉。
基本语法
SELECT ... FROM ... [LIMIT [offset,] row_count]
SELECT ... FROM ... [LIMIT row_count][ OFFSET offset]
示例
-- 返回前 5 个结果(页面 1)
SELECT id, title, price, weight() FROM products
WHERE MATCH('smartphone')
ORDER BY weight() DESC, price ASC, id ASC
LIMIT 5;
输出:
+------+----------------------------------------------+-------+----------+
| id | title | price | weight() |
+------+----------------------------------------------+-------+----------+
| 1179 | 弱的轮辐? 落下。智能手机 | 10 | 1272 |
| 1388 | 3; 她的日子。智能手机 | 10 | 1272 |
| 1636 | 表现得很糟糕的冬季清理。智能手机 | 10 | 1272 |
| 5628 | 愚蠢的因低而被听到。智能手机 | 10 | 1272 |
| 8561 | 左边的发送。不经常,尝试躺下。智能手机 | 10 | 1272 |
+------+----------------------------------------------+-------+----------+
-- 返回结果 6-10(页面 2)
SELECT id, title, price, weight() FROM products
WHERE MATCH('smartphone')
ORDER BY weight() DESC, price ASC, id ASC
LIMIT 5, 5;
输出:
+------+----------------------------------------------+-------+----------+
| id | title | price | weight() |
+------+----------------------------------------------+-------+----------+
| 246 | 柔软的技术性袭来。智能手机 | 11 | 1272 |
| 1105 | 脏污的东东!暂时被称为。智能手机 | 11 | 1272 |
| 3293 | 立刻厚重的跑来伤害。智能手机 | 11 | 1272 |
| 3736 | 她错误的本土工作。智能手机 | 11 | 1272 |
| 6978 | 陈旧的响亮被动的甜美清洁。智能手机 | 11 | 1272 |
+------+----------------------------------------------+-------+----------+
-- 使用单独的 LIMIT 和 OFFSET 的替代语法
SELECT id, title, price, weight() FROM products
WHERE MATCH('smartphone')
ORDER BY weight() DESC, price ASC, id ASC
LIMIT 5 OFFSET 10;
-- 返回结果 11-15(页面 3)
输出:
+------+----------------------------------------+-------+----------+
| id | title | price | weight() |
+------+----------------------------------------+-------+----------+
| 7318 | 她的支出。智能手机 | 11 | 1272 |
| 8436 | 他的? 感觉工作。不快。智能手机 | 11 | 1272 |
| 9699 | 感觉花费有趣。智能手机 | 11 | 1272 |
| 2326 | 安静的湿画发现。智能手机 | 12 | 1272 |
| 4171 | 设置也许新的发送很快。智能手机 | 12 | 1272 |
+------+--------------------------------------------+-------+----------+
这个方法简单易行,适合在初始结果页面之间导航。然而,由于Manticore内部处理查询的方式,对于深分页效率较低。有关这些限制的详细信息,请参见 第8节:限制和性能考虑 。
结果集窗口和max_matches
默认情况下,Manticore Search将可以返回的匹配数限制为1000。如果您尝试超出此限制进行分页,则查询将导致错误。这个限制可以使用max_matches
选项进行调整。
理解max_matches
max_matches
选项控制Manticore在搜索时将在RAM中保留多少匹配项。它旨在限制每个查询的内存使用。默认值为1000,对于大多数常见搜索场景来说已足够,但在需要访问更深的结果页面时,您可以增加此值。
-- 增加max_matches以允许更深的分页(结果1001-1005)
SELECT id, title, price FROM products
WHERE MATCH('智能手机')
ORDER BY weight() DESC, price ASC, id ASC
LIMIT 1000, 5
OPTION max_matches=1500;
输出:
+------+-------------------------------------------+-------+
| id | title | price |
+------+-------------------------------------------+-------+
| 3550 | 开放出售迟到宽阔。智能手机 | 111 |
| 4896 | 增长。写道非常饥饿。智能手机 | 111 |
| 647 | 去见过有。智能手机 | 112 |
| 883 | 保持失去;有想法。智能手机 | 112 |
| 1774 | 得到!开始生病。走了,狂野。智能手机 | 112 |
+------+-------------------------------------------+-------+
-- 对于非常深的分页(结果5000-5005)
SELECT id, title, price FROM products
WHERE MATCH('智能手机')
ORDER BY weight() DESC, price ASC, id ASC
LIMIT 5000, 5
OPTION max_matches=5500;
输出:
+------+------------------------------------------------+-------+
| id | title | price |
+------+------------------------------------------------+-------+
| 4894 | 微妙地?内部花费傍晚。智能手机 | 507 |
| 5203 | 糟糕;艺术性从每年。智能手机 | 507 |
| 7446 | 空的可能聪明!全球的。智能手机 | 507 |
| 8053 | 在!丰富的每日不规则。智能手机 | 507 |
| 8055 | 短冷秋;保持身体健康。智能手机 | 507 |
+------+------------------------------------------------+-------+
请记住,增加max_matches
会带来内存成本。每个匹配都会消耗内存,因此将此值设置得过高可能会影响服务器性能,尤其是在负载较重的情况下。有关更具内存效率的深分页替代方法,请参见
基于滚动的分页
。
基于滚动的分页
基于滚动的分页提供了一种高效的方法来在大型结果集中导航。与传统的基于偏移量的分页不同,它使用一个令牌来跟踪当前在结果集中的位置,这有助于有效地在结果的连续页面之间导航。
工作原理
- 执行带有排序标准的初始查询(ORDER BY中必须包含
id
) - 检索一个滚动令牌,它封装当前的位置
- 在后续查询中使用此令牌以获取下一批结果
示例
-- 带有排序标准的初始查询(ORDER BY中必须包含id)
SELECT id, title, price, weight() FROM products
WHERE MATCH('智能手机')
ORDER BY weight() DESC, price ASC, id ASC
LIMIT 5; SHOW SCROLL\G
输出:
+------+--------------------------------------------+-------+----------+
| id | title | price | weight() |
+------+--------------------------------------------+-------+----------+
| 1179 | 微弱说话?跌落。智能手机 | 10 | 1272 |
| 1388 | 3;她的日子。智能手机 | 10 | 1272 |
| 1636 | 积极不好的冬季清洁。智能手机 | 10 | 1272 |
| 5628 | 愚蠢的从去的听见低。智能手机 | 10 | 1272 |
| 8561 | 左发送。不频繁,尝试放置。智能手机 | 10 | 1272 |
+------+--------------------------------------------+-------+----------+
*************************** 1. 行 ***************************
scroll_token: eyJvcmRlcl9ieV9zdHIiOiJ3ZWlnaHQoKSBERVNDLCBwcmljZSBBU0MsIGlkIEFTQyIsIm9yZGVyX2J5IjpbeyJhdHRyIjoid2VpZ2h0KCkiLCJkZXNjIjp0cnVlLCJ2YWx1ZSI6MTI3MiwidHlwZSI6ImludCJ9LHsiYXR0ciI6InByaWNlIiwiZGVzYyI6ZmFsc2UsInZhbHVlIjoxMCwidHlwZSI6ImludCJ9LHsiYXR0ciI6ImlkIiwiZGVzYyI6ZmFsc2UsInZhbHVlIjo4NTYxLCJ0eXBlIjoiaW50In1dfQ==
-- 使用滚动令牌的后续分页查询
SELECT id, title, price, weight() FROM products
WHERE MATCH('智能手机')
ORDER BY weight() DESC, price ASC, id ASC
LIMIT 5
OPTION scroll='eyJvcmRlcl9ieV9zdHIiOiJ3ZWlnaHQoKSBERVNDLCBwcmljZSBBU0MsIGlkIEFTQyIsIm9yZGVyX2J5IjpbeyJhdHRyIjoid2VpZ2h0KCkiLCJkZXNjIjp0cnVlLCJ2YWx1ZSI6MTI3MywidHlwZSI6ImludCJ9LHsiYXR0ciI6InByaWNlIiwiZGVzYyI6ZmFsc2UsInZhbHVlIjo1MiwidHlwZSI6ImludCJ9LHsiYXR0ciI6ImlkIiwiZGVzYyI6ZmFsc2UsInZhbHVlIjo0NCwidHlwZSI6ImludCJ9XX0=';
输出:
+------+--------------------------------------------+-------+----------+
| id | title | price | weight() |
+------+--------------------------------------------+-------+----------+
| 1179 | 微弱说话?跌落。智能手机 | 10 | 1272 |
| 1388 | 3;她的日子。智能手机 | 10 | 1272 |
| 1636 | 积极不好的冬季清洁。智能手机 | 10 | 1272 |
| 5628 | 愚蠢的从曾听到低。智能手机 | 10 | 1272 |
| 8561 | 左发送。偶尔,尝试躺下。智能手机 | 10 | 1272 |
+------+----------------------------------------------+-------+----------+
基于滚动的分页特别适合在Web应用程序中实现“加载更多”或无限滚动功能。有关完整的实现示例,请参见 实际使用案例 。
通过HTTP JSON进行分页
除了SQL,Manticore Search还提供通过HTTP JSON进行分页。这对于Web应用程序或微服务架构特别有用。传统的基于偏移量的分页和基于滚动的分页都可以通过JSON API支持。
通过HTTP JSON进行传统的基于偏移量的分页
HTTP JSON API使用offset
和limit
参数来控制分页。这相当于SQL LIMIT offset, count
语法。
基本语法
{
"table": "table_name",
"query": { "match": { ... } },
"limit": number_of_results,
"offset": starting_position
}
示例
# 第一页(结果1-5)
curl -s 0:9308/search -d '{
"table": "products",
"query": { "match": { "*": "智能手机" } },
"limit": 5,
"offset": 0,
"sort": [
{ "_score": "desc" },
{ "price": "asc" },
{ "id": "asc" }
]
}'|jq .
输出:
{
"took": 0,
"timed_out": false,
"hits": {
"total": 10000,
"total_relation": "eq",
"hits": [
{
"_id": 1179,
"_score": 1272,
"_source": {
"title": "弱的发言?跌倒。智能手机",
"description": "从未长时间愤怒地被询问关闭!冷的历史上常常情绪化地被带走;社会上被问到经常认为无处可去?仔细?科学上很少。",
"price": 10,
"category": "hkadwpwwk",
"rating": 2.300000,
"stock": 63
}
},
// ... 更多结果 ...
]
}
}
# 第二页(结果6-10)
curl -s 0:9308/search -d '{
"table": "products",
"query": { "match": { "*": "智能手机" } },
"limit": 5,
"offset": 5,
"sort": [
{ "_score": "desc" },
{ "price": "asc" },
{ "id": "asc" }
]
}' | jq .
输出:
{
"took": 0,
"timed_out": false,
"hits": {
"total": 10000,
"total_relation": "eq",
"hits": [
{
"_id": 246,
"_score": 1272,
"_source": {
"title": "软的技术性被采纳。智能手机",
"description": "有时这个空短暂通常!被发送。在本地善良!迅速可能偶尔文化上柔和地安静逐渐做到好;里面死寂响亮。",
"price": 11,
"category": "wuiymrjdp",
"rating": 1.600000,
"stock": 87
}
},
// ... 更多结果 ...
]
}
}
替代语法
HTTP JSON API还支持使用from
和size
的替代语法,而不是offset
和limit
:
{
"table": "table_name",
"query": { "match": { ... } },
"size": number_of_results,
"from": starting_position
}
这与之前的格式等效,但遵循了Elasticsearch的约定,对某些用户可能更加熟悉。
通过HTTP JSON进行基于滚动的分页
HTTP JSON API还支持基于滚动的分页,以实现更高效的深度分页。这需要设置正确的排序标准并使用滚动令牌。
初始查询
curl -s 0:9308/search -d '{
"table": "products",
"query": { "match": { "*": "智能手机" } },
"limit": 5,
"track_scores": true,
"sort": [
{ "_score": "desc" },
{ "id": "asc" }
],
"options": {
"scroll": true
}
}' | jq .
输出:
{
"took": 0,
"timed_out": false,
"hits": {
"total": 10000,
"total_relation": "eq",
"hits": [
{
"_id": 1,
"_score": 1272,
"_source": {
"title": "不正确的沉重柔软。智能手机",
"description": "仔细的晚座;放置绘制恐惧;几乎给了1000这个在理解上总是向东假冒,乏味国际发送。在冷;热。",
"price": 540,
"category": "ogqejby",
"rating": 2.800000,
"stock": 52
}
},
// ... 更多结果 ...
]
},
"scroll": "eyJvcmRlcl9ieV9zdHIiOiJAd2VpZ2h0IGRlc2MsIGlkIGFzYyIsIm9yZGVyX2J5IjpbeyJhdHRyIjoid2VpZ2h0KCkiLCJkZXNjIjp0cnVlLCJ2YWx1ZSI6MTI3MiwidHlwZSI6ImludCJ9LHsiYXR0ciI6ImlkIiwiZGVzYyI6ZmFsc2UsInZhbHVlIjo1LCJ0eXBlIjoiaW50In1dfQ=="
}
请注意响应中的scroll
值,这是一个Base64编码的令牌,表示您在结果集中的当前位置。
随后查询
curl -s 0:9308/search -d '{
"table": "products",
"query": { "match": { "*": "智能手机" } },
"limit": 5,
"track_scores": true,
"options": {
"scroll": "eyJvcmRlcl9ieV9zdHIiOiJAd2VpZ2h0IGRlc2MsIGlkIGFzYyIsIm9yZGVyX2J5IjpbeyJhdHRyIjoid2VpZ2h0KCkiLCJkZXNjIjp0cnVlLCJ2YWx1ZSI6MTI3MiwidHlwZSI6ImludCJ9LHsiYXR0ciI6ImlkIiwiZGVzYyI6ZmFsc2UsInZhbHVlIjo1LCJ0eXBlIjoiaW50In1dfQ=="
}
}' | jq .
输出:
{
"took": 0,
"timed_out": false,
"hits": {
"total": 9995,
"total_relation": "eq",
"hits": [
{
"_id": 6,
"_score": 1272,
"_source": {
"title": "消极的!它变成。智能手机",
"description": "安静冷漠的夜晚,贫穷的小时薄的神经,非正式工作,100 50特定严肃地带下开始!富有响亮。",
"price": 602,
"category": "hjpwt",
"rating": 2.500000,
"stock": 47
}
},
// ... 更多结果 ...
]
},
"scroll": "eyJvcmRlcl9ieV9zdHIiOiJAd2VpZ2h0IGRlc2MsIGlkIGFzYyIsIm9yZGVyX2J5IjpbeyJhdHRyIjoid2VpZ2h0KCkiLCJkZXNjIjp0cnVlLCJ2YWx1ZSI6MTI3MiwidHlwZSI6ImludCJ9LHsiYXR0ciI6ImlkIiwiZGVzYyI6ZmFsc2UsInZhbHVlIjoxMCwidHlwZSI6ImludCJ9XX0="
}
每个响应包括下一个页面的新滚动令牌,允许继续分页。
HTTP JSON分页的最佳实践
- 包含排序标准:为了可预测的分页结果,始终包括明确的排序,例如:
"sort": [ { "_score": "desc" }, { "price": "asc" }, { "id": "asc" } ]
- 正确处理滚动令牌:存储每个响应的滚动令牌,并在下一个请求中使用它。
- 使用适当的错误处理:检查错误响应,特别是在深度分页时。
- 考虑响应大小:通过仅选择所需字段来控制返回的数据量,使用
_source
参数:"_source": ["title", "price", "rating"]
- 监控性能:对于非常大的结果集,监控服务器性能,考虑使用基于滚动的分页。
HTTP JSON分页方法遵循与其SQL对应物相同的原则(在 第3节 和 第5节 中描述),但使用JSON语法。这使得它们非常适合现代Web应用程序和REST API。
分页的排序最佳实践
正确的排序对于有效的分页至关重要,特别是对于基于滚动的分页。以下是关键考虑因素:
1. 包含唯一标识符
为了确保一致的分页结果,始终在ORDER BY子句中包含唯一标识符(通常是id
字段)。这确保即使多个文档在其他排序标准下具有相同值,分页仍会产生一致的结果。
-- 好:包含id作为平局打破者
SELECT * FROM products WHERE MATCH('smartphone') ORDER BY weight() DESC, price ASC, id ASC;
-- 不好:没有唯一标识符,可能导致不一致的分页
SELECT * FROM products WHERE MATCH('smartphone') ORDER BY price ASC;
2. 包含weight()进行全文搜索
在使用MATCH()
进行查询时,始终在排序标准中包含weight()
,以确保最相关的结果首先出现:
-- 好:包含weight()进行相关性排序
SELECT * FROM products WHERE MATCH('smartphone') ORDER BY weight() DESC, price ASC, id ASC;
-- 较差:缺少相关性排序
SELECT * FROM products WHERE MATCH('smartphone') ORDER BY price ASC, id ASC;
3. 保持排序顺序一致性
在使用传统的基于偏移量的分页时,始终在所有分页查询中保持相同的排序顺序。在页面之间更改排序顺序可能会导致结果错过或重复。
-- 第一页(正确)
SELECT * FROM products WHERE MATCH('smartphone') ORDER BY weight() DESC, price ASC, id ASC LIMIT 10;
-- 第二页(正确 - 相同的排序顺序)
SELECT * FROM products WHERE MATCH('smartphone') ORDER BY weight() DESC, price ASC, id ASC LIMIT 10, 10;
-- 第二页(不正确 - 不同的排序顺序)
SELECT * FROM products WHERE MATCH('smartphone') ORDER BY rating DESC, id ASC LIMIT 10, 10;
4. 使用确定性排序
对于基于滚动的分页,始终使用确定性的排序标准。应避免使用非确定性函数如RAND()
,因为它们会在查询之间产生不一致的结果。
-- 好:确定性排序
SELECT * FROM products WHERE MATCH('smartphone') ORDER BY weight() DESC, price ASC, id ASC;
-- 不好:非确定性排序,将打破滚动分页
SELECT * FROM products WHERE MATCH('smartphone') ORDER BY RAND();
5. 使用ID的排序方向
在使用id
字段作为排序的平局打破者时,可以使用ASC或DESC顺序,但必须一致应用:
-- 这两种方法对于滚动分页都是有效的
SELECT * FROM products WHERE MATCH('smartphone') ORDER BY weight() DESC, price ASC, id ASC;
SELECT * FROM products WHERE MATCH('smartphone') ORDER BY weight() DESC, price ASC, id DESC;
6. 优化排序性能
为了更好的排序性能,考虑使用sort_method
选项:
-- 对于预排序的数据(例如,通过id)
SELECT * FROM products WHERE MATCH('smartphone') ORDER BY id ASC
LIMIT 5 OPTION sort_method=kbuffer;
-- 对于其他排序标准
SELECT * FROM products WHERE MATCH('smartphone') ORDER BY weight() DESC, price ASC, id ASC
LIMIT 5 OPTION sort_method=pq;
您选择的排序方法可以显著影响分页性能和一致性。例如,确定性排序对基于滚动的分页至关重要,而包含唯一标识符对于所有分页方法中的一致结果至关重要。有关排序如何影响性能的更多细节,请参见 限制和性能考虑 。
限制和性能考虑
了解不同分页方法的限制将帮助您为特定用例选择正确的方法。
基于偏移量的分页限制
- 大型偏移量时性能下降:随着偏移量值的增加,查询性能下降。这是因为Manticore仍然需要处理所有记录,直到偏移量值,然后返回请求的页面。
- max_matches约束:默认情况下,您只能通过前1000个结果进行分页。要超过此限制,您必须增加
max_matches
,这会增加内存使用。 - 内存使用:较大的
max_matches
值每个查询需要更多的RAM,这可能在负载较重时影响服务器性能。 - 不保证一致的结果:如果在页面请求之间添加或删除了文档,您可能会看到重复或错过结果。
对于深度分页场景或实现“加载更多”功能,请考虑使用 Scroll-Based Pagination 中描述的内容,见 第 5 节 。
基于滚动的分页限制
- 需要排序条件中的 ID:您必须在 ORDER BY 子句中包含
id
字段,这可能与您期望的排序逻辑不 always 一致。 - 令牌管理:您需要在请求之间存储和管理滚动令牌,这给您的应用程序增加了复杂性。
- 无一致性保证:基于滚动的分页不保证时间点一致性。如果在请求之间添加、删除或修改文档,结果仍可能受到影响。
- 无随机访问:与基于偏移量的分页不同,您不能直接跳转到特定页面;必须按顺序导航。
带分页的多面搜索
多面搜索允许用户使用多个维度对搜索结果进行过滤和导航。与分页结合时,理解分页如何影响面结果很重要。
-- 带分页的多面搜索(第一页)
SELECT id, title, price, weight()
FROM products
WHERE MATCH('smartphone')
ORDER BY weight() DESC, price ASC, id ASC
LIMIT 0, 5
FACET category ORDER BY COUNT(*) DESC;
输出:
+------+----------------------------------------------+-------+----------+
| id | title | price | weight() |
+------+----------------------------------------------+-------+----------+
| 1179 | 弱轮辐?坠落。智能手机 | 10 | 1272 |
| 1388 | 3;她的日子。智能手机 | 10 | 1272 |
| 1636 | 确实糟糕的冬天清洁。智能手机 | 10 | 1272 |
| 5628 | 愚蠢的从去过低。智能手机 | 10 | 1272 |
| 8561 | 左边发送。很少,尝试躺下。智能手机 | 10 | 1272 |
+------+----------------------------------------------+-------+----------+
+-----------+----------+
| category | count(*) |
+-----------+----------+
| ogqejby | 1 |
| unmfujgqr | 1 |
| ttefm | 1 |
| hceihdy | 1 |
| sicjr | 1 |
| hjpwt | 1 |
| tvfqyj | 1 |
| mvdjhbexo | 1 |
| scayuo | 1 |
| esmlh | 1 |
| fvbhplj | 1 |
| lcphmiqmv | 1 |
| lnjfhb | 1 |
| qexfdulub | 1 |
| tbswa | 1 |
| eekarf | 1 |
| airjuod | 1 |
| ozkbuvgj | 1 |
| yafbhr | 1 |
| duccr | 1 |
+-----------+----------+
-- 带分页的多面搜索(第二页)
SELECT id, title, price, weight()
FROM products
WHERE MATCH('smartphone')
ORDER BY weight() DESC, price ASC, id ASC
LIMIT 5, 5
FACET category ORDER BY COUNT(*) DESC;
输出:
+------+-----------------------------------------------------------+-------+----------+
| id | title | price | weight() |
+------+-----------------------------------------------------------+-------+----------+
| 246 | 软性技术采取。智能手机 | 11 | 1272 |
| 1105 | 脏的上是!临时叫。智能手机 | 11 | 1272 |
| 3293 | 立刻变厚了那伤。智能手机 | 11 | 1272 |
| 3736 | 工作她错过当地。智能手机 | 11 | 1272 |
| 6978 | 陈旧响亮被动甜美清洁。智能手机 | 11 | 1272 |
+------+-----------------------------------------------------------+-------+----------+
+-----------+----------+
| category | count(*) |
+-----------+----------+
| ogqejby | 1 |
| unmfujgqr | 1 |
| ttefm | 1 |
| hceihdy | 1 |
| sicjr | 1 |
| hjpwt | 1 |
| tvfqyj | 1 |
| mvdjhbexo | 1 |
| scayuo | 1 |
| esmlh | 1 |
| fvbhplj | 1 |
| lcphmiqmv | 1 |
| lnjfhb | 1 |
| qexfdulub | 1 |
| tbswa | 1 |
| eekarf | 1 |
| airjuod | 1 |
| ozkbuvgj | 1 |
| yafbhr | 1 |
| duccr | 1 |
+-----------+----------+
重要说明: 面结果是基于与查询匹配的整个结果集计算的,而不仅仅是分页部分。这意味着无论您查看哪个页面,面计数都代表整个匹配文档的分布。LIMIT 子句仅影响主要结果集中的返回文档,而不影响面计算。如您所见,上述示例中,两个页面的面结果是相同的,确认它们是从整个结果集计算的。
面结果中的分页
除分页主要搜索结果外,Manticore Search 还允许您对面结果本身进行分页。这在处理大量面值时尤其有用,因为显示所有面值可能不实际。
您可以在 FACET
语句中使用 LIMIT
子句来控制返回的面值数量。
-- 仅获取按计数排序的前 5 个类别
SELECT id, title FROM products
WHERE MATCH('smartphone')
LIMIT 3
FACET category ORDER BY COUNT(*) DESC, category asc LIMIT 0,5;
输出:
+------+---------------------------------------------+
| id | title |
+------+---------------------------------------------+
| 1 | 错误地沉重柔软。智能手机 |
| 2 | 知道的制造害怕愚蠢。智能手机 |
| 3 | 糟糕的春天傍晚驱动年轻。智能手机 |
+------+---------------------------------------------+
+------------+----------+
| category | count(*) |
+------------+----------+
| aaaabzwfzn | 1 |
| aabswb | 1 |
| aacla | 1 |
| aaejmtubv | 1 |
| aaethytj | 1 |
+------------+----------+
您可以为同一查询中的不同方面提供不同的分页参数:
-- 多个方面具有不同的分页参数
SELECT id, title FROM products
WHERE MATCH('smartphone')
LIMIT 3
FACET category ORDER BY COUNT(*) DESC, category asc LIMIT 0,5
FACET category ORDER BY category ASC LIMIT 3,5
FACET price ORDER BY FACET() ASC LIMIT 5;
输出:
+------+---------------------------------------------+
| id | title |
+------+---------------------------------------------+
| 1 | 错误地沉重的柔软。智能手机 |
| 2 | 知道让人害怕愚蠢。智能手机 |
| 3 | 糟糕的春季晚上驱动的年轻。智能手机 |
+------+---------------------------------------------+
+------------+----------+
| category | count(*) |
+------------+----------+
| aaaabzwfzn | 1 |
| aabswb | 1 |
| aacla | 1 |
| aaejmtubv | 1 |
| aaethytj | 1 |
+------------+----------+
+------------+----------+
| category | count(*) |
+------------+----------+
| aaejmtubv | 1 |
| aaethytj | 1 |
| aaktjgaa | 1 |
| aalfwcvwil | 1 |
| aaqmumofe | 1 |
+------------+----------+
+-------+----------+
| price | count(*) |
+-------+----------+
| 10 | 5 |
| 11 | 8 |
| 12 | 10 |
| 13 | 9 |
| 14 | 8 |
+-------+----------+
此示例演示了几种方面分页技术:
- 第一个方面返回按计数排序的前5个类别(偏移量0,限制5)
- 第二个方面按字母顺序返回类别,跳过前3个(偏移量3,限制5)
- 第三个方面返回按升序排列的前5个价格点
方面分页特别适用于:
- 处理大量方面值(例如,成千上万的产品类别)
- 实现“显示更多”功能以查看更多方面值
- 创建动态过滤接口,首先显示最受欢迎的过滤器
- 通过限制返回的方面值的数量来优化性能
注意: 与主结果分页不同,主结果分页在深偏移时具有性能影响,方面分页通常在大偏移时仍然有效,因为方面计算发生在文档匹配阶段之后。
通过HTTP JSON进行方面分页
使用HTTP JSON接口时,也可以实现方面分页。您可以在聚合规范中使用size
参数限制返回的方面值数量:
{
"table": "products",
"query": { "match": { "*": "smartphone" } },
"limit": 3,
"aggs": {
"category_counts": {
"terms": {
"field": "category",
"size": 5
}
}
}
}
此示例限制类别方面结果为前5个值。
重要: 与SQL方面不同,HTTP JSON接口目前仅支持使用size
参数限制方面值的数量。不支持在方面内进行分页的OFFSET
参数。您只能指定返回多少结果,而不能指定跳过哪些结果。
有关通过HTTP JSON进行方面分页的更多详细信息,请参见 Manticore 搜索文档 。
实际用例
带高亮的分页
-- 带高亮的分页
SELECT id, title, price, weight(),
HIGHLIGHT({limit=100}) as highlight
FROM products
WHERE MATCH('smartphone')
ORDER BY weight() DESC, price ASC, id ASC
LIMIT 5, 5;
输出:
+------+----------------------------------------------+-------+----------+-----------------------------------------------------+
| id | title | price | weight() | highlight |
+------+----------------------------------------------+-------+----------+-----------------------------------------------------+
| 246 | 软的技术上已采取。智能手机 | 11 | 1272 | 软的技术上已采取。<b>智能手机</b> |
| 1105 | 肮脏的事情!暂时称之为。智能手机 | 11 | 1272 | 肮脏的事情!暂时称之为。<b>智能手机</b> |
| 3293 | 瞬间变厚了,伤害了。智能手机 | 11 | 1272 | 瞬间变厚了,伤害了。<b>智能手机</b> |
| 3736 | 工作她出了错,局部地方。智能手机 | 11 | 1272 | 工作她出了错,局部地方。<b>智能手机</b> |
| 6978 | 陈旧的声音被动甜美干净。智能手机 | 11 | 1272 | 陈旧的声音被动甜美干净。<b>智能手机</b> |
+------+----------------------------------------------+-------+----------+-----------------------------------------------------+
实现“加载更多”按钮与滚动分页
基于滚动的分页非常适合实现“加载更多”功能,因为它提供了高效的方式在大型结果集之间导航。以下是如何实现它:
-- 初始查询与SHOW SCROLL以获取滚动令牌
SELECT id, title, price, weight() FROM products
WHERE MATCH('smartphone')
ORDER BY weight() DESC, price ASC, id ASC
LIMIT 5; SHOW SCROLL\G
输出:
+------+------------------------------------------------+-------+----------+
| id | title | price | weight() |
+------+------------------------------------------------+-------+----------+
| 1179 | 弱的说?摔倒。智能手机 | 10 | 1272 |
| 1388 | 3;她的日子。智能手机 | 10 | 1272 |
| 1636 | 积极的糟糕的冬季清洁。智能手机 | 10 | 1272 |
| 5628 | 愚蠢地从听说低。智能手机 | 10 | 1272 |
| 8561 | 左边寄送。偶尔,尝试躺下。智能手机 | 10 | 1272 |
+------+------------------------------------------------+-------+----------+
*************************** 1. 行 ***************************
scroll_token: eyJvcmRlcl9ieV9zdHIiOiJ3ZWlnaHQoKSBERVNDLCBwcmljZSBBU0MsIGlkIEFTQyIsIm9yZGVyX2J5IjpbeyJhdHRyIjoid2VpZ2h0KCkiLCJkZXNjIjp0cnVlLCJ2YWx1ZSI6MTI3MiwidHlwZSI6ImludCJ9LHsiYXR0ciI6InByaWNlIiwiZGVzYyI6ZmFsc2UsInZhbHVlIjoxMCwidHlwZSI6ImludCJ9LHsiYXR0ciI6ImlkIiwiZGVzYyI6ZmFsc2UsInZhbHVlIjo4NTYxLCJ0eXBlIjoiaW50In1dfQ==
1 row in set (0.00 sec)
-- 当用户点击“加载更多”时,使用滚动令牌获取下一批
SELECT id, title, price, weight() FROM products
WHERE MATCH('smartphone')
LIMIT 5
OPTION scroll='eyJvcmRlcl9ieV9zdHIiOiJ3ZWlnaHQoKSBERVNDLCBwcmljZSBBU0MsIGlkIEFTQyIsIm9yZGVyX2J5IjpbeyJhdHRyIjoid2VpZ2h0KCkiLCJkZXNjIjp0cnVlLCJ2YWx1ZSI6MTI3MiwidHlwZSI6ImludCJ9LHsiYXR0ciI6InByaWNlIiwiZGVzYyI6ZmFsc2UsInZhbHVlIjoxMCwidHlwZSI6ImludCJ9LHsiYXR0ciI6ImlkIiwiZGVzYyI6ZmFsc2UsInZhbHVlIjo4NTYxLCJ0eXBlIjoiaW50In1dfQ=='; SHOW SCROLL\G
输出:
+------+---------------------------------------------+-------+----------+
| id | title | price | weight() |
+------+---------------------------------------------+-------+----------+
| 246 | 软件技术上占用。智能手机 | 11 | 1272 |
| 1105 | 肮脏时叫。智能手机 | 11 | 1272 |
| 3293 | 立即变厚跑得痛。智能手机 | 11 | 1272 |
| 3736 | 本地工作错了。智能手机 | 11 | 1272 |
| 6978 | 陈旧的声音消极甜美干净。智能手机 | 11 | 1272 |
+------+---------------------------------------------+-------+----------+
*************************** 1. row ***************************
scroll_token: eyJvcmRlcl9ieV9zdHIiOiJ3ZWlnaHQoKSBERVNDLCBwcmljZSBBU0MsIGlkIEFTQyIsIm9yZGVyX2J5IjpbeyJhdHRyIjoid2VpZ2h0KCkiLCJkZXNjIjp0cnVlLCJ2YWx1ZSI6MTI3MiwidHlwZSI6ImludCJ9LHsiYXR0ciI6InByaWNlIiwiZGVzYyI6ZmFsc2UsInZhbHVlIjoxMSwidHlwZSI6ImludCJ9LHsiYXR0ciI6ImlkIiwiZGVzYyI6ZmFsc2UsInZhbHVlIjo2OTc4LCJ0eXBlIjoiaW50In1dfQ==
对于每次随后的“加载更多”点击,您将使用上一个查询返回的新滚动令牌。这种方法相对于传统的基于偏移的分页有几个优点:
- 更好的性能:深度分页没有性能下降
- 没有 max_matches 限制:您可以高效地逐页浏览数百万条结果
- 顺序访问:高效地浏览连续页面的结果
实现提示:
- 将滚动令牌存储在您的应用状态中(客户端或服务器端)
- 当用户点击“加载更多”时,在每个后续请求中包含令牌
- 始终使用最新的令牌,因为每个令牌表示结果集中的当前位
- 记住,滚动令牌是临时的,并最终会过期
这种实施方法解决了许多传统基于偏移的分页所述的限制,特别是在深度分页场景中。
结论
Manticore Search 提供了灵活的分页选项,以满足不同的用例。传统的基于偏移的分页适用于有限页面导航的简单界面,而基于滚动的分页在深度分页场景(如无限滚动或数据导出)中提供更好的性能。
在 Manticore Search 中实施分页时:
- 对于浅层分页(前几页):使用一致排序的传统基于偏移的分页。
- 对于深层分页(许多页面或大数据集):使用基于滚动的分页,并使用适当的基于 id 的排序标准。
- 对于一致结果:在您的 ORDER BY 子句中始终包含
id
字段,并在查询中保持一致的排序标准。 - 对于全文搜索:在您的排序标准中包含
weight()
或_score
,以确保最相关的结果首先出现。
通过理解每种方法的优缺点和最佳实践,您可以为特定应用需求实施最有效的分页策略。请记住,最佳的分页方法取决于您的用例、数据量和用户体验要求。