Percolate 查询

Percolate 查询也称为持久查询、前瞻搜索、文档路由、反向搜索和逆向搜索。

传统的搜索方式是存储文档并对其执行搜索查询。然而,有些情况下我们希望将查询应用于新到达的文档以标记匹配。一些需要这种方式的场景包括监控系统收集数据并通知用户特定事件,例如达到某个指标阈值或监控数据中出现特定值。另一个例子是新闻聚合,用户可能只想收到某些类别或主题,甚至特定“关键词”的通知。

在这些情况下,传统搜索并不适用,因为它假设搜索是在整个集合上执行的。这个过程会随着用户数量的增加而成倍增长,导致大量查询在整个集合上运行,可能造成显著的额外负载。本节描述的替代方法是存储查询,然后将它们测试于新到达的文档或文档批次。

Google Alerts、AlertHN、Bloomberg Terminal 以及其他允许用户订阅特定内容的系统都使用类似技术。

使用 CALL PQ 执行 percolate 查询

关于 percolate 查询,关键是你的搜索查询已经存储在表中。你需要提供的是文档,以检查它们是否匹配任何存储的规则

你可以通过 SQL 或 JSON 接口执行 percolate 查询,也可以使用编程语言客户端。SQL 方法更灵活,而 HTTP 方法更简单,且提供了大部分所需功能。下表帮助你理解两者的区别。

