记录一个ES分词器不生效的解决过程

问题背景

商城项目,其中商品查询检索使用的是ES, 但存在某些商品查询不到的问题

例如:某商品名包含AA_BBB 这样的关键词,但是搜索"AA"不能查询到该商品,但是将商品名修改为AA BBB 后就能查询到了.

怀疑是分词的问题,但看代码,在创建ES索引时在对应字段上也定义了分词器,但是不知道什么原因不好用,于是开始了问题调查

解决过程

解决过程比较坎坷,调查问题时由于我不怎么会ES,所以只能边搜索边调查,好在最后解决了问题.由于时间过去很久了,没留存截图和代码,只能简要复述一下

查询字段: name

查询条件: AA

商品名为 AA_BBB时无法查询出,改为AA BBB可以查询到

  1. 还原DSL,尝试简化条件后检索
    由于是业务相关的检索,调用接口查询ES时必然会除了关键词还有一些其他的查询条件,比如排序啊,分页啊,价格区间等等,所以结合日志还原DSL,省略掉其他的查询条件后一样无法查询到; 但直接使用ik分词对商品名进行分词后可以分词出所使用的查询关键词
  2. 仔细查阅了一下构建DSL进行搜索的代码, 未查看到疑点, 语句构建时没有异常
  3. 于是又查阅了一下ES索引创建的语句,发现在需要分词字段上都定义了分词器
  4. 于是尝试分析搜索的向量命中情况,发现确实未命中对应doc,说明分词或查询确实有问题
  5. 此时已经焦头烂额了...后来,考虑到从头复现一次,于是在本地安装了一个ES,版本与测试环境相同,使用代码里的语句创建索引,并写入了两个商品,名称分别为AA BBB和AA_BBB
  6. 尝试搜索后AA后发现依然无法查询到AA_BBB的文档,很诡异. 于是尝试查看了一下所创建的索引mapping,发现name字段上并没有analyzer!查看测试环境的实际业务索引,确实也没有analyzer! 至此已经发现了问题所在------ES索引创建定义analyzer异常
  7. 既然定位到问题了,就可以找到解决问题的方法了,于是查阅了一下资料,发现系统所使用的ES7版本,在创建索引时,除了在mapping中定义字段及analyzer时,还需要在定义setting时需要增加analysis配置,否则定义的分词器会不生效,即在创建mapping时需要有 如下配置
json 复制代码
"analysis":
    {
        "analyzer":
        {
            "ik":
            {
                "tokenizer": "ik_max_word"
            }
        }
    }
  1. 至此,重新创建索引后检查mapping,发现字段上已经定义了ik分词器了,重新写入两个doc之后发现搜索AA可以搜索到AA_BBB和AA BBB了, 问题解决!撒花!★,° :.☆( ̄▽ ̄)/$:.°★

最终解决

总结就是项目中的索引创建代码版本可能比较旧,在ES7上有些不再适用(其实发现问题后发现有的字段类型是string,但是其实ES7已经不再支持string类型,而是使用keywords和text了,所以进一步说明是ES相关代码的版本有问题)
最终解决问题的索引创建代码如下:

java 复制代码
// 创建索引
public boolean createIndex(String indexName) {
        if (isIndexExist(indexName)) {
            log.info("Index is exits!");
            return true;
        }

        CreateIndexResponse createIndexResponse = null;
        try {
            //创建映射
            XContentBuilder mapping = null;
            mapping = getMappingBuilder();
            // 初始化settings
            XContentBuilder settings = getIndexSettings();
//            CreateIndexRequest request = new CreateIndexRequest(indexName).source(mapping);
            CreateIndexRequest request = new CreateIndexRequest(indexName)
                    .settings(settings)
                    .mapping(mapping);
            //设置创建索引超时2分钟
            request.setTimeout(TimeValue.timeValueMinutes(2));
            createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("createIndex error ! e:{}", e);
        }
        return createIndexResponse.isAcknowledged();
    }

