感知查询(Percolate Queries)

现在,长期以来被请求的功能已在最新代码中可用:感知查询。

如果你想进行交互式课程,请点击 这里

感知查询也被称为持久查询、预测搜索、文档路由、反向搜索或逆向搜索。

通常的搜索方式是存储要搜索的文档并对其执行查询。然而,在某些情况下,我们希望对新传入的文档应用查询以发出匹配信号。有些场景需要这种功能。例如,监控系统不仅仅收集数据,还希望在不同事件中通知用户。这可能是达到某个指标的阈值,或者在监控数据中出现某个特定值。另一个类似的案例是新闻聚合。你可以通知用户任何新鲜新闻,但用户可能只想被通知特定类别或主题。更进一步,他们可能只对某些"关键词"感兴趣。

在这种情况下,传统搜索并不适合,因为它假定对整个集合执行所需的搜索,这会随着用户数量的增加而成倍增长,最终导致大量查询在整个集合上运行,这可能会带来额外的负载。为了克服这一点,产生了一个想法:存储查询并针对新传入的文档或文档批次对其进行测试。

之前可以通过使用实时索引(Real-Time index)来变通解决,只在其上添加新文档,测试查询,然后截断RT索引。但这远非一个好的解决方案。最后,我们将这一功能添加到Manticore Search中(对于从Sphinx Search升级的用户)。

对于查询的存储,使用了一种基于实时索引的新索引类型。percolate索引允许添加、查看或删除查询。

匹配查询是通过一个新的语句完成的,该语句以文档或文档列表作为输入,并返回匹配的查询。

请注意,‘percolate’索引不是现有索引的替代品,它不存储文档。在常规用例中,文档的插入方式与之前相同。感知查询可以在插入后执行,可以由同一工作进程或独立于插入工作进程的另一工作进程执行(可以由cron作业或插入触发)。后者更可取,因为PQ不会延迟插入的确认(对最终用户),但这取决于应用程序的工作流程。

存储感知查询

如上所述,感知查询存储在定义为percolate类型的特殊实时索引中。

一个简单的PQ索引只需定义类型和路径:

index pq {
    type = percolate
    path = /var/lib/sphinxsearch/pq
}

要使其可用,只需发出 RELOAD INDEXES 命令:

MySQL [(none)]> RELOAD INDEXES; SHOW TABLES LIKE 'pq';
Query OK, 0 rows affected (0.00 sec)

+-------+-----------+
| Index | Type      |
+-------+-----------+
| pq    | percolate |
+-------+-----------+
1 row in set (0.00 sec)

文档ID支持自动递增,在插入时,你不需要像普通RT索引那样生成ID。插入新查询需要一个强制的’query’文本字段,其中包含全文搜索表达式。

MySQL [(none)]> INSERT INTO pq(query) VALUES('catch me');
Query OK, 1 rows affected (0.00 sec)

MySQL [(none)]> SELECT * FROM pq;
+------+----------+------+---------+
| UID  | Query    | Tags | Filters |
+------+----------+------+---------+
|    1 | catch me |      |         |
+------+----------+------+---------+
1 row in set (0.00 sec)

有两个具有特殊用途的可选属性。

第一个是’tags’,它支持字符串包(类似字符串MVA)。标签可用于在执行SELECT或DELETE时过滤PQ,它们在执行感知搜索时没有作用。

MySQL [(none)]> insert into pq(query,tags) values('catch me if','tag1');
Query OK, 1 rows affected (0.00 sec)

MySQL [(none)]> SELECT * FROM pq WHEREtags='tag1';
+------+-------------+------+---------+
| UID  | Query       | Tags | Filters |
+------+-------------+------+---------+
|    2 | catch me if | tag1 |         |
+------+-------------+------+---------+
2 rows in set (0.00 sec)

第二个是’filters’,包含额外的查询规则,可以存储SphinxQL格式的属性过滤。这里使用的属性必须像RT索引中的属性一样在索引配置中声明。要启用新属性,我们可以使用ALTER RTINDEX RECONFIGURE。

index pq {
    type = percolate
     path = /var/lib/sphinxsearch/pq
     rt_attr_uint = catId
}

MySQL [(none)]> ALTER RTINDEX pq RECONFIGURE;
Query OK, 0 rows affected (0.00 sec)

MySQL [(none)]> INSERT INTO pq(query,tags,filters) VALUES('catch me','tag2,tag1','catId=10');
Query OK, 1 rows affected (0.00 sec)

