ElasticSearch之TermQuery和MatchQuery

在日常使用ES的时候,经常会将TermQuery和MatchQuery混淆,不知道该使用什么查询。本文举例说明。

一、TermQuery

Term是表达语意的最小单位。搜索和利用统计语言模型进行自然语言处理都需要处理Term

特点:

  1. Term Query主要包括:Term Query、Range Query、Exists Query、Prefix Query、Wildcard Query
  2. 在ES中,Term查询,对输入不做分词处理。会将输入作为一个整体,基于这个整体在倒排索引中查找准确的词项,并且使用相关度算分公式为每个包含该词项的文档进行相关度算分。
  3. 可以通过Constant Score 将查询转换成一个Filtering,避免算分,利用缓存,提高性能

首先我们先创建索引并准备三条数据

put phone

put phone/_mapping
{
  "properties": {
    "name": {
      "type": "text"
    },
    "desc":{
      "type":"text",
      "fields":{
        "keyword":{
          "type":"keyword"
        }
      }
    }
  }
}

POST phone/_doc
{
  "name": "apple",
  "desc":"Apple MacBook Pro 14英寸 M1 Pro芯片(8核中央处理器 14核图形处理器) 16G 512G 深空灰 笔记本 MKGP3CHA"
}

POST phone/_doc
{
  "name": "huawei",
  "desc":"华为 HUAWEI nova 8 Pro 麒麟985 5G SoC芯片 8GB+128GB 绮境森林全网通5G手机套餐一(无充电器和数据线)"
}

POST phone/_doc
{
  "name": "xiaomi",
  "desc":"小米12X 骁龙870 黄金手感 6.28英寸视感屏 120Hz高刷 5000万超清主摄 67W快充 8GB+128GB 蓝色 5G手机 8GB"
}

1.验证Term查询

先使用term查询如下:

get phone/_search
{
  "query":{
    "term":{
      "name":{
        "value":"Apple"
      }
    }
  }
}
结果如下:
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}

并没有查询到结果,前面我们说了,使用term查询的时候,会将文本进行全匹配,所以输入大写的Apple,查不到数据。

get phone/_search
{
  "query":{
    "term":{
      "name":{
        "value":"apple"
      }
    }
  }
}
结果如下:
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.9808291,
    "hits" : [
      {
        "_index" : "phone",
        "_type" : "_doc",
        "_id" : "fK7i8n4BV9WTNY27xi5I",
        "_score" : 0.9808291,
        "_source" : {
          "name" : "apple",
          "desc" : "Apple MacBook Pro 14英寸 M1 Pro芯片(8核中央处理器 14核图形处理器) 16G 512G 深空灰 笔记本 MKGP3CHA"
        }
      }
    ]
  }
}

使用term查询,指定name为存储的name,可以查询到数据。

2.Term特殊用法

我们知道,Term查询会将查询的文本作为一个单元去进行匹配,并不会做分词处理,但是text类型的字段,在写入时会做倒排索引分词。

所以当我们想对text类型的字段进行全匹配的时候,term查询是查不到结果的,如下:

get phone/_search
{
  "query":{
    "term":{
      "desc":{
        "value":"小米12X 骁龙870 黄金手感 6.28英寸视感屏 120Hz高刷 5000万超清主摄 67W快充 8GB+128GB 蓝色 5G手机 8GB"
      }
    }
  }
}

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

因为我们使用的是默认分词器,所以来验证下使用分词后的文本查询是否能够查询到结果

get phone/_search
{
  "query":{
    "term":{
      "desc":{
        "value":"8gb"
      }
    }
  }
}

查询结果
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.64110327,
    "hits" : [
      {
        "_index" : "phone",
        "_type" : "_doc",
        "_id" : "ha728n4BV9WTNY27BC62",
        "_score" : 0.64110327,
        "_source" : {
          "name" : "xiaomi",
          "desc" : "小米12X 骁龙870 黄金手感 6.28英寸视感屏 120Hz高刷 5000万超清主摄 67W快充 8GB+128GB 蓝色 5G手机 8GB"
        }
      },
      {
        "_index" : "phone",
        "_type" : "_doc",
        "_id" : "hK718n4BV9WTNY27_i4z",
        "_score" : 0.46457356,
        "_source" : {
          "name" : "huawei",
          "desc" : "华为 HUAWEI nova 8 Pro 麒麟985 5G SoC芯片 8GB+128GB 绮境森林全网通5G手机套餐一(无充电器和数据线)"
        }
      }
    ]
  }
}