// 定义mapping
private XContentBuilder getMappingBuilder(){
        XContentBuilder mapping = null;
        try {
            // 修改type类型,string->text/keyword,decimal-?double --update on 2024/8/1 by MaYue
            mapping = XContentFactory.jsonBuilder()
                    .startObject()
                    .startObject("properties")
                    //.startObject("m_id").field("type","keyword").endObject()  //m_id:字段名,type:文本类型,analyzer 分词器类型
                    //该字段添加的内容,查询时将会使用ik_max_word 分词 //ik_smart  ik_max_word  standard
                    .startObject("id")
                    .field("type", "keyword")
                    .endObject()
                    // ES7已无string类型, 把需要分词的字段改成text
                    .startObject("name")
                    .field("type", "text")
                    .field("analyzer", "ik_max_word")
                    .endObject()
                    .endObject()
                    // .......其他字段省略
                    // settings单独设置,这段放到getIndexSettings方法里了
//                    .startObject("settings")
//                    //分片数
//                    .field("number_of_shards", 3)
//                    //副本数
//                    .field("number_of_replicas", 1)
//                    .endObject()
                    .endObject();
        } catch (IOException e) {
            log.error("createGoodsIndex error ! e:{}", e);
        }
        return mapping;
    }

// 定义settings
private XContentBuilder getIndexSettings() {
        XContentBuilder settings = null;
        try {
            settings = XContentFactory.jsonBuilder()
                    .startObject()
                    // 分片/副本设置
                    //分片数
                    .field("number_of_shards", 3)
                    //副本数
                    .field("number_of_replicas", 1)
                    // 增加分词器设置,否则创建mapping时指定analyzer不生效
                    // "analysis" : {
                    //          "analyzer" : {
                    //            "ik" : {
                    //              "tokenizer" : "ik_max_word"
                    //            }
                    //          }
                    //        }
                    .startObject("analysis")
                    .startObject("analyzer")
                    .startObject("ik")
                    .field("tokenizer","ik_max_word")
                    .endObject()
                    .endObject()
                    .endObject()
                    .endObject();
        } catch (IOException e) {
            log.error("createGoodsIndex error ! e:{}", e);
        }
        return settings;
    }

一些吐槽

最近入职了一家新公司,是做电商类项目的,其中商品查询检索那部分用的是ES. 刚来的时候不熟悉项目,也暂时没有新的需求给我,就主要熟悉一下项目,顺便给了我一个排查一些商品无法正常搜索出来的问题.比如此博文所记录的这个问题

但是在此之前其实我完全没用过ES, 招聘JD上写了ES但是我并不会面试的时候也没问过相关问题,而我还通过了面试...所以接到这个任务后基本上是现学的,粗略看了一下ES相关名词的定义,然后面向搜索引擎调查问题,查一点,学一点,最后居然解决了问题...总有种"当你在简历上吹了牛,但是获得了这份工作"的不真实感...

后续考虑系统地学习一下ES,并尝试优化一下现有的搜索逻辑,现在的搜索只是一个很基础的商品名称搜索,其实还有很多优化点,例如权重、字典、分词器等,开个坑吧!等我学会了来填!

相关推荐
fruge4 小时前
git上传 项目 把node_modules也上传至仓库了,在文件.gitignore 中忽略node_modules 依然不行
大数据·git·elasticsearch
飞火流星020278 小时前
ElasticSearch公共方法封装
elasticsearch·搜索引擎·es鉴权·es代理访问·es公共方法封装·es集群访问·判断es索引是否存在
vvvae12349 小时前
Elasticsearch实战应用:从“搜索小白”到“数据侦探”的进阶之路
elasticsearch
yinbp9 小时前
bboss v7.3.5来袭!新增异地灾备机制和Kerberos认证机制,助力企业数据安全
大数据·elasticsearch·微服务·etl·restclient·bboss
m0_748255029 小时前
Springboot中使用Elasticsearch(部署+使用+讲解 最完整)
spring boot·elasticsearch·jenkins
Elastic 中国社区官方博客9 小时前
Elasticsearch 自动补全搜索 - autocomplete
大数据·数据库·elasticsearch·搜索引擎·全文检索
Elastic 中国社区官方博客14 小时前
Elasticsearch 混合搜索 - Hybrid Search
大数据·人工智能·elasticsearch·搜索引擎·ai·语言模型·全文检索
{⌐■_■}14 小时前
【git】工作场景下的 工作区 <-> 暂存区<-> 本地仓库 命令实战 具体案例
大数据·git·elasticsearch·golang·iphone·ip·etcd
risc12345614 小时前
【Elasticsearch】为一个字段配置多个分析器
elasticsearch