掌握 ElasticSearch 精准查询:Term Query 与 Filter 详解

掌握 ElasticSearch 精准查询:Term Query 与 Filter 详解

一、引言 (Introduction)

在信息检索的世界里,我们常常面临两种不同但又互补的需求:

  1. 全文检索 (Full-text Search): 就像你在 Google 或百度中输入一个关键词,搜索引擎会返回一系列相关的网页。这种搜索方式关注的是文档与查询之间的 相关性 ,它会考虑词频、词的位置等因素,对结果进行排序。Elasticsearch 中的 match 查询(如上一篇博客所述)就是典型的全文检索方式。

  2. 精准查询 (Exact Value Search): 想象一下,你正在一个电商网站上浏览商品,你只想看 "在售" 状态的商品,或者只想找 ID 为 "12345" 的特定商品。这种情况下,你关心的不是商品与查询的 相关程度 ,而是商品是否 完全符合 你的要求。这就是精准查询的用武之地。

Elasticsearch 作为一款强大的搜索引擎,不仅擅长全文检索,也提供了强大的精准查询功能。在本文中,我们将深入探讨两种核心的精准查询方式:Term QueryFilter

  • Term Query: 用于查找某个字段的值与查询值 完全相等 的文档。它不会对查询值进行分词,而是直接进行精确匹配。
  • Filter: 用于筛选符合特定条件的文档,但 不计算相关性得分 。它只关心文档是否匹配条件,不关心匹配程度,因此通常比计算得分的查询(如 match)更高效。

通过本文,你将掌握 Term Query 和 Filter 的基本概念、用法、区别以及它们在实际应用中的价值。

二、准备工作:创建索引和添加示例数据

在开始学习查询之前,我们需要先创建一个索引并添加一些示例数据。请确保你已经安装并启动了 Elasticsearch 7.10。推荐使用 Kibana 的 Dev Tools 来执行以下操作。

  1. 创建索引 products:

    我们创建一个名为 products 的索引,其中包含以下字段:

    • product_id (keyword): 产品ID,不分词。
    • status (keyword): 产品状态(如 "in_stock", "out_of_stock", "discontinued"),不分词。
    • category (keyword): 产品类别(如 "electronics", "clothing", "books"),不分词。
    • price (double): 产品价格。
    • in_stock (boolean): 是否有库存。
    • launch_date (date): 产品发布日期。
    json 复制代码
    PUT products
    {
      "mappings": {
        "properties": {
          "product_id": {
            "type": "keyword"
          },
          "status": {
            "type": "keyword"
          },
          "category": {
            "type": "keyword"
          },
          "price": {
            "type": "double"
          },
          "in_stock": {
            "type": "boolean"
          },
          "launch_date": {
            "type": "date"
          }
        }
      }
    }
  2. 添加示例数据:

    我们使用 _bulk API 批量添加一些产品数据:

    json 复制代码
    POST products/_bulk
    {"index":{"_index": "products"}}
    {"product_id": "12345", "status": "in_stock", "category": "electronics", "price": 299.99, "in_stock": true, "launch_date": "2023-01-15"}
    {"index":{"_index": "products"}}
    {"product_id": "67890", "status": "out_of_stock", "category": "clothing", "price": 49.99, "in_stock": false, "launch_date": "2023-03-10"}
    {"index":{"_index": "products"}}
    {"product_id": "13579", "status": "in_stock", "category": "books", "price": 19.99, "in_stock": true, "launch_date": "2023-05-20"}
    {"index":{"_index": "products"}}
    {"product_id": "24680", "status": "discontinued", "category": "electronics", "price": 199.99, "in_stock": false, "launch_date": "2022-11-01"}
    {"index":{"_index": "products"}}
    {"product_id": "11223", "status": "in_stock", "category": "electronics", "price": 599.99, "in_stock": true, "launch_date": "2023-08-01"}
    {"index":{"_index": "products"}}
    {"product_id": "33445", "status": "in_stock", "category": "clothing", "price": 79.99, "in_stock": true, "launch_date": "2023-07-15"}

三、Term Query:精准匹配

3.1 term 查询:单个值的精准匹配

基本概念: term 查询是 Elasticsearch 中最基本的精准查询方式。它用于查找指定字段的值与查询值 完全相等 的文档。需要特别注意的是,term 查询 不会 对查询值进行分词,而是直接将其作为一个整体进行匹配。

语法:

json 复制代码
GET index/_search
{
  "query": {
    "term": {
      "field_name": {
        "value": "your_exact_value"
      }
    }
  }
}

参数说明:

  • field_name: 要搜索的字段名。
  • value: 要匹配的精确值。

示例:

  • 查找 product_id 为 "12345" 的产品:

    json 复制代码
    GET products/_search
    {
      "query": {
        "term": {
          "product_id": {
            "value": "12345"
          }
        }
      }
    }

    结果解释: 根据我们添加的数据,这个查询将返回 product_id 为 "12345" 的那一条文档。

  • 查找 status 为 "in_stock" 的产品:

    json 复制代码
    GET products/_search
    {
      "query": {
        "term": {
          "status": {
            "value": "in_stock"
          }
        }
      }
    }

    结果解释: 这个查询将返回所有 status 字段值为 "in_stock" 的产品文档。

Code 运行结果

json 复制代码
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 4,
      "relation" : "eq"
    },
    "max_score" : 0.44183272,
    "hits" : [
      {
        "_index" : "products",
        "_type" : "_doc",
        "_id" : "PjDrMJUBaTLipzfiYli6",
        "_score" : 0.44183272,
        "_source" : {
          "product_id" : "12345",
          "status" : "in_stock",
          "category" : "electronics",
          "price" : 299.99,
          "in_stock" : true,
          "launch_date" : "2023-01-15"
        }
      },
      {
        "_index" : "products",
        "_type" : "_doc",
        "_id" : "QDDrMJUBaTLipzfiYli6",
        "_score" : 0.44183272,
        "_source" : {
          "product_id" : "13579",
          "status" : "in_stock",
          "category" : "books",
          "price" : 19.99,
          "in_stock" : true,
          "launch_date" : "2023-05-20"
        }
      },
      {
        "_index" : "products",
        "_type" : "_doc",
        "_id" : "QjDrMJUBaTLipzfiYli6",
        "_score" : 0.44183272,
        "_source" : {
          "product_id" : "11223",
          "status" : "in_stock",
          "category" : "electronics",
          "price" : 599.99,
          "in_stock" : true,
          "launch_date" : "2023-08-01"
        }
      },
      {
        "_index" : "products",
        "_type" : "_doc",
        "_id" : "QzDrMJUBaTLipzfiYli6",
        "_score" : 0.44183272,
        "_source" : {
          "product_id" : "33445",
          "status" : "in_stock",
          "category" : "clothing",
          "price" : 79.99,
          "in_stock" : true,
          "launch_date" : "2023-07-15"
        }
      }
    ]
  }
}

重要提示:

  • term 查询对 keyword 类型的字段效果最好,因为 keyword 字段不会被分词。
  • 如果你对一个 text 类型的字段使用 term 查询,很可能得不到你想要的结果 。因为 text 字段在索引时会被分词,而 term 查询不会对查询值分词。

3.2 terms 查询:多个值的精准匹配

基本概念:

terms 查询是 term 查询的扩展,它允许你指定一个值的列表,只要文档的指定字段与列表中的 任意一个 值完全匹配,该文档就会被返回。这相当于 SQL 中的 IN 操作符。

语法:

json 复制代码
GET index/_search
{
  "query": {
    "terms": {
      "field_name": ["value1", "value2", "value3"]
    }
  }
}
  • field_name: 要搜索的字段名。
  • []: 一个包含多个值的数组,表示要匹配的多个精确值。

示例:

  • 查找 category 为 "electronics" 或 "appliances" 的产品:

    json 复制代码
    GET products/_search
    {
      "query": {
        "terms": {
          "category": ["electronics", "clothing"]
        }
      }
    }

    结果解释: 这个查询将返回所有 category 字段值为 "electronics" 或 "clothing" 的产品文档。根据我们的示例数据:

    • product_id 为 "12345"、"24680" 和 "11223" 的产品 (category 为 "electronics")
    • product_id 为 "67890" 和 "33445" 的产品 (category 为 "clothing")

    都会被返回。

Code 运行结果

json 复制代码
{
 ...
  "hits" : {
    "total" : {
      "value" : 5,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "products",
        "_type" : "_doc",
        "_id" : "9WhxJpUBFTEr5wdT-FfA",
        "_score" : 1.0,
        "_source" : {
          "product_id" : "12345",
          "status" : "in_stock",
          "category" : "electronics",
          "price" : 299.99,
          "in_stock" : true,
          "launch_date" : "2023-01-15"
        }
      },
      {
        "_index" : "products",
        "_type" : "_doc",
        "_id" : "-GhxJpUBFTEr5wdT-FfA",
        "_score" : 1.0,
        "_source" : {
          "product_id" : "11223",
          "status" : "in_stock",
          "category" : "electronics",
          "price" : 599.99,
          "in_stock" : true,
          "launch_date" : "2023-08-01"
        }
      },
      {
        "_index" : "products",
        "_type" : "_doc",
        "_id" : "9mhxJpUBFTEr5wdT-FfA",
        "_score" : 1.0,
        "_source" : {
          "product_id" : "67890",
          "status" : "out_of_stock",
          "category" : "clothing",
          "price" : 49.99,
          "in_stock" : false,
          "launch_date" : "2023-03-10"
        }
      },
      {
        "_index" : "products",
        "_type" : "_doc",
        "_id" : "-WhxJpUBFTEr5wdT-FfA",
        "_score" : 1.0,
        "_source" : {
          "product_id" : "24680",
          "status" : "discontinued",
          "category" : "electronics",
          "price" : 199.99,
          "in_stock" : false,
          "launch_date" : "2022-11-01"
        }
      },
      {
        "_index" : "products",
        "_type" : "_doc",
        "_id" : "-mhxJpUBFTEr5wdT-FfA",
        "_score" : 1.0,
        "_source" : {
          "product_id" : "33445",
          "status" : "in_stock",
          "category" : "clothing",
          "price" : 79.99,
          "in_stock" : true,
          "launch_date" : "2023-07-15"
        }
      }
    ]
  }
}

3.3 term vs. match_phrase

为了更好地理解 term 查询的特性,我们将其与上一篇博客中介绍的 match_phrase 查询进行对比:

特性 term 查询 match_phrase 查询
查询类型 精准查询 全文检索
分词 不对查询值分词 对查询值分词
匹配要求 字段值与查询值完全相等 所有查询词项都必须出现,顺序和邻近度(默认情况下)必须与查询字符串完全一致。可以通过slop参数调整
适用场景 查找与特定值完全匹配的文档(如 ID、状态码) 查找包含特定短语的文档,且对短语中词项的顺序和邻近度有要求

假设我们有一个索引 my_index,其中有一个 description 字段,类型为 text。我们向该索引添加一个文档,其 description 值为 "The quick brown fox jumps over the lazy dog"。

  1. 准备数据:

    json 复制代码

PUT my_index

{

"mappings": {

"properties": {

"description": {

"type": "text"

}

}

}

}

POST my_index/_doc

{

"description": "The quick brown fox jumps over the lazy dog"

}

```

  1. 使用 term 查询:
  • 示例 1:查询 "quick brown"

    json 复制代码
    GET my_index/_search
    {
      "query": {
        "term": {
          "description": {
            "value": "quick brown"
          }
        }
      }
    }

    结果: 这个查询很 不会 返回任何结果(默认分词器下 )。因为 term 查询不会对 "quick brown" 进行分词,而 description 字段在索引时已经被分词为 "the", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog" 等词项。没有一个词项与 "quick brown" 完全相等。

  • 示例 2:查询 "quick"

    json 复制代码
    GET my_index/_search
    {
      "query": {
        "term": {
          "description": {
            "value": "quick"
          }
        }
      }
    }

    结果: 这个查询 返回包含 "quick" 作为分词结果的文档。因为 "quick" 是 description 字段分词后的一个词项。

  1. 使用 match_phrase 查询:
  • 示例 1:查询 "quick brown"

    JSON

    复制代码
    GET my_index/_search
    {
      "query": {
        "match_phrase": {
          "description": "quick brown"
        }
      }
    }

    结果: 这个查询 返回包含 "quick brown" 这个短语的文档。因为 match_phrase 查询会对 "quick brown" 分词,然后要求这两个词项按顺序相邻出现。

  • 示例 2:查询 "brown fox jumps"

    json 复制代码
    GET my_index/_search
    {
      "query": {
        "match_phrase": {
          "description": "brown fox jumps"
        }
      }
    }

    结果: 这个查询也会返回文档,因为 "brown", "fox", "jumps" 三个词项按照顺序相邻出现。

  • term 查询适用于 keyword 类型字段的精确匹配。

  • 对于 text 类型字段,term 查询匹配的是分词后的单个词项,而不是整个字段值。

  • match_phrase 查询适用于 text 类型字段的短语匹配,要求词项顺序和邻近度。

四、Filter:高效过滤

4.1 什么是 Filter?

基本概念:

  • Filter(过滤器)是 Elasticsearch 中一种特殊的查询方式,它用于筛选符合特定条件的文档,但 不计算相关性得分_score)。

  • Filter 的核心思想是 结果导向 ,它只关心文档是否 匹配 过滤条件,而不关心文档与查询的 相关程度

  • 由于不计算得分,Filter 通常比计算得分的查询(如 match)更 高效 。此外,Elasticsearch 会自动 缓存 Filter 的结果,进一步提高查询性能。

语法:

Filter 通常与 constant_score 查询结合使用。constant_score 查询会将 Filter 包装起来,并为所有匹配的文档赋予一个固定的得分(默认为 1.0)。

json 复制代码
GET _search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": { 
          "status": "in_stock"
        }
      }
    }
  }
}

参数说明:

  • constant_score: 将 filter 查询包装成为一个不计算分数的查询。
  • filter: 包含具体的过滤条件。在 filter 内部,你可以使用各种查询,如 termtermsrangeexistsbool 等,就像在普通的 query 中一样。

示例:

  • 使用 term Filter 筛选 status 为 "in_stock" 的产品:

    json 复制代码
    GET products/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "term": {
              "status": "in_stock"
            }
          }
        }
      }
    }

    结果解释: 这个查询将返回所有 status 为 "in_stock" 的产品,但所有返回文档的 _score 都将是 1.0(或你在 constant_score 中指定的其他值)。

  • 使用 range Filter 筛选 price 在 100 到 300 之间的产品:

    json 复制代码
    GET products/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "range": {
              "price": {
                "gte": 100,
                "lte": 300
              }
            }
          }
        }
      }
    }
  • 使用 terms Filter 筛选 category 为 "electronics" 或 "clothing" 的产品:

json 复制代码
GET products/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "terms": {
          "category": ["electronics", "clothing"]
        }
      }
    }
  }
}

4.2 Query vs. Filter

为了更好地理解 Filter 的作用和优势,我们将它与 Query 进行对比:

特性 Query Filter
核心思想 过程导向:关心文档与查询的 相关程度 ,计算相关性得分(_score)。 结果导向:只关心文档是否 匹配 过滤条件,不计算得分。
性能 通常较慢,因为需要计算得分。 通常较快,因为不计算得分,且结果可以被缓存。
缓存 默认情况下不缓存结果。 自动缓存结果,提高查询效率。
使用场景 当你需要根据相关性得分对文档进行排序时。 当你只关心文档是否匹配,不关心匹配程度,且过滤条件不影响文档的排序时。
当你需要执行全文检索,且查询条件会影响文档的排序时(例如,使用 match 查询搜索包含特定关键词的文档)。 当你需要对结果进行过滤,且过滤条件不影响文档的排序时(例如,筛选特定状态、类别或范围的文档)。

何时使用 Filter?

  • 当你只关心文档是否匹配过滤条件,而 不关心 匹配程度(相关性得分)时。
  • 当你需要对结果进行 过滤 ,并且过滤条件 不影响 文档的排序时。
  • 当你需要 提高查询性能 时,特别是对于经常使用的过滤条件,Filter 的缓存机制可以带来显著的性能提升。

何时使用 Query?

  • 当你需要根据 相关性得分 对文档进行 排序 时。
  • 当你需要执行 全文检索 ,并且查询条件 会影响 文档的排序时(例如,使用 match 查询搜索包含特定关键词的文档)。

在实际应用中,Query 和 Filter 经常 结合使用。例如,你可以使用 Query 来查找与关键词相关的文档,然后使用 Filter 来过滤出符合特定条件的文档。

5. 结合使用 Term 和 Filter

在实际应用中,我们经常需要将 Term 查询与其他查询或过滤器结合起来,以构建更复杂的查询逻辑。Filter 尤其适合与 Term 查询结合,因为它们都关注精确匹配,并且 Filter 可以提高查询效率。

示例:

假设我们需要找到 products 索引中所有类别为 "electronics" 且价格在 200 到 600 之间的在售产品。我们可以结合使用 termrangebool 查询,并将 range 查询放在 filter 子句中:

json 复制代码
GET products/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "category": "electronics"
          }
        },
        {
          "term": {
            "status": "in_stock"
          }
        }
      ],
      "filter": [
        {
          "range": {
            "price": {
              "gte": 200,
              "lte": 600
            }
          }
        }
      ]
    }
  }
}

结果解释:

  • bool 查询:用于组合多个查询子句。我们将在下一节详细学习 bool 查询。
    • must 子句:表示必须匹配的条件。这里我们使用了两个 term 查询,要求 category 为 "electronics" 且 status 为 "in_stock"。
    • filter 子句:表示过滤条件,不影响评分。这里我们使用了一个 range 查询,要求 price 在 200 到 600 之间。
  • 由于 range 查询位于 filter 子句中,它不会影响文档的得分,只起到过滤作用。
  • 最终返回的结果是同时满足 mustfilter 条件的文档。

关于 bool 查询的进一步说明:

在上面的示例中,我们使用了 bool 查询来组合 Query 和 Filter。bool 查询提供了一种灵活的方式来组合多个查询子句:

  • must 类似于"与" (AND) 关系,要求所有子句都必须匹配。子句可以是 Query 或 Filter。
  • filter 用于放置 Filter 子句,这些子句不影响评分,只进行过滤。
  • should 类似于"或" (OR) 关系,至少有一个子句匹配即可。子句可以是 Query 或 Filter。
  • must_not 类似于"非" (NOT) 关系,要求所有子句都不匹配。子句可以是 Query 或 Filter。

通过灵活组合 bool 查询的这四个子句,我们可以构建出非常复杂的查询逻辑,同时利用 Filter 来提高查询效率。我们将在下一章节详细介绍 bool 查询的用法和更多高级特性。

六、总结 (Conclusion)

在本文中,我们深入探讨了 ElasticSearch 7.10 中的两种核心精准查询方式:Term Query 和 Filter。

  • Term Query:
    • 用于查找某个字段的值与查询值 完全相等 的文档。
    • 不对查询值进行分词,直接进行精确匹配。
    • 适用于 keyword 类型字段的精确匹配。
    • terms 查询是 term 查询的扩展,允许指定多个值进行匹配。
    • range查询允许进行范围查询
  • Filter:
    • 用于筛选符合特定条件的文档,但 不计算相关性得分
    • 结果导向,只关心文档是否匹配,不关心匹配程度。
    • 通常比计算得分的查询更 高效 ,且结果可以被 缓存
    • 常与 constant_score 查询结合使用。

全文检索 vs. 精准查询:

特性 全文检索 (如 match) 精准查询 (如 term, Filter)
关注点 文档与查询的 相关性 文档是否 完全符合 条件
分词 对查询值进行分词 不对查询值分词 (Term Query)
得分 计算相关性得分 (_score) 不计算得分 (Filter) 或固定得分 (constant_score)
适用场景 查找与关键词 相关 的文档 查找与特定值 完全匹配 的文档,或进行数据过滤
性能 相对较低,因为需要计算得分 相对较高,因为不计算得分,且 Filter 可缓存

最佳实践:

  • 对于精确匹配的场景,优先使用 Term Query 和 Filter。
  • 对于不需要相关性得分的过滤,使用 Filter。
  • 结合使用 Query 和 Filter,构建复杂的查询逻辑(可以使用 bool 查询,我们将在下一章节详细介绍)。
  • 充分利用 Filter 的缓存机制,提高查询效率。

希望通过本文,你已经对 Elasticsearch 中的 Term Query 和 Filter 有了深入的理解。在下一章节中,我们将深入探讨 bool 查询,学习如何构建更复杂的查询组合。

相关推荐
Wnq100721 小时前
养猪场巡检机器人的设计与应用研究
大数据·人工智能·数据挖掘·机器人·巡检机器人·北京玉麟科技巡检机器人
StarRocks_labs9 小时前
从InfluxDB到StarRocks:Grab实现Spark监控平台10倍性能提升
大数据·数据库·starrocks·分布式·spark·iris·物化视图
若兰幽竹10 小时前
【Spark分析HBase数据】Spark读取并分析HBase数据
大数据·spark·hbase
R²AIN SUITE10 小时前
金融合规革命:R²AIN SUITE 如何重塑银行业务智能
大数据·人工智能
绿算技术11 小时前
“强强联手,智启未来”凯创未来与绿算技术共筑高端智能家居及智能照明领域新生态
大数据·人工智能·智能家居
只因只因爆12 小时前
spark的缓存
大数据·缓存·spark
Leo.yuan13 小时前
3D 数据可视化系统是什么?具体应用在哪方面?
大数据·数据库·3d·信息可视化·数据分析
只因只因爆14 小时前
spark小任务
大数据·分布式·spark
cainiao08060514 小时前
Java 大视界——Java 大数据在智慧交通智能停车诱导系统中的数据融合与实时更新
java·大数据·开发语言
End92817 小时前
Spark之搭建Yarn模式
大数据·分布式·spark