渗透查询

长期以来请求的一个功能现在在最新的代码中可用:渗透查询。

如果您想参加互动课程,请 点击这里

渗透查询也被称为持久查询、前瞻性搜索、文档路由、反向搜索或逆向搜索。

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

在这种情况下,传统搜索并不适合,因为它假设对整个集合执行所需的搜索,这会因用户数量而成倍增加,最终导致大量查询在整个集合上运行,这可能会带来额外的负担。为了解决这个问题,产生了一个想法,即存储查询并将其测试与即将到来的新文档或一批文档。

通过使用一个仅添加新文档的实时索引,可以实现一种变通方法,查询被测试,实时索引被截断。但这远不是一个解决方案。最终,我们将此功能添加到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开辟了新的使用案例,我们欢迎大家进行测试并向我们提供任何反馈。同时,我们将继续在这方面工作,还有一些事情需要完成,才能称其为生产就绪。我们将非常感谢任何测试方面的帮助。

安装Manticore Search

安装Manticore Search