MySQL [(none)]> select * from pq;
+------+-------------+-----------+-----------+
| UID  | Query       | Tags      | Filters   |
+------+-------------+-----------+-----------+
|    3 | catch me    |           |           |
|    4 | catch me if | tag1      |           |
|    5 | catch me    | tag2,tag1 |  catid=10 |
+------+-------------+-----------+-----------+
3 rows in set (0.00 sec)

如果要启用每个字段的文本搜索,需要在索引配置中定义全文字段并执行重新配置。

index pq {
    type = percolate
    path = /var/lib/sphinxsearch/pq
    rt_field = subject
    rt_field = content
    rt_attr_uint = catId
}

MySQL [(none)]>  ALTER RTINDEX pq RECONFIGURE;
Query OK, 0 rows affected (0.00 sec)

MySQL [(none)]> INSERT INTO pq(query,tags,filters) VALUES('@subject match by field','tag2,tag3','catId=10');
Query OK, 1 rows affected (0.00 sec)

MySQL [(none)]> INSERT INTO pq(query,tags,filters) VALUES('@subject  match by field','tag2,tag3','catId=20');
Query OK, 1 rows affected (0.00 sec)

执行感知查询

CALL PQ函数可用于针对单个感知索引(目前),该索引在函数的第一个参数中设置。第二个参数是必需的,包含一个文档或文档列表。传递的文档可以是纯文本或JSON对象。JSON对象允许传递多个文本字段,因此可以进行每个字段的匹配,以及针对感知查询的’filters’属性中定义的表达式进行测试的属性。这意味着查询不仅仅是全文匹配表达式,还可以有属性过滤,允许更复杂的匹配(例如某人搜索特定关键词,但他们只想被通知某些类型或类别的文章)。

默认情况下,CALL PQ期望JSON格式的文档,所以如果我们要执行最基本的示例,需要传递0 as docs_json。默认结果集仅包含查询ID,但我们可以使用query选项显示所有存储的信息

MySQL [(none)]> CALL PQ('pq','catch me',0 AS docs_json,1 AS query);
+------+----------+------+---------+
| UID  | Query    | Tags | Filters |
+------+----------+------+---------+
|    6 | catch me |      |         |
+------+----------+------+---------+
1 row in set (0.00 sec)

要执行每个字段匹配或额外的属性过滤,我们需要将文档作为JSON对象传递。

MySQL [(none)]> CALL PQ('pq','{"subject":"expect to match by field","content":"here is some content","catid":20}',1 AS query);
+------+--------------------------+-----------+-----------+
| UID  | Query                    | Tags      | Filters   |
+------+--------------------------+-----------+-----------+
|   10 | @subject  match by field | tag2,tag3 |  catid=20 |
+------+--------------------------+-----------+-----------+
1 row in set (0.00 sec)

CALL PQ还有2个运行时选项:‘docs’选项在输入有多个文档时很有用,它会告诉你哪个文档匹配了搜索(从索引1开始)。

MySQL [(none)]> CALL PQ('pq',('catch me if can','catch me'),0 AS docs_json,1 AS docs,1 AS verbose);
+------+-----------+-------------+------+---------+
| UID  | Documents | Query       | Tags | Filters |
+------+-----------+-------------+------+---------+
|    6 | 1,2       | catch me    |      |         |
|    7 | 1         | catch me if | tag1 |         |
+------+-----------+-------------+------+---------+
2 rows in set (0.00 sec)

在运行CALL PQ时,SHOW META提供了有关执行函数的信息,如执行时间、匹配的查询和文档数量、存储的查询总数。
当在CALL PQ中设置’verbose’时,会显示一些额外信息,如每个查询花费的时间。

MySQL [(none)]> SHOW META;
+-------------------------+-----------+
| Name                    | Value     |
+-------------------------+-----------+
| Total                   | 0.000 sec |
| Setup                   | 0.000 sec |
| Queries matched         | 2         |
| Document matches        | 2         |
| Total queries stored    | 6         |
| Term only queries       | 6         |
| Fast rejected queries   | 3         |
| Time per query          | 32, 21    |
| Time of matched queries | 53        |
+-------------------------+-----------+
9 rows in set (0.00 sec)

反馈

作为添加到Manticore的新功能,它为Manticore Search开辟了新的使用场景,我们欢迎大家测试并提供任何形式的反馈。同时我们将继续在此方面工作,在我们可以称其为生产就绪之前还有一些事情需要完成。我们将感谢任何帮助测试的努力。

安装Manticore Search

安装Manticore Search