本地英文版地址: ../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 查询的可伸缩性与其他查询不同,因此使用不同的索引配置(如主分片的数量)可能有利于渗透性能。