本地英文版地址: ../en/query-dsl-percolate-query.html
percolate查询可用于匹配存储在索引中的查询。
percolate 查询本身包含将用作查询的文档,以匹配存储的查询。
使用示例
创建一个有2个字段的索引:
PUT /my-index
{
"mappings": {
"properties": {
"message": {
"type": "text"
},
"query": {
"type": "percolator"
}
}
}
}
message 字段是用于在将 percolator 查询中定义的文档编入临时索引之前对其进行预处理的字段。
query 字段用于索引查询文档。
它将保存一个 json 对象,表示一个实际的 Elasticsearch 查询。
字段 query 已在映射中配置为使用percolator字段类型。
这个字段类型理解查询领域专用语言(dsl),并以这样的方式存储查询,以便以后可以使用它来匹配在 percolate 查询中定义的文档。
在 percolator 中注册一个查询(相当于添加并索引一个id为1的文档):
PUT /my-index/_doc/1?refresh
{
"query" : {
"match" : {
"message" : "bonsai tree"
}
}
}
将文档与注册的 percolator 查询进行匹配:
GET /my-index/_search
{
"query" : {
"percolate" : {
"field" : "query",
"document" : {
"message" : "A new bonsai tree in the office"
}
}
}
}
上面这个请求将产生如下响应:
{
"took": 13,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped" : 0,
"failed": 0
},
"hits": {
"total" : {
"value": 1,
"relation": "eq"
},
"max_score": 0.26152915,
"hits": [
{
"_index": "my-index",
"_type": "_doc",
"_id": "1",
"_score": 0.26152915,
"_source": {
"query": {
"match": {
"message": "bonsai tree"
}
}
},
"fields" : {
"_percolator_document_slot" : [0]
}
}
]
}
}
为了举一个简单的例子,这个文档为 percolate 查询和文档使用一个名为 my-index 的索引。 当只注册了几个 percolate 查询时,这种设置可以很好地工作。 但是,如果使用量较大,建议将查询和文档存储在单独的索引中。 更多细节请参考 它如何在引擎盖下工作的。
参数
渗透(percolating)文档时需要以下参数:
|
|
(必需) 保存索引查询的 |
|
|
(可选) 在指定了多个 |
|
|
被渗透的文档源。 |
|
|
类似于 |
|
|
被渗透的文档的类型(type)/映射(mapping)。此参数已被废弃,将在 Elasticsearch 8.0 中移除。 |
也可以从已经存储的文档中检索源,而不是指定要渗透的文档源。
然后,percolate 查询将在内部执行一个 get 请求来获取文档。
在这种情况下,document 参数可以用以下参数替换:
|
|
(必需) 文档所在的索引。 |
|
|
要获取的文档的类型。此参数已被废弃,将在 Elasticsearch 8.0 中移除。 |
|
|
(必需) 要获取的文档的 id。 |
|
|
(可选) 用于获取要渗透(percolate)的文档的路由。 |
|
|
(可选) 用于获取要渗透的文档的首选项。 |
|
|
(可选) 预期要获取的文档的版本。 |
在过滤上下文中渗透(percolating in a filter context)
如果你对相关性评分不感兴趣,可以通过将 percolator 查询包裹在 bool 查询的 filter 子句或 constant_score 查询中来获得更好的性能:
GET /my-index/_search
{
"query" : {
"constant_score": {
"filter": {
"percolate" : {
"field" : "query",
"document" : {
"message" : "A new bonsai tree in the office"
}
}
}
}
}
}
在索引时,从渗透器(percolator) query 中提取 词项(term),渗透器通常可以通过查看这些提取的词项来确定查询是否匹配。
然而,计算得分需要对每个匹配的查询进行反序列化,并在渗透后的文档上运行,这是一个成本很高的操作。
因此,如果不需要计算得分,则 percolate 查询应该包裹在一个 constant_score 查询或 bool 查询的 filter 子句中。
请注意,查询缓存永远不会缓存 percolate 查询。
渗透多个文档 (percolating multiple documents)
percolate 查询可以用索引过的渗透查询同时匹配多个文档。
在单个请求中渗透多个文档可以提高性能,因为查询只需要解析和匹配一次,而不是多次。
同时渗透多个文档时,每个匹配的渗透器查询返回的 _percolator_document_slot 字段非常重要。
它指示哪些文档与特定的渗透器查询相匹配。
这些数字与 percolate 查询中指定的 documents 数组中的槽相关。
GET /my-index/_search
{
"query" : {
"percolate" : {
"field" : "query",
"documents" : [
{
"message" : "bonsai tree"
},
{
"message" : "new tree"
},
{
"message" : "the office"
},
{
"message" : "office tree"
}
]
}
}
}
{
"took": 13,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped" : 0,
"failed": 0
},
"hits": {
"total" : {
"value": 1,
"relation": "eq"
},
"max_score": 0.7093853,
"hits": [
{
"_index": "my-index",
"_type": "_doc",
"_id": "1",
"_score": 0.7093853,
"_source": {
"query": {
"match": {
"message": "bonsai tree"
}
}
},
"fields" : {
"_percolator_document_slot" : [0, 1, 3]
}
}
]
}
}
渗透一个已存在的文档 (percolating an existing document)
为了渗透新索引的文档,可以使用 percolate 查询。
基于来自索引请求的响应,可以使用 _id 和其他元信息来立即渗透新添加的文档。
示例
基于前面的例子。
索引要渗透的文档:
PUT /my-index/_doc/2
{
"message" : "A new bonsai tree in the office"
}
索引响应:
{
"_index": "my-index",
"_type": "_doc",
"_id": "2",
"_version": 1,
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"result": "created",
"_seq_no" : 1,
"_primary_term" : 1
}
渗透现有文档,使用索引响应作为基础来构建新的搜索请求:
GET /my-index/_search
{
"query" : {
"percolate" : {
"field": "query",
"index" : "my-index",
"id" : "2",
"version" : 1
}
}
}
返回的搜索响应与前面的示例相同。
percolate 查询以及高亮
当需要高亮显示时,percolate 查询以一种特殊的方式处理。
查询命中用于高亮 percolate 查询中提供的文档。
而对于常规高亮显示,搜索请求中的查询用于高亮显示命中。
示例
这个示例基于第一个示例中的mapping。
保存一个查询:
PUT /my-index/_doc/3?refresh
{
"query" : {
"match" : {
"message" : "brown fox"
}
}
}
保存另外一个查询:
PUT /my-index/_doc/4?refresh
{
"query" : {
"match" : {
"message" : "lazy dog"
}
}
}
在启用高亮显示的情况下执行 percolate 查询搜索请求:
GET /my-index/_search
{
"query" : {
"percolate" : {
"field": "query",
"document" : {
"message" : "The quick brown fox jumps over the lazy dog"
}
}
},
"highlight": {
"fields": {
"message": {}
}
}
}
这将产生以下响应:
{
"took": 7,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped" : 0,
"failed": 0
},
"hits": {
"total" : {
"value": 2,
"relation": "eq"
},
"max_score": 0.26152915,
"hits": [
{
"_index": "my-index",
"_type": "_doc",
"_id": "3",
"_score": 0.26152915,
"_source": {
"query": {
"match": {
"message": "brown fox"
}
}
},
"highlight": {
"message": [
"The quick <em>brown</em> <em>fox</em> jumps over the lazy dog"
]
},
"fields" : {
"_percolator_document_slot" : [0]
}
},
{
"_index": "my-index",
"_type": "_doc",
"_id": "4",
"_score": 0.26152915,
"_source": {
"query": {
"match": {
"message": "lazy dog"
}
}
},
"highlight": {
"message": [
"The quick brown fox jumps over the <em>lazy</em> <em>dog</em>"
]
},
"fields" : {
"_percolator_document_slot" : [0]
}
}
]
}
}
不是搜索请求中的查询高亮显示渗透器命中,而是渗透器查询高亮显示 percolate 查询中定义的文档。
当像下面的请求一样同时渗透多个文档时,高亮显示的响应是不同的:
GET /my-index/_search
{
"query" : {
"percolate" : {
"field": "query",
"documents" : [
{
"message" : "bonsai tree"
},
{
"message" : "new tree"
},
{
"message" : "the office"
},
{
"message" : "office tree"
}
]
}
},
"highlight": {
"fields": {
"message": {}
}
}
}
响应略有不同,如下所示:
{
"took": 13,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped" : 0,
"failed": 0
},
"hits": {
"total" : {
"value": 1,
"relation": "eq"
},
"max_score": 0.7093853,
"hits": [
{
"_index": "my-index",
"_type": "_doc",
"_id": "1",
"_score": 0.7093853,
"_source": {
"query": {
"match": {
"message": "bonsai tree"
}
}
},
"fields" : {
"_percolator_document_slot" : [0, 1, 3]
},
"highlight" : {
"0_message" : [
"<em>bonsai</em> <em>tree</em>"
],
"3_message" : [
"office <em>tree</em>"
],
"1_message" : [
"new <em>tree</em>"
]
}
}
]
}
}
指定多个 percolate 查询
可以在单个搜索请求中指定多个 percolate 查询:
GET /my-index/_search
{
"query" : {
"bool" : {
"should" : [
{
"percolate" : {
"field" : "query",
"document" : {
"message" : "bonsai tree"
},
"name": "query1"
}
},
{
"percolate" : {
"field" : "query",
"document" : {
"message" : "tulip flower"
},
"name": "query2"
}
}
]
}
}
}
_percolator_document_slot 字段名将以参数 _name 中指定的内容作为后缀。
如果没有指定,那么将使用参数 field,在这种情况下会产生歧义。
上面的搜索请求返回类似于以下内容的响应:
{
"took": 13,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped" : 0,
"failed": 0
},
"hits": {
"total" : {
"value": 1,
"relation": "eq"
},
"max_score": 0.26152915,
"hits": [
{
"_index": "my-index",
"_type": "_doc",
"_id": "1",
"_score": 0.26152915,
"_source": {
"query": {
"match": {
"message": "bonsai tree"
}
}
},
"fields" : {
"_percolator_document_slot_query1" : [0]
}
}
]
}
}
它是如何工作的?
当将文档索引到配置了percolator字段类型映射的索引中时,文档的查询部分被解析成 Lucene 查询并存储到 Lucene 索引中。 存储查询的二进制表示,而且还分析查询的词项并存储到一个索引字段中。
在搜索时,请求中指定的文档被解析成 Lucene 文档,并存储在内存中的临时 Lucene 索引中。 这个内存索引只能保存这一个文档,它为此进行了优化。 在此之后,基于内存索引中的词项构建一个特殊的查询,该查询基于候选渗透器查询的索引查询词项来选择候选渗透器查询。 然后,如果这些查询确实匹配,则由内存中的索引对它们进行评估。
候选渗透器查询匹配的选择是 percolate 查询执行期间的重要性能优化,因为它可以显著减少内存索引需要评估的候选匹配的数量。
percolate 查询可以这样做的原因是因为在渗透器查询的索引过程中,查询词项被提取并用渗透器查询进行索引。
不幸的是,渗透器无法从所有查询中提取词项(例如wildcard或geo_shape查询),因此在某些情况下,渗透器无法进行选择优化(例如,如果在 bool 查询的必需的子句中定义了不支持的查询,或者不支持的查询是渗透器文档中唯一的查询)。
这些查询由渗透器标记,可以通过运行以下搜索找到:
GET /_search
{
"query": {
"term" : {
"query.extraction_result" : "failed"
}
}
}
上面的例子假设在mapping中有一个 percolator 类型的 query 字段。
考虑到渗透的设计,对渗透查询和被渗透的文档使用单独的索引通常是有意义的,而不是像我们在示例中那样使用单一的索引。这种方法有几个好处: 这种方法有几个好处:
- 因为 percolate 查询包含一组不同于被渗透的文档的字段,使用两个独立的索引允许以更密集、更有效的方式存储字段。
- percolate 查询的可伸缩性与其他查询不同,因此使用不同的索引配置(如主分片的数量)可能有利于渗透性能。