【线上踩坑分享】使用ES term query时遇到的问题

问题现象

由于对Elasticsearch term query的不了解,导致线上条件过滤出现问题,下面举个例子进行说明:

建立一个索引,其中namekeyword类型,full_nametext类型。

java 复制代码
PUT /test-index
{
  "mappings": {
    "_doc": {
      "properties": {
        "name": {
          "type": "keyword"
        },
        "full_name": {
          "type": "text"
        }
      }
    }
  }
}

插入一条数据

java 复制代码
PUT /test-index/_doc/1
{
  "name":"zhang",
  "full_name":"zhang san"
}

查询name字段

java 复制代码
GET /test-index/_search
{
  "query": {
    "term": {
      "name": "zhang"
    }
  }
}

查询结果如下图所示:

改为查询full_name字段

java 复制代码
GET /test-index/_search
{
  "query": {
    "term": {
      "full_name": "zhang san"
    }
  }
}

没有查到任何结果

但是如果只查询zhang或者san,就可以查询到结果。

java 复制代码
GET /test-index/_search
{
  "query": {
    "term": {
      "full_name": "zhang"
    }
  }
}

问题分析

通过查询官方文档发现,关于term query的使用官方给出了如下3项使用说明:

  1. 避免对text属性的字段使用term query查询。

  2. Elasticsearch会因为分词器的原因改变字段的值,所以如果想要精确匹配text类型的字段将会很难。

  3. 如果查询text类型的字段,建议使用match query

所以,前面的问题很明显和分词有关。

通过如下验证可以发现在标准分词器下,zhang san会被分成zhangsan,所以使用term query查询时,如果直接查询zhang san是查询不到结果的。

java 复制代码
POST _analyze
{
  "analyzer": "standard",
  "text": "zhang san"
}

解决方式

使用match query

按照官方建议,直接使用match query

java 复制代码
GET /test-index/_search
{
  "query": {
    "match": {
      "full_name": "zhang san"
    }
  }
}

term query与match query对比

term query:会直接按照查询内容进行精确匹配,如果分词表中能查到对应的结果则返回。 match query:会对查询的内容先进行分词,然后按照分词的结果与分词表进行匹配,只要有一个匹配到就算匹配成功。 可以这样理解,以分词为查询单位来看,term是精确匹配,而match是模糊匹配。

延伸扩展

关于分词器,我们可以再聊聊,Elasticsearch实际上有多种分词器,同时也支持自定义分词器,所以实际上在不同的分词器下,zhang san可能有不同的拆分方法,就像前面说的,分词的规则如果你掌握不好,就会导致查询的结果和你设想中的结果不匹配,接下来一些案例就让我们来看看默认的分词器standard analyzer会有哪些意想不到的场景!

1. 删除大多数标点符号

java 复制代码
POST _analyze
{
  "analyzer": "standard",
  "text": "hello! zhang san"
}

对比whitespace分词器则可以识别出标点符号

java 复制代码
POST _analyze
{
  "analyzer": "whitespace",
  "text": "hello! zhang san"
}

可以看到hello!并没有被拆分开。

2. 英文大写转小写

这是一个很容易掉坑的地方。

java 复制代码
POST _analyze
{
  "analyzer": "standard",
  "text": "Hello Zhang San"
}

经过standard分词器后,大写的英文字母都被转换成了小写,所以,此时如果你还是按照Hello来查询的话,是不会查询到结果的,这点要特别注意。

3. 超过max_token_length部分单独拆分

max_token_length:最大令牌长度。如果超过此长度,则会被拆分开。默认值为 :255

下面我们自定义一个分词器,并设置max_token_length长度为5

java 复制代码
PUT test-analyzer
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
        "my_tokenizer": {
          "type": "standard",
          "max_token_length": 5
        }
      }
    }
  }
}

看一下分词效果

java 复制代码
POST test-analyzer/_analyze
{
  "analyzer": "my_analyzer",
  "text": "Hello, Elasticsearch"
}

可以看到,拆分后的每一项长度最多不会超过5

相关推荐
それども9 分钟前
为什么要加@ResponseBody
java·开发语言·spring boot
一只专注api接口开发的技术猿20 分钟前
微服务架构下集成淘宝商品 API 的实践与思考
java·大数据·开发语言·数据库·微服务·架构
2501_9444241230 分钟前
Flutter for OpenHarmony游戏集合App实战之记忆翻牌配对消除
android·java·开发语言·javascript·windows·flutter·游戏
李慕婉学姐31 分钟前
【开题答辩过程】以《基于Spring Boot和大数据的医院挂号系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
大数据·spring boot·后端
鹿角片ljp38 分钟前
Java网络编程入门:从Socket到多线程服务器
java·服务器·网络
走进IT1 小时前
DDD项目分层结构说明
java
橙露1 小时前
嵌入式实时操作系统 FreeRTOS:任务调度与信号量的核心应用
java·大数据·服务器
愚公移码1 小时前
蓝凌EKP产品:关联机制浅析
java·服务器·前端
阿蒙Amon1 小时前
C#每日面试题-is和as的区别
java·开发语言·c#
Tao____1 小时前
适合中小企业的物联网平台
java·物联网·mqtt·低代码·开源