很明显,匹配到了两条数据,携带_score匹配度算分,根据_score进行排序。

那么我们想要使用term对text进行精准匹配,需要怎么做呢? 还记得在Mapping中desc字段,设置了多字段类型。
在这里插入图片描述
那么就可以强制字段类型进行查询。

get phone/_search
{
  "query":{
    "term":{
      "desc.keyword":{
        "value":"小米12X 骁龙870 黄金手感 6.28英寸视感屏 120Hz高刷 5000万超清主摄 67W快充 8GB+128GB 蓝色 5G手机 8GB"
      }
    }
  }
}

结果如下:
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.9808291,
    "hits" : [
      {
        "_index" : "phone",
        "_type" : "_doc",
        "_id" : "ha728n4BV9WTNY27BC62",
        "_score" : 0.9808291,
        "_source" : {
          "name" : "xiaomi",
          "desc" : "小米12X 骁龙870 黄金手感 6.28英寸视感屏 120Hz高刷 5000万超清主摄 67W快充 8GB+128GB 蓝色 5G手机 8GB"
        }
      }
    ]
  }
}

结论:
1.Term Query不会将查询文本进行分词。
2.如果想要使用完全匹配,可以采用ES中的多字段属性
3.Term Query会返回算分结果

3.跳过算分

  1. 将Query转为Filter,忽略算分计算,避免开销。
  2. Filter可以有效利用缓存

利用Constant Score将查询转为Filter

get phone/_search
{
  "query":{
    "constant_score": {
      "filter": {
        "term":{
          "desc":{
            "value":"8gb"
          }
        }
      }
    }
  }
}

查询结果
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "phone",
        "_type" : "_doc",
        "_id" : "hK718n4BV9WTNY27_i4z",
        "_score" : 1.0,
        "_source" : {
          "name" : "huawei",
          "desc" : "华为 HUAWEI nova 8 Pro 麒麟985 5G SoC芯片 8GB+128GB 绮境森林全网通5G手机套餐一(无充电器和数据线)"
        }
      },
      {
        "_index" : "phone",
        "_type" : "_doc",
        "_id" : "ha728n4BV9WTNY27BC62",
        "_score" : 1.0,
        "_source" : {
          "name" : "xiaomi",
          "desc" : "小米12X 骁龙870 黄金手感 6.28英寸视感屏 120Hz高刷 5000万超清主摄 67W快充 8GB+128GB 蓝色 5G手机 8GB"
        }
      }
    ]
  }
}

可以看到ES没有对结果进行算分计算。

二、MatchQuery

基于全文本的查找:Match Query、Match Phrase Query、Query String Query

特点

  1. 索引和搜索时都会进行分词,查询字符串先传递到一个合适的分词器,然后生成一个供查询的词项列表
  2. 查询的时候。会先对输入的文本进行分词,然后每个此项逐个进行查询,最终将结果进行合并。并为每个文档生成一个算分。

1.验证Match查询

get phone/_search
{
  "query":{
    "match":{
      "desc":{
        "query":"小米手机"
      }
    }
  }
}

查询结果
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 3.0446715,
    "hits" : [
      {
        "_index" : "phone",
        "_type" : "_doc",
        "_id" : "ha728n4BV9WTNY27BC62",
        "_score" : 3.0446715,
        "_source" : {
          "name" : "xiaomi",
          "desc" : "小米12X 骁龙870 黄金手感 6.28英寸视感屏 120Hz高刷 5000万超清主摄 67W快充 8GB+128GB 蓝色 5G手机 8GB"
        }
      },
      {
        "_index" : "phone",
        "_type" : "_doc",
        "_id" : "hK718n4BV9WTNY27_i4z",
        "_score" : 0.9291471,
        "_source" : {
          "name" : "huawei",
          "desc" : "华为 HUAWEI nova 8 Pro 麒麟985 5G SoC芯片 8GB+128GB 绮境森林全网通5G手机套餐一(无充电器和数据线)"
        }
      }
    ]
  }
}

