渗透查询
编辑渗透查询编辑
percolate
查询可用于匹配存储在索引中的查询。percolate
查询本身包含将用作查询的文档,以与存储的查询进行匹配。
示例用法编辑
为了提供一个简单的示例,本文档对渗透查询和文档都使用了一个索引 my-index-000001
。当只注册了少量渗透查询时,此设置可以很好地工作。对于更重的使用,我们建议您将查询和文档存储在单独的索引中。有关更多详细信息,请参阅幕后工作原理。
创建包含两个字段的索引
response = client.indices.create( index: 'my-index-000001', body: { mappings: { properties: { message: { type: 'text' }, query: { type: 'percolator' } } } } ) puts response
PUT /my-index-000001 { "mappings": { "properties": { "message": { "type": "text" }, "query": { "type": "percolator" } } } }
message
字段是在将其索引到临时索引之前用于预处理 percolator
查询中定义的文档的字段。
query
字段用于索引查询文档。它将保存一个表示实际 Elasticsearch 查询的 json 对象。query
字段已配置为使用 渗透器字段类型。此字段类型理解查询 dsl 并以一种稍后可用于匹配 percolate
查询中定义的文档的方式存储查询。
在渗透器中注册查询
response = client.index( index: 'my-index-000001', id: 1, refresh: true, body: { query: { match: { message: 'bonsai tree' } } } ) puts response
PUT /my-index-000001/_doc/1?refresh { "query": { "match": { "message": "bonsai tree" } } }
将文档与已注册的渗透器查询匹配
GET /my-index-000001/_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-000001", "_id": "1", "_score": 0.26152915, "_source": { "query": { "match": { "message": "bonsai tree" } } }, "fields" : { "_percolator_document_slot" : [0] } } ] } }
参数编辑
渗透文档时需要以下参数
|
保存索引查询的 |
|
如果指定了多个 |
|
被渗透文档的来源。 |
|
与 |
|
被渗透文档的类型/映射。此参数已弃用,将在 Elasticsearch 8.0 中删除。 |
除了指定被渗透文档的来源外,还可以从已存储的文档中检索来源。percolate
查询然后将在内部执行 get 请求以获取该文档。
在这种情况下,document
参数可以用以下参数代替
|
文档所在的索引。这是一个必填参数。 |
|
要获取的文档的类型。此参数已弃用,将在 Elasticsearch 8.0 中删除。 |
|
要获取的文档的 ID。这是一个必填参数。 |
|
(可选)用于获取要渗透的文档的路由。 |
|
(可选)用于获取要渗透的文档的首选项。 |
|
(可选)要获取的文档的预期版本。 |
在过滤器上下文中渗透编辑
如果您对分数不感兴趣,则可以通过将渗透器查询包装在 bool
查询的过滤器子句或 constant_score
查询中来获得更好的性能
GET /my-index-000001/_search { "query": { "constant_score": { "filter": { "percolate": { "field": "query", "document": { "message": "A new bonsai tree in the office" } } } } } }
在索引时,从渗透器查询中提取术语,并且渗透器通常可以通过查看这些提取的术语来确定查询是否匹配。但是,计算分数需要反序列化每个匹配的查询并针对渗透的文档运行它,这是一个更加昂贵的操作。因此,如果不需要计算分数,则应将 percolate
查询包装在 constant_score
查询或 bool
查询的过滤器子句中。
请注意,percolate
查询永远不会被查询缓存缓存。
渗透多个文档编辑
percolate
查询可以同时匹配多个文档与索引的渗透器查询。在单个请求中渗透多个文档可以提高性能,因为查询只需要解析和匹配一次,而不是多次。
在同时渗透多个文档时,每个匹配的渗透器查询返回的 _percolator_document_slot
字段非常重要。它指示哪些文档与特定的渗透器查询匹配。这些数字与 percolate
查询中指定的 documents
数组中的位置相关。
GET /my-index-000001/_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-000001", "_id": "1", "_score": 0.7093853, "_source": { "query": { "match": { "message": "bonsai tree" } } }, "fields" : { "_percolator_document_slot" : [0, 1, 3] } } ] } }
渗透现有文档编辑
为了渗透新索引的文档,可以使用 percolate
查询。根据索引请求的响应,可以使用 _id
和其他元信息立即渗透新添加的文档。
示例编辑
基于前面的例子。
索引我们要渗透的文档
response = client.index( index: 'my-index-000001', id: 2, body: { message: 'A new bonsai tree in the office' } ) puts response
PUT /my-index-000001/_doc/2 { "message" : "A new bonsai tree in the office" }
索引响应
{ "_index": "my-index-000001", "_id": "2", "_version": 1, "_shards": { "total": 2, "successful": 1, "failed": 0 }, "result": "created", "_seq_no" : 1, "_primary_term" : 1 }
渗透现有文档,使用索引响应作为构建新搜索请求的基础
GET /my-index-000001/_search { "query": { "percolate": { "field": "query", "index": "my-index-000001", "id": "2", "version": 1 } } }
返回的搜索响应与前面的示例相同。
渗透查询和高亮显示编辑
在高亮显示方面,percolate
查询的处理方式很特别。查询命中用于突出显示 percolate
查询中提供的文档。而在常规高亮显示中,搜索请求中的查询用于高亮显示命中。
示例编辑
此示例基于第一个示例的映射。
保存查询
response = client.index( index: 'my-index-000001', id: 3, refresh: true, body: { query: { match: { message: 'brown fox' } } } ) puts response
PUT /my-index-000001/_doc/3?refresh { "query": { "match": { "message": "brown fox" } } }
保存另一个查询
response = client.index( index: 'my-index-000001', id: 4, refresh: true, body: { query: { match: { message: 'lazy dog' } } } ) puts response
PUT /my-index-000001/_doc/4?refresh { "query": { "match": { "message": "lazy dog" } } }
使用 percolate
查询执行搜索请求并启用高亮显示
GET /my-index-000001/_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-000001", "_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-000001", "_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-000001/_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-000001", "_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>" ] } } ] } }
渗透器查询中的命名查询编辑
如果存储的渗透器查询是一个复杂查询,并且您想跟踪其哪些子查询匹配了渗透的文档,则可以使用其子查询的 \_name
参数。在这种情况下,在响应中,每个命中以及 _percolator_document_slot
字段都包含 _percolator_document_slot_<slotNumber>_matched_queries
字段,这些字段显示哪些子查询匹配了每个渗透的文档。
例如
response = client.index( index: 'my-index-000001', id: 5, refresh: true, body: { query: { bool: { should: [ { match: { message: { query: 'Japanese art', _name: 'query1' } } }, { match: { message: { query: 'Holand culture', _name: 'query2' } } } ] } } } ) puts response
PUT /my-index-000001/_doc/5?refresh { "query": { "bool": { "should": [ { "match": { "message": { "query": "Japanese art", "_name": "query1" } } }, { "match": { "message": { "query": "Holand culture", "_name": "query2" } } } ] } } }
GET /my-index-000001/_search { "query": { "percolate": { "field": "query", "documents": [ { "message": "Japanse art" }, { "message": "Holand culture" }, { "message": "Japanese art and Holand culture" }, { "message": "no-match" } ] } } }
{ "took": 55, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped" : 0, "failed": 0 }, "hits": { "total" : { "value": 1, "relation": "eq" }, "max_score": 1.1181908, "hits": [ { "_index": "my-index-000001", "_id": "5", "_score": 1.1181908, "_source": { "query": { "bool": { "should": [ { "match": { "message": { "query": "Japanese art", "_name": "query1" } } }, { "match": { "message": { "query": "Holand culture", "_name": "query2" } } } ] } } }, "fields" : { "_percolator_document_slot" : [0, 1, 2], "_percolator_document_slot_0_matched_queries" : ["query1"], "_percolator_document_slot_1_matched_queries" : ["query2"], "_percolator_document_slot_2_matched_queries" : ["query1", "query2"] } } ] } }
指定多个渗透查询编辑
可以在单个搜索请求中指定多个 percolate
查询
GET /my-index-000001/_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-000001", "_id": "1", "_score": 0.26152915, "_source": { "query": { "match": { "message": "bonsai tree" } } }, "fields" : { "_percolator_document_slot_query1" : [0] } } ] } }
幕后工作原理编辑
将文档索引到配置了 渗透器字段类型 映射的索引时,文档的查询部分将被解析为 Lucene 查询并存储到 Lucene 索引中。查询的二进制表示形式被存储,但查询的术语也被分析并存储到索引字段中。
在搜索时,请求中指定的文档会被解析成一个 Lucene 文档,并存储在一个内存中的临时 Lucene 索引中。这个内存索引只能保存这一个文档,并且针对这种情况进行了优化。之后,会根据内存索引中的词项构建一个特殊的查询,根据索引的查询词项选择候选的渗透查询。然后,如果这些查询实际匹配,则由内存索引对它们进行评估。
选择候选渗透查询匹配是执行 percolate
查询期间一项重要的性能优化,因为它可以显著减少内存索引需要评估的候选匹配数量。 percolate
查询之所以能够做到这一点,是因为在对渗透查询进行索引时,会提取查询词项,并使用渗透查询对它们进行索引。遗憾的是,渗透器无法从所有查询中提取词项(例如 wildcard
或 geo_shape
查询),因此在某些情况下,渗透器无法进行选择优化(例如,如果在布尔查询的必需子句中定义了不受支持的查询,或者不受支持的查询是渗透器文档中唯一的查询)。这些查询由渗透器标记,可以通过运行以下搜索找到
response = client.search( body: { query: { term: { 'query.extraction_result' => 'failed' } } } ) puts response
GET /_search { "query": { "term" : { "query.extraction_result" : "failed" } } }
上面的例子假设在映射中有一个类型为 percolator
的 query
字段。
考虑到渗透的设计,通常使用单独的索引来存储渗透查询和被渗透的文档是有意义的,而不是像我们在示例中那样使用单个索引。这种方法有几个好处
- 因为渗透查询包含与被渗透文档不同的字段集,所以使用两个独立的索引可以以更密集、更高效的方式存储字段。
- 渗透查询的扩展方式与其他查询不同,因此使用不同的索引配置(如主分片数)可能有利于渗透性能。
备注编辑
允许高开销查询编辑
如果 search.allow_expensive_queries
设置为 false,则不会执行渗透查询。