长期请求的功能之一现在在最新代码中可用:渗透查询。
如果想通过交互式课程了解,请前往 此处 。
渗透查询也被称为持久查询、前瞻性搜索、文档路由、反向搜索或逆向搜索。
通常进行搜索的方式是存储我们想要搜索的文档,并对它们执行查询。然而,有时我们希望将查询应用于新到达的文档以触发匹配。在某些场景中这是需要的。例如,监控系统不仅收集数据,还希望在不同事件发生时通知用户。这可能包括达到某个指标的阈值或监控数据中出现的特定值。另一个类似的案例是新闻聚合。您可以通知用户任何新鲜的新闻,但用户可能只希望收到特定类别或主题的通知。更进一步,他们可能只对某些“关键词”感兴趣。
在这种情况下,传统搜索并不适用,因为传统搜索会假设在全部集合上执行所需的搜索,这会随着用户数量的增加而乘以数量,最终导致大量查询在全部集合上运行,这会带来大量额外的负载。为了解决这个问题,诞生了一个想法,即存储查询并将其与新到达的文档或文档批次进行测试。
通过使用实时索引,仅添加新文档,测试查询并截断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还有两个运行时选项:'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开辟了新的使用场景,我们欢迎所有人进行测试并提供任何反馈。同时,我们继续对此进行工作,还有一些事情需要完成才能将其称为生产就绪。我们非常感谢任何测试的帮助。