可以看到查询结果不仅有小米手机,还有华为手机。因为默认分词器将输入文本小米手机分词为小、米、手、机,而华为的desc匹配到了手和机,所以也被查询出来了

并且查询结果携带_score的匹配度算分。

2.Operator提高精准度

但是上述我们查询的是小米手机,华为手机也被匹配到了,在某些业务场景下显然是不允许的。

所以我们可以使用Operator对查询文本分词进行控制!

get phone/_search
{
  "query":{
    "match":{
      "desc":{
        "query":"小米手机",
        "operator":"AND"
      }
    }
  }
}

查询结果
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 3.0446715,
    "hits" : [
      {
        "_index" : "phone",
        "_type" : "_doc",
        "_id" : "ha728n4BV9WTNY27BC62",
        "_score" : 3.0446715,
        "_source" : {
          "name" : "xiaomi",
          "desc" : "小米12X 骁龙870 黄金手感 6.28英寸视感屏 120Hz高刷 5000万超清主摄 67W快充 8GB+128GB 蓝色 5G手机 8GB"
        }
      }
    ]
  }
}

指定了operator为AND,查询文本的分词需要全部被包含在内才能够被匹配到。

3.minimum_should_match提高精准度

minimum_should_match可以指定匹配的分词数。

get phone/_search
{
  "query":{
    "match":{
      "desc":{
        "query":"小米手机",
        "minimum_should_match": 2
      }
    }
  }
}

查询结果
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 3.0446715,
    "hits" : [
      {
        "_index" : "phone",
        "_type" : "_doc",
        "_id" : "ha728n4BV9WTNY27BC62",
        "_score" : 3.0446715,
        "_source" : {
          "name" : "xiaomi",
          "desc" : "小米12X 骁龙870 黄金手感 6.28英寸视感屏 120Hz高刷 5000万超清主摄 67W快充 8GB+128GB 蓝色 5G手机 8GB"
        }
      },
      {
        "_index" : "phone",
        "_type" : "_doc",
        "_id" : "hK718n4BV9WTNY27_i4z",
        "_score" : 0.9291471,
        "_source" : {
          "name" : "huawei",
          "desc" : "华为 HUAWEI nova 8 Pro 麒麟985 5G SoC芯片 8GB+128GB 绮境森林全网通5G手机套餐一(无充电器和数据线)"
        }
      }
    ]
  }
}

指定"minimum_should_match": 2,只要字段中包含了匹配到了两个分词,及在默认分词器下小米手机四个字中的任意两字,就会被查询出来,如上结果,华为手机也被查询出来了。

那我们指定为3应该就查询不出来了吧?

get phone/_search
{
  "query":{
    "match":{
      "desc":{
        "query":"小米手机",
        "minimum_should_match": 3
      }
    }
  }
}

查询结果
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 3.0446715,
    "hits" : [
      {
        "_index" : "phone",
        "_type" : "_doc",
        "_id" : "ha728n4BV9WTNY27BC62",
        "_score" : 3.0446715,
        "_source" : {
          "name" : "xiaomi",
          "desc" : "小米12X 骁龙870 黄金手感 6.28英寸视感屏 120Hz高刷 5000万超清主摄 67W快充 8GB+128GB 蓝色 5G手机 8GB"
        }
      }
    ]
  }
}

显然,华为手机的描述中,只能匹配到手和机这两个词条,我们指定最小匹配数为3就读不出来了。

4.Match Query的查询过程

由上述实践可知,MatchQuery的查询过程如下,首先对查询文本进行分词,在desc的倒排索引中,将分词后的每个词条进行查询,最终汇总得分结果进行排序,然后返回。
在这里插入图片描述

三、小结

  1. Term Query 和 Match Query 的区别:TermQuery不做分词处理,MatchQuery做分词处理。
  2. 如果有全文本匹配的场景,可以在Mapping中将字段设置为多类型 text+keyword
  3. 利用ES提供的参数来增加查询的精准度
  4. 复合查询 - Constant Score查询
    即便对Keyword进行Term查询,同样会进行算分。但是既然都使用term来查询Keyword了,那么算分其实并不重要。
    可以将查询转换为filter,取消相关性算分,并且能够利用缓存。达到一个提升性能的目的。
  • 15
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程大帅气

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值