期望行为 SQL HTTP
提供单个文档 CALL PQ('tbl', '{doc1}') query.percolate.document{doc1}
提供单个文档(替代) CALL PQ('tbl', 'doc1', 0 as docs_json) -
提供多个文档 CALL PQ('tbl', ('doc1', 'doc2'), 0 as docs_json) -
提供多个文档(替代) CALL PQ('tbl', ('{doc1}', '{doc2}')) -
提供多个文档(替代) CALL PQ('tbl', '[{doc1}, {doc2}]') -
返回匹配的文档 ID 0/1 作为 docs(默认禁用) 默认启用
使用文档自身 ID 显示结果 'id field' 作为 docs_id(默认禁用) 不可用
认为输入文档是 JSON 1 作为 docs_json(默认 1) 默认启用
认为输入文档是纯文本 0 作为 docs_json(默认 1) 不可用
稀疏分布模式 默认 默认
分片分布模式 sharded 作为 mode 不可用
返回匹配查询的所有信息 1 作为 query(默认 0) 默认启用
跳过无效 JSON 1 作为 skip_bad_json(默认 0) 不可用
SHOW META 中显示扩展信息 1 作为 verbose(默认 0) 不可用
定义当未提供 docs_id 字段时加到文档 ID 的数值(主要用于分布式 PQ 模式 1 作为 shift(默认 0) 不可用

为了演示其工作原理,以下是几个示例。我们创建一个包含两个字段的 PQ 表:

  • title(文本)
  • color(字符串)

以及其中的三条规则:

  • 仅全文。查询:@title bag
  • 全文加过滤。查询:@title shoes。过滤条件:color='red'
  • 全文加更复杂的过滤。查询:@title shoes。过滤条件:color IN('blue', 'green')
‹›
  • SQL
  • JSON
  • PHP
  • Python
  • Python-asyncio
  • javascript
  • Java
  • C#
  • Rust
  • TypeScript
  • Go
📋
CREATE TABLE products(title text, color string) type='pq';
INSERT INTO products(query) values('@title bag');
INSERT INTO products(query,filters) values('@title shoes', 'color=\'red\'');
INSERT INTO products(query,filters) values('@title shoes', 'color in (\'blue\', \'green\')');
select * from products;
‹›
Response
+---------------------+--------------+------+---------------------------+
| id                  | query        | tags | filters                   |
+---------------------+--------------+------+---------------------------+
| 1657852401006149635 | @title shoes |      | color IN ('blue, 'green') |
| 1657852401006149636 | @title shoes |      | color='red'               |
| 1657852401006149637 | @title bag   |      |                           |
+---------------------+--------------+------+---------------------------+
只告诉我哪些 PQ 规则匹配我的单个文档

第一个文档不匹配任何规则。它可能匹配前两个,但它们需要额外的过滤条件。

第二个文档匹配一条规则。注意 CALL PQ 默认期望文档为 JSON,但如果使用 0 as docs_json,你可以传递纯字符串。

‹›
  • SQL
  • JSON
  • PHP
  • Python
  • Python-asyncio
  • javascript
  • Java
  • C#
  • Rust
  • TypeScript
  • Go
📋
CALL PQ('products', 'Beautiful shoes', 0 as docs_json);
CALL PQ('products', 'What a nice bag', 0 as docs_json);
CALL PQ('products', '{"title": "What a nice bag"}');
‹›
Response
+---------------------+
| id                  |
+---------------------+
| 1657852401006149637 |
+---------------------+
+---------------------+
| id                  |
+---------------------+
| 1657852401006149637 |
+---------------------+
我想知道与我的文档匹配的完整 PQ 规则
‹›
  • SQL
  • JSON
  • PHP
  • Python
  • Python-asyncio
  • javascript
  • Java
  • C#
  • Rust
  • TypeScript
  • Go
📋
CALL PQ('products', '{"title": "What a nice bag"}', 1 as query);
‹›
Response
+---------------------+------------+------+---------+
| id                  | query      | tags | filters |
+---------------------+------------+------+---------+
| 1657852401006149637 | @title bag |      |         |
+---------------------+------------+------+---------+
多个文档怎么样?

请注意,使用 CALL PQ,您可以通过不同方式提供多个文档:

  • 作为圆括号中的普通文档数组 ('doc1', 'doc2')。这需要 0 as docs_json
  • 作为圆括号中的 JSON 数组 ('{doc1}', '{doc2}')
  • 或作为标准 JSON 数组 '[{doc1}, {doc2}]'
‹›
  • SQL
  • JSON
  • PHP
  • Python
  • Python-asyncio
  • javascript
  • Java
  • C#
  • Rust
  • TypeScript
  • Go
📋
CALL PQ('products', ('nice pair of shoes', 'beautiful bag'), 1 as query, 0 as docs_json);
CALL PQ('products', ('{"title": "nice pair of shoes", "color": "red"}', '{"title": "beautiful bag"}'), 1 as query);
CALL PQ('products', '[{"title": "nice pair of shoes", "color": "blue"}, {"title": "beautiful bag"}]', 1 as query);
‹›
Response
+---------------------+------------+------+---------+
| id                  | query      | tags | filters |
+---------------------+------------+------+---------+
| 1657852401006149637 | @title bag |      |         |
+---------------------+------------+------+---------+
+---------------------+--------------+------+-------------+
| id                  | query        | tags | filters     |
+---------------------+--------------+------+-------------+
| 1657852401006149636 | @title shoes |      | color='red' |
| 1657852401006149637 | @title bag   |      |             |
+---------------------+--------------+------+-------------+
+---------------------+--------------+------+---------------------------+
| id                  | query        | tags | filters                   |
+---------------------+--------------+------+---------------------------+
| 1657852401006149635 | @title shoes |      | color IN ('blue, 'green') |
| 1657852401006149637 | @title bag   |      |                           |
+---------------------+--------------+------+---------------------------+
我想知道哪些文档匹配哪些规则

使用选项 1 as docs 可以让您看到提供的文档中哪些匹配哪些规则。

‹›
  • SQL
  • JSON
  • PHP
  • Python
  • Python-asyncio
  • javascript
  • Java
  • C#
  • Rust
  • TypeScript
  • Go
📋
CALL PQ('products', '[{"title": "nice pair of shoes", "color": "blue"}, {"title": "beautiful bag"}]', 1 as query, 1 as docs);
‹›
Response
+---------------------+-----------+--------------+------+---------------------------+
| id                  | documents | query        | tags | filters                   |
+---------------------+-----------+--------------+------+---------------------------+
| 1657852401006149635 | 1         | @title shoes |      | color IN ('blue, 'green') |
| 1657852401006149637 | 2         | @title bag   |      |                           |
+---------------------+-----------+--------------+------+---------------------------+

静态 id

默认情况下,匹配的文档 id 对应于您提供列表中的相对编号。但是,在某些情况下,每个文档已经有自己的 id。对于这种情况,CALL PQ 有一个选项 'id field name' as docs_id

请注意,如果通过提供的字段名找不到 id,则 PQ 规则不会显示在结果中。

此选项仅适用于通过 SQL 使用 CALL PQ

‹›
  • SQL
SQL
📋
CALL PQ('products', '[{"id": 123, "title": "nice pair of shoes", "color": "blue"}, {"id": 456, "title": "beautiful bag"}]', 1 as query, 'id' as docs_id, 1 as docs);
‹›
Response
+---------------------+-----------+--------------+------+---------------------------+
| id                  | documents | query        | tags | filters                   |
+---------------------+-----------+--------------+------+---------------------------+
| 1657852401006149664 | 456       | @title bag   |      |                           |
| 1657852401006149666 | 123       | @title shoes |      | color IN ('blue, 'green') |
+---------------------+-----------+--------------+------+---------------------------+
我可能有无效的 JSON,请跳过它们

当使用带有独立 JSON 的 CALL PQ 时,您可以使用选项 1 作为 skip_bad_json 来跳过输入中的任何无效 JSON。在下面的示例中,第 2 个查询由于无效的 JSON 而失败,但第 3 个查询通过使用 1 作为 skip_bad_json 避免了错误。请记住,当通过 HTTP 发送 JSON 查询时,此选项不可用,因为在这种情况下整个 JSON 查询必须是有效的。

‹›
  • SQL
SQL
📋
CALL PQ('products', ('{"title": "nice pair of shoes", "color": "blue"}', '{"title": "beautiful bag"}'));
CALL PQ('products', ('{"title": "nice pair of shoes", "color": "blue"}', '{"title": "beautiful bag}'));
CALL PQ('products', ('{"title": "nice pair of shoes", "color": "blue"}', '{"title": "beautiful bag}'), 1 as skip_bad_json);
‹›
Response
+---------------------+
| id                  |
+---------------------+
| 1657852401006149635 |
| 1657852401006149637 |
+---------------------+
ERROR 1064 (42000): Bad JSON objects in strings: 2
+---------------------+
| id                  |
+---------------------+
| 1657852401006149635 |
+---------------------+
我想要更高性能的 percolate 查询

Percolate 查询是为高吞吐量和大数据量设计的。为了优化性能以实现更低的延迟和更高的吞吐量,请考虑以下内容。

percolate 表有两种分布模式,以及 percolate 查询如何针对它工作:

  • 稀疏(默认)。 适用于:大量文档,镜像 PQ 表。当您的文档集很大但存储在 PQ 表中的查询集较小时,稀疏模式是有益的。在此模式下,您传递的文档批次将被分配给多个代理,因此每个节点只处理请求中文档的一部分。Manticore 会拆分您的文档集并在镜像之间分发块。一旦代理完成查询处理,Manticore 会收集并合并结果,返回一个最终的查询集,就像它来自单个表一样。使用复制来辅助此过程。
  • 分片。 适用于:大量 PQ 规则,规则分布在多个 PQ 表中。在此模式下,整个文档集会广播到分布式 PQ 表的所有表,而不会初始拆分文档。当推送的文档集相对较小时,但存储的查询数量很大时,这种方式是有益的。在这种情况下,更适合在每个节点上只存储部分 PQ 规则,然后合并从处理相同文档集但针对不同 PQ 规则集的节点返回的结果。此模式必须显式设置,因为它意味着网络负载增加,并且期望表具有不同的 PQ,这一点是复制无法开箱即用实现的。

假设您有定义为:

table pq_d2
{
    type = distributed
    agent = 127.0.0.1:6712:pq
    agent = 127.0.0.1:6712:ptitle
}

“pq”和“ptitle”各自包含:

‹›
  • SQL
  • JSON
  • PHP
  • Python
  • Python-asyncio
  • javascript
  • Java
  • C#
  • Rust
  • TypeScript
  • Go
📋
SELECT * FROM pq;
‹›
Response
+------+-------------+------+-------------------+
| id   | query       | tags | filters           |
+------+-------------+------+-------------------+
|    1 | filter test |      | gid>=10           |
|    2 | angry       |      | gid>=10 OR gid<=3 |
+------+-------------+------+-------------------+
2 rows in set (0.01 sec)

然后您在分布式表上执行 CALL PQ,并传入几个文档。

‹›
  • SQL
  • JSON
  • PHP
  • Python
  • Python-asyncio
  • javascript
  • Java
  • C#
  • Rust
  • TypeScript
  • Go
📋
CALL PQ ('pq_d2', ('{"title":"angry test", "gid":3 }', '{"title":"filter test doc2", "gid":13}'), 1 AS docs);
‹›
Response
+------+-----------+
| id   | documents |
+------+-----------+
|    1 | 2         |
|    2 | 1         |
+------+-----------+

在前面的示例中,我们使用了默认的 稀疏 模式。为了演示 分片 模式,让我们创建一个由 2 个本地 PQ 表组成的分布式 PQ 表,并向 "products1" 添加 2 个文档,向 "products2" 添加 1 个文档:

create table products1(title text, color string) type='pq';
create table products2(title text, color string) type='pq';
create table products_distributed type='distributed' local='products1' local='products2';
INSERT INTO products1(query) values('@title bag');
INSERT INTO products1(query,filters) values('@title shoes', 'color=\'red\'');
INSERT INTO products2(query,filters) values('@title shoes', 'color in (\'blue\', \'green\')');

现在,如果您向 CALL PQ 添加 'sharded' as mode,它将把文档发送到所有代理的表(在此情况下仅本地表,但它们可以是远程的以利用外部硬件)。此模式通过 JSON 接口不可用。

‹›
  • SQL
SQL
📋
CALL PQ('products_distributed', ('{"title": "nice pair of shoes", "color": "blue"}', '{"title": "beautiful bag"}'), 'sharded' as mode, 1 as query);
‹›
Response
+---------------------+--------------+------+---------------------------+
| id                  | query        | tags | filters                   |
+---------------------+--------------+------+---------------------------+
| 1657852401006149639 | @title bag   |      |                           |
| 1657852401006149643 | @title shoes |      | color IN ('blue, 'green') |
+---------------------+--------------+------+---------------------------+

请注意,配置中代理镜像的语法(当一个 agent 行分配了多个主机,用 | 分隔)与 CALL PQ 查询模式无关。每个 agent 始终代表一个节点,无论为该代理指定了多少 HA 镜像。

我如何了解更多关于性能的信息?

在某些情况下,您可能想要获取有关 percolate 查询性能的更多详细信息。为此,有一个选项 1 as verbose,该选项仅通过 SQL 可用,允许您保存更多性能指标。您可以使用 SHOW META 查询查看它们,该查询可以在 CALL PQ 之后运行。有关更多信息,请参见 SHOW META

‹›
  • 1 as verbose
  • 0 as verbose
📋
CALL PQ('products', ('{"title": "nice pair of shoes", "color": "blue"}', '{"title": "beautiful bag"}'), 1 as verbose); show meta;
‹›
Response
+---------------------+
| id                  |
+---------------------+
| 1657852401006149644 |
| 1657852401006149646 |
+---------------------+
+-------------------------+-----------+
| Variable name           | Value     |
+-------------------------+-----------+
| total                   | 0.000 sec |
| setup                   | 0.000 sec |
| queries_matched         | 2         |
| queries_failed          | 0         |
| document_matched        | 2         |
| total_queries_stored    | 3         |
| term_only_queries       | 3         |
| fast_rejected_queries   | 0         |
| time_per_query          | 27, 10    |
| time_of_matched_queries | 37        |
+-------------------------+-----------+
Last modified: December 09, 2025