当前位置: 首页 > news >正文

记录一个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时需要有 如下配置
"analysis":
    {
        "analyzer":
        {
            "ik":
            {
                "tokenizer": "ik_max_word"
            }
        }
    }
  1. 至此,重新创建索引后检查mapping,发现字段上已经定义了ik分词器了,重新写入两个doc之后发现搜索AA可以搜索到AA_BBB和AA BBB了, 问题解决!撒花!★,°:.☆( ̄▽ ̄)/$:.°★

最终解决

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

// 创建索引
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,并尝试优化一下现有的搜索逻辑,现在的搜索只是一个很基础的商品名称搜索,其实还有很多优化点,例如权重、字典、分词器等,开个坑吧!等我学会了来填!

相关文章:

  • 知识库-搜索知识接口
  • Java链表全解析:从单向链表到双向链表的实现与优化
  • AI+战略解码 【制造企业数字化转型】
  • 【Scrapy】Scrapy教程7——存储数据
  • UE5从入门到精通之多人游戏编程常用函数
  • C++ Primer 类的静态成员
  • 人工智能驱动的自动驾驶:技术解析与发展趋势
  • 雷龙CS SD NAND(贴片式TF卡)测评体验
  • tp6上传文件大小超过了最大值+验证文件上传大小和格式函数
  • 支持向量机(SVM)在 NLP 中的使用场景
  • Redis常见问题排查
  • AI训练中的常用指令
  • ue----git局域网内部署裸仓库,别的机器进行访问
  • 电脑连接wifi成功但上不了网 电脑网络故障解决方法
  • WinMerge深度解析:开源免费的文件与文件夹比较利器
  • 【Bug经验分享】Postgresql 项目链接不上,JDBC及Datasource可以连接,Navicat也可连接
  • c#爬取数据并解析json
  • PH热榜 | 2025-02-20
  • 美国第1代哈希散列算法SHA-1
  • 【自动化脚本工具】AutoHotkey (Windows)
  • “90后”樊鑫履新乌兰察布市察右中旗副旗长人选
  • 周口一乡镇公务员“被老赖”,两年4场官司均败诉,市监局将线索移送公安厅
  • 全过程人民民主研究基地揭牌,为推动我国民主政治建设贡献上海智慧
  • 上海质子重离子医院已收治8000例患者,基本覆盖国内常见恶性肿瘤
  • 野猪穿过江苏电视台楼前广场,被抓捕后送往红山森林动物园
  • 六朝文物草连空——丹阳句容南朝石刻考察纪