当前位置: 首页 > 工具软件 > Percolator > 使用案例 >

Elasticsearch:理解 Elasticsearch 中的 Percolator 数据类型及 Percolate 查询

祁飞飙
2023-12-01

Elasticsearch 是功能强大的功能丰富的工具。在今天的文章中,我来介绍一下 Percolator 数据类型。同时也介绍一下 Percolate query。 您需要基本了解 Elasticsearch,尤其是映射搜索

概念

Elasticsearch 的正常工作流程是将文档(作为JSON数据)存储在索引中,并执行搜索(也是JSON数据)以向索引询问有关这些文档的信息。简而言之,Percolate 可以逆转这种情况。 你存储 search 并使用文档询问有关这些搜索的索引。 是的,但这并不是特别可行的信息。 多年来,如何构造 percolator,以至于我们可以给出更有用的解释。


现在,Percolation 围绕 percolator 映射字段类型展开。 与其他任何字段类型一样,不同的是它希望你将搜索文档分配为该值。 当你存储数据时,索引会将此搜索文档处理为可执行形式,并将其保存以备后用。


Percolate query 接受一个或多个文档,并将结果限制为那些存储的搜索至少匹配一个文档的文档。 搜索时,渗滤查询的工作原理与任何其他查询元素一样。

更深入的理解

在底层,这是以你期望的方式实现的:具有 percolate 字段的索引保留一个隐藏的(内存中)索引。首先,将在 percolate query 中列出的文档放入该索引,然后对该索引执行常规查询,以查看原始的含 percolate 字段的文档是否匹配。


要记住的重要一点是,该隐藏索引是从原始 percolator 索引获取其映射的。因此,用于 percolate query 的索引需要具有适合原始数据和查询文档数据的映射。


这引入了一些管理问题,因为你的索引数据和 percolate query 文档可能以不同的方式使用同一字段。一个简单的答案是使用对象类型 (object type) 将与 percolate 相关的映射与普通文档映射隔分开。


假设你使用的查询最初是为实际文档的另一个索引编写的,那么最直接的方法是将数据隔离衣避免数据直接写到 percolate 索引中去,并将根级别传递给 percolate query文档的映射定义。


另外,由于渗滤字段被解析为搜索并在索引时保存,因此你可能需要在升级后重新索引 Percolate 文档,以利用对系统的任何优化。

一个例子

在此示例中,我们将建立一个索引,该索引含有保存的玩具名字和玩具价格搜索。 其背后的想法是,用户应该能够输入搜索字词和最高价格,然后在与该字词匹配的商品价格低于此价格时立即得到通知。 用户还应该能够打开和关闭这些通知。下面的映射实现了 percolate 索引来支持此功能。 与保存的搜索本身相关的字段位于搜索对象中,而与原始玩具相关的字段位于映射的根级别。

首先,我们使用如下的命令来生产一个索引:

PUT toys
{
  "mappings": {
    "properties": {
      "search": {
        "properties": {
          "query": {
            "type": "percolator"
          },
          "user_id": {
            "type": "integer"
          },
          "enabled": {
            "type": "boolean"
          }
        }
      },
      "price": {
        "type": "float"
      },
      "description": {
        "type": "text"
      }
    }
  }
}

我们接着使用如下的命令写入一个文档:

PUT toys/_doc/1
{
  "search": {
    "user_id": 5,
    "enabled": true,
    "query": {
      "bool": {
        "filter": [
          {
            "match": {
              "description": {
                "query": "nintendo switch"
              }
            }
          },
          {
            "range": {
              "price": {
                "lte": 300
              }
            }
          }
        ]
      }
    }
  }
}

请注意,我们仅将数据存储在 search 对象字段中。 price 和 description 的映射仅用于支持 percolate query。
在查询时,我们要同时使用普通对象字段和“特殊” percolator 字段。 此查询将在用户搜索中检查是否有当前启用的搜索与文档匹配。

GET toys/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "percolate": {
            "field": "search.query",
            "document": {
              "description": "Nintendo Switch",
              "price": 250
            }
          }
        },
        {
          "term": {
            "search.enabled": true
          }
        },
        {
          "term": {
            "search.user_id": 5
          }
        }
      ]
    }
  }
}  

请注意,它结合了针对字段中存储的查询和常规术语查询的文档的 percolator 匹配,以限制基于其启用状态和 user_id 测试的文档。运行上面的指令后,我们可以看到如下的结果:

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.0,
    "hits" : [
      {
        "_index" : "toys",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.0,
        "_source" : {
          "search" : {
            "user_id" : 5,
            "enabled" : true,
            "query" : {
              "bool" : {
                "filter" : [
                  {
                    "match" : {
                      "description" : {
                        "query" : "nintendo switch"
                      }
                    }
                  },
                  {
                    "range" : {
                      "price" : {
                        "lte" : 300
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "fields" : {
          "_percolator_document_slot" : [
            0
          ]
        }
      }
    ]
  }
}

如果我们该用如下的搜索:

GET toys/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "percolate": {
            "field": "search.query",
            "document": {
              "description": "Nintendo Switch",
              "price": 500
            }
          }
        },
        {
          "term": {
            "search.enabled": true
          }
        },
        {
          "term": {
            "search.user_id": 5
          }
        }
      ]
    }
  }
}  

我们将找不到任何的结果:

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}

另外一个例子

我们想创建一个 percolator 来匹配 body 字段中的一些文本。 我们可以像这样定义映射:

PUT test-percolator
{
  "mappings": {
    "properties": {
      "query": {
        "type": "percolator"
      },
      "body": {
        "type": "text"
      }
    }
  }
}

现在,我们可以在其中存储一个带有过 percolator 查询的文档,如下所示:

PUT test-percolator/_doc/1?refresh
{
  "query": {
    "match": {
      "body": "quick brown fox"
    }
  }
}

现在,让我们对其执行搜索,如以下代码所示:

GET test-percolator/_search?filter_path=**.hits
{
  "query": {
    "percolate": {
      "field": "query",
      "document": {
        "body": "fox jumps over the lazy dog"
      }
    }
  }
}

这将导致我们检索存储文档的命中,如下所示:

{
  "hits" : {
    "hits" : [
      {
        "_index" : "test-percolator",
        "_id" : "1",
        "_score" : 0.13076457,
        "_source" : {
          "query" : {
            "match" : {
              "body" : "quick brown fox"
            }
          }
        },
        "fields" : {
          "_percolator_document_slot" : [
            0
          ]
        }
      }
    ]
  }
}

Percolator 字段在其中存储了一个 Elasticsearch 查询。 因为所有的 percolator 都被缓存并且对于性能总是处于活动状态,所以查询中需要的所有字段都必须在文档的映射中定义。由于所有 percolator 文档中的所有查询都将针对每个文档执行,为了获得最佳性能,必须优化 Percolator 内部的查询,以便它们在 percolator 查询中快速执行。

到现在为止,可能很多人也不一定知道到底为什么要用到这个 percolate query。在实际的使用中,我们可以在 Logstash 的 Elasticsearch 过滤器中针对每一个事件来针对 Elasticsearch 做 query。也即,我们可以知道这个事件是否满足其中的 search,如果是,将会丰富事件的数据。这个练习就留给你们来试。

参考:

【1】Elasticsearch Data Enrichment with Logstash: A Few Security Examples | Elastic Blog

 类似资料: