掌握 ElasticSearch 精准查询:Term Query 与 Filter 详解_elasticsearch term
掌握 ElasticSearch 精准查询:Term Query 与 Filter 详解
- 
- 一、引言 (Introduction)
 - 二、准备工作:创建索引和添加示例数据
 - 三、Term Query:精准匹配
 - 
- 3.1 `term` 查询:单个值的精准匹配
 - 3.2 `terms` 查询:多个值的精准匹配
 - 3.3 `term` vs. `match_phrase`
 
 - 四、Filter:高效过滤
 - 
- 4.1 什么是 Filter?
 - 4.2 Query vs. Filter
 
 - 5. 结合使用 Term 和 Filter
 - 六、总结 (Conclusion)
 
 
一、引言 (Introduction)
在信息检索的世界里,我们常常面临两种不同但又互补的需求:
- 
全文检索 (Full-text Search): 就像你在 Google 或百度中输入一个关键词,搜索引擎会返回一系列相关的网页。这种搜索方式关注的是文档与查询之间的 相关性,它会考虑词频、词的位置等因素,对结果进行排序。Elasticsearch 中的
match查询(如上一篇博客所述)就是典型的全文检索方式。 - 
精准查询 (Exact Value Search): 想象一下,你正在一个电商网站上浏览商品,你只想看 “在售” 状态的商品,或者只想找 ID 为 “12345” 的特定商品。这种情况下,你关心的不是商品与查询的 相关程度,而是商品是否 完全符合 你的要求。这就是精准查询的用武之地。
 
