一、基本环境
- elasticsearch 版本:7.11.1
- 客户端环境:kibana v7.11.1、Java8 应用程序模块。
其中 kibana 主要用于数据查询诊断和查阅日志,Java8 为主要的客户端,数据插入和查询都是由Java 实现的。
二、问题现象
共有三个部署环境,一个是开发环境、一个是测试环境、一个是正式环境。
前提:APP的首页搜索功能(搜索设备列表和搜索智能列表)在开发环境和正式环境一切正常。
测试人员在测试APP的首页搜索功能(搜索设备列表和搜索智能列表),发现搜索智能列表功能正常,而搜索设备时,无数据。
使用 kibana 里的开发工具查询时单个汉字可以搜索出设备列表,而使用词语去搜索设备时一直搜索不到任何数据。
查询数据命令:命令 索引/_search
下图中的命令是查询该索引下的所有数据
示例(根据某一字段查询)如下:
GET device-name-index/_search { "query": { "bool": { "must": [ {"term": { "deviceName": { "value": "开关" } }} ] } } }
搜索“开”字可以把设备名称中带有“开”字的设备搜索出来,但是搜索“开关”词语时,设备名称中带有“开关”词语的设备结果为空(ES中实际上有数据)。
三、案例介绍
使用 elasticsearch 存储设备列表的主要信息,document 内的 field,基本上是 integer 或 keyword,es 自动创建的索引 device-name-index 如下:
查询 mapping 信息,命令如下:
GET device-name-index/_mapping
结果返回如下:
{ "device-name-index" : { "mappings" : { "properties" : { "deviceId" : { "type" : "integer" }, "deviceModel" : { "type" : "text" }, "deviceName" : { "type" : "text" }, "deviceType" : { "type" : "text" }, "floorName" : { "type" : "text" }, "id" : { "type" : "integer" }, "roomName" : { "type" : "text" }, "sn" : { "type" : "text" } } } } }
而部署在开发环境里的 es 索引里的字段类型如下:
{ "device-name-index" : { "mappings" : { "properties" : { "deviceId" : { "type" : "integer" }, "deviceModel" : { "type" : "keyword" }, "deviceName" : { "type" : "text", "fields" : { "ikmaxword" : { "type" : "text", "analyzer" : "ik_max_word" }, "pinyin" : { "type" : "text", "analyzer" : "pinyin" } }, "analyzer" : "standard" }, "deviceType" : { "type" : "keyword" }, "floorName" : { "type" : "keyword" }, "roomName" : { "type" : "keyword" }, "sn" : { "type" : "keyword" } } } } }
以上字段,只需要关注 deviceName 即可。因为搜索是根据此字段检索数据的。
可以很清楚的看到 deviceName 字段使用了 ik分词器(ik_max_word)。
四、问题原因
按照 mapping 返回结果来看,部署在测试环境的字段 deviceName 没有添加 ik 分词器,而 es 采取的策略是,如果没有添加自定义的分词器,那么便会使用 es 默认的标准分词器分词,这就是导致单个字搜索时可以检索出数据,而使用词语检索数据时无数据的原因。
五、解决方案
命令如下:
DELETE device-name-index
第二步:重建索引
命令如下:
PUT device-name-index { "mappings": { "properties": { "deviceId": { "type": "integer" }, "deviceModel": { "type": "keyword" }, "deviceName": { "type": "text", "fields": { "ikmaxword": { "type": "text", "analyzer": "ik_max_word" }, "pinyin": { "type": "text", "analyzer": "pinyin" } }, "analyzer": "standard" }, "deviceType": { "type": "keyword" }, "floorName": { "type": "keyword" }, "roomName": { "type": "keyword" }, "sn": { "type": "keyword" } } } }
第三步:触发程序灌数据
在我的项目中只需要修改设备名称即可触发数据内容变更(全量删除并全量更新),再次在APP首页搜索设备名称,单个字和词语都可以检索出数据,问题搞定。
使用ik分词器后,查看分词结果情况,命令格式:
GET 索引/_doc/索引下某字段_id/_termvectors?fields=字段名称.ikmaxword
使用es默认的分词器查看分词结果情况,命令:
GET 索引/_doc/索引下某字段_id/_termvectors?fields=字段名称
示例如下:
GET device-name-index/_doc/cO-TNIMBWdpBXCOgNBnM/_termvectors?fields=deviceName.ikmaxword
六、总结
问题虽小,但一定要追溯源头,比如此次测试环境的不规范操作。后期如果有删除索引的操作,应该先手动建立索引后,再灌数据,而不是直接让其自动 mapping 建立索引,自动 mapping 建立的字段类型,可能不是我们期望的。
完结!