渗透查询编辑

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] 
        }
      }
    ]
  }
}

id 为 1 的查询与我们的文档匹配。

_percolator_document_slot 字段指示哪个文档与此查询匹配。在同时渗透多个文档时很有用。

参数编辑

渗透文档时需要以下参数

字段

保存索引查询的 percolator 类型的字段。这是一个必填参数。

名称

如果指定了多个 percolate 查询,则要用于 _percolator_document_slot 字段的后缀。这是一个可选参数。

文档

被渗透文档的来源。

文档

document 参数类似,但通过 json 数组接受多个文档。

文档类型

被渗透文档的类型/映射。此参数已弃用,将在 Elasticsearch 8.0 中删除。

除了指定被渗透文档的来源外,还可以从已存储的文档中检索来源。percolate 查询然后将在内部执行 get 请求以获取该文档。

在这种情况下,document 参数可以用以下参数代替

索引

文档所在的索引。这是一个必填参数。

类型

要获取的文档的类型。此参数已弃用,将在 Elasticsearch 8.0 中删除。

ID

要获取的文档的 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"
        }
      ]
    }
  }
}

documents 数组包含 4 个将同时渗透的文档。

{
  "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] 
        }
      }
    ]
  }
}

_percolator_document_slot 指示 percolate 查询中指定的第一个、第二个和最后一个文档与此查询匹配。

渗透现有文档编辑

为了渗透新索引的文档,可以使用 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" 
          }
        }
      ]
    }
  }
}

name 参数将用于标识哪些渗透器文档位置属于哪个 percolate 查询。

_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] 
        }
      }
    ]
  }
}

_percolator_document_slot_query1 渗透器位置字段指示这些匹配的位置来自 _name 参数设置为 query1percolate 查询。

幕后工作原理编辑

将文档索引到配置了 渗透器字段类型 映射的索引时,文档的查询部分将被解析为 Lucene 查询并存储到 Lucene 索引中。查询的二进制表示形式被存储,但查询的术语也被分析并存储到索引字段中。

在搜索时,请求中指定的文档会被解析成一个 Lucene 文档,并存储在一个内存中的临时 Lucene 索引中。这个内存索引只能保存这一个文档,并且针对这种情况进行了优化。之后,会根据内存索引中的词项构建一个特殊的查询,根据索引的查询词项选择候选的渗透查询。然后,如果这些查询实际匹配,则由内存索引对它们进行评估。

选择候选渗透查询匹配是执行 percolate 查询期间一项重要的性能优化,因为它可以显著减少内存索引需要评估的候选匹配数量。 percolate 查询之所以能够做到这一点,是因为在对渗透查询进行索引时,会提取查询词项,并使用渗透查询对它们进行索引。遗憾的是,渗透器无法从所有查询中提取词项(例如 wildcardgeo_shape 查询),因此在某些情况下,渗透器无法进行选择优化(例如,如果在布尔查询的必需子句中定义了不受支持的查询,或者不受支持的查询是渗透器文档中唯一的查询)。这些查询由渗透器标记,可以通过运行以下搜索找到

response = client.search(
  body: {
    query: {
      term: {
        'query.extraction_result' => 'failed'
      }
    }
  }
)
puts response
GET /_search
{
  "query": {
    "term" : {
      "query.extraction_result" : "failed"
    }
  }
}

上面的例子假设在映射中有一个类型为 percolatorquery 字段。

考虑到渗透的设计,通常使用单独的索引来存储渗透查询和被渗透的文档是有意义的,而不是像我们在示例中那样使用单个索引。这种方法有几个好处

  • 因为渗透查询包含与被渗透文档不同的字段集,所以使用两个独立的索引可以以更密集、更高效的方式存储字段。
  • 渗透查询的扩展方式与其他查询不同,因此使用不同的索引配置(如主分片数)可能有利于渗透性能。

备注编辑

允许高开销查询编辑

如果 search.allow_expensive_queries 设置为 false,则不会执行渗透查询。

使用自定义相似度编辑

渗透查询不会考虑任何配置的 自定义相似度。它们始终使用默认的 Lucene 相似度。