Elasticsearch 作为一款强大的搜索引擎,不仅擅长全文检索,也提供了强大的精准查询功能。在本文中,我们将深入探讨两种核心的精准查询方式:Term Query 和 Filter。
- Term Query: 用于查找某个字段的值与查询值 完全相等 的文档。它不会对查询值进行分词,而是直接进行精确匹配。
 - Filter: 用于筛选符合特定条件的文档,但 不计算相关性得分。它只关心文档是否匹配条件,不关心匹配程度,因此通常比计算得分的查询(如 
match)更高效。 
通过本文,你将掌握 Term Query 和 Filter 的基本概念、用法、区别以及它们在实际应用中的价值。
二、准备工作:创建索引和添加示例数据
在开始学习查询之前,我们需要先创建一个索引并添加一些示例数据。请确保你已经安装并启动了 Elasticsearch 7.10。推荐使用 Kibana 的 Dev Tools 来执行以下操作。
- 
创建索引
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): 产品发布日期。
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\" } } }} - 
添加示例数据:
我们使用
_bulkAPI 批量添加一些产品数据: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 查询 不会 对查询值进行分词,而是直接将其作为一个整体进行匹配。
语法:
GET index/_search{ \"query\": { \"term\": { \"field_name\": { \"value\": \"your_exact_value\" } } }}
参数说明:
field_name: 要搜索的字段名。value: 要匹配的精确值。
示例:
- 
查找
product_id为 “12345” 的产品:GET products/_search{ \"query\": { \"term\": { \"product_id\": { \"value\": \"12345\" } } }}结果解释: 根据我们添加的数据,这个查询将返回
product_id为 “12345” 的那一条文档。 - 
查找
status为 “in_stock” 的产品:GET products/_search{ \"query\": { \"term\": { \"status\": { \"value\": \"in_stock\" } } }}结果解释: 这个查询将返回所有
status字段值为 “in_stock” 的产品文档。 
Code 运行结果
{ \"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 操作符。
语法:
GET index/_search{ \"query\": { \"terms\": { \"field_name\": [\"value1\", \"value2\", \"value3\"] } }}
field_name: 要搜索的字段名。[]: 一个包含多个值的数组,表示要匹配的多个精确值。
示例:
- 
查找
category为 “electronics” 或 “appliances” 的产品: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 运行结果
{ ... \"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参数调整假设我们有一个索引 my_index,其中有一个 description 字段,类型为 text。我们向该索引添加一个文档,其 description 值为 “The quick brown fox jumps over the lazy dog”。
- 
准备数据:
 
PUT my_index
 {
 “mappings”: {
 “properties”: {
 “description”: {
 “type”: “text”
 }
 }
 }
 }
POST my_index/_doc
 {
 “description”: “The quick brown fox jumps over the lazy dog”
 }
 ```
- 使用 
term查询: 
- 
示例 1:查询 “quick brown”
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”
GET my_index/_search{ \"query\": { \"term\": { \"description\": { \"value\": \"quick\" } } }}结果: 这个查询 会 返回包含 “quick” 作为分词结果的文档。因为 “quick” 是
description字段分词后的一个词项。 
- 使用 
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”
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)。
GET _search{ \"query\": { \"constant_score\": { \"filter\": { \"term\": {  \"status\": \"in_stock\" } } } }}
参数说明:
constant_score: 将 filter 查询包装成为一个不计算分数的查询。filter: 包含具体的过滤条件。在filter内部,你可以使用各种查询,如term、terms、range、exists、bool等,就像在普通的query中一样。
示例:
- 
使用
termFilter 筛选status为 “in_stock” 的产品:GET products/_search{ \"query\": { \"constant_score\": { \"filter\": { \"term\": { \"status\": \"in_stock\" } } } }}结果解释: 这个查询将返回所有
status为 “in_stock” 的产品,但所有返回文档的_score都将是 1.0(或你在constant_score中指定的其他值)。 - 
使用
rangeFilter 筛选price在 100 到 300 之间的产品:GET products/_search{ \"query\": { \"constant_score\": { \"filter\": { \"range\": { \"price\": { \"gte\": 100, \"lte\": 300 } } } } }} - 
使用
termsFilter 筛选category为 “electronics” 或 “clothing” 的产品: 
GET products/_search{ \"query\": { \"constant_score\": { \"filter\": { \"terms\": { \"category\": [\"electronics\", \"clothing\"] } } } }}
4.2 Query vs. Filter
为了更好地理解 Filter 的作用和优势,我们将它与 Query 进行对比:
_score)。match 查询搜索包含特定关键词的文档)。何时使用 Filter?
- 当你只关心文档是否匹配过滤条件,而 不关心 匹配程度(相关性得分)时。
 - 当你需要对结果进行 过滤,并且过滤条件 不影响 文档的排序时。
 - 当你需要 提高查询性能 时,特别是对于经常使用的过滤条件,Filter 的缓存机制可以带来显著的性能提升。
 
何时使用 Query?
- 当你需要根据 相关性得分 对文档进行 排序 时。
 - 当你需要执行 全文检索,并且查询条件 会影响 文档的排序时(例如,使用 
match查询搜索包含特定关键词的文档)。 
在实际应用中,Query 和 Filter 经常 结合使用。例如,你可以使用 Query 来查找与关键词相关的文档,然后使用 Filter 来过滤出符合特定条件的文档。
5. 结合使用 Term 和 Filter
在实际应用中,我们经常需要将 Term 查询与其他查询或过滤器结合起来,以构建更复杂的查询逻辑。Filter 尤其适合与 Term 查询结合,因为它们都关注精确匹配,并且 Filter 可以提高查询效率。
示例:
假设我们需要找到 products 索引中所有类别为 “electronics” 且价格在 200 到 600 之间的在售产品。我们可以结合使用 term、range 和 bool 查询,并将 range 查询放在 filter 子句中:
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子句中,它不会影响文档的得分,只起到过滤作用。 - 最终返回的结果是同时满足 
must和filter条件的文档。 
关于 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)_score)最佳实践:
- 对于精确匹配的场景,优先使用 Term Query 和 Filter。
 - 对于不需要相关性得分的过滤,使用 Filter。
 - 结合使用 Query 和 Filter,构建复杂的查询逻辑(可以使用 
bool查询,我们将在下一章节详细介绍)。 - 充分利用 Filter 的缓存机制,提高查询效率。
 
希望通过本文,你已经对 Elasticsearch 中的 Term Query 和 Filter 有了深入的理解。在下一章节中,我们将深入探讨 bool 查询,学习如何构建更复杂的查询组合。


