Percolate 查询

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

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

在这种情况下,传统的搜索方式并不适用,因为它假设所需的搜索是在整个集合上进行的。这个过程会随着用户的数量而增加,导致许多查询在整集中运行,从而造成显著的额外负载。本节中描述的另一种方法是将查询存储起来,并在新文档或一批文档中测试它们。

Google Alerts、AlertHN、彭博终端和其他允许用户订阅特定内容的系统使用类似的技术。

  • 请参阅 percolate 以获取创建 PQ 表的信息。
  • 请参阅 向 percolate 表添加规则 以了解如何添加 percolate 规则(也称为 PQ 规则)。这里有一个快速示例:

使用 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 as docs (默认禁用) 默认启用
使用文档自身的 id 显示在结果中 'id field' as docs_id (默认禁用)
考虑输入文档是 JSON 1 as docs_json (默认 1) 默认启用
考虑输入文档是纯文本 0 as docs_json (默认 1)
稀疏分布模式 默认 默认
分片分布模式 sharded as mode
返回匹配查询的所有信息 1 as query (默认 0) 默认启用
跳过无效 JSON 1 as skip_bad_json (默认 0)
SHOW META 中提供扩展信息 1 as verbose (默认 0)
定义如果未提供 docs_id 字段将添加到文档 id 的数字(主要适用于 分布式 PQ 模式 1 as 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,请跳过它们

当使用 CALL PQ 处理多个单独的 JSON 时,可以使用选项 1 as skip_bad_json 来跳过输入中的任何无效 JSON。下面的例子中,第 2 个查询因无效 JSON 而失败,但第 3 个查询通过使用 1 as 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 |
+---------------------+
我想提高渗透查询的性能

渗透查询的设计初衷是处理高吞吐量和大数据量。为了优化性能以实现更低的延迟和更高的吞吐量,请考虑以下方面。

渗透表的分布有两种模式,渗透查询可以针对这些模式工作:

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

假设你有一个表 pq_d2 定义如下:

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 个本地渗透表组成的分布式渗透表,并向 "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 镜像。

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

在某些情况下,你可能希望获取更多关于渗透查询性能的详细信息。为此,有一个选项 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