记录一个ES分词器不生效的解决过程
- 游戏开发
- 2025-08-25 12:36:01

问题背景
商城项目,其中商品查询检索使用的是ES, 但存在某些商品查询不到的问题 例如:某商品名包含AA_BBB这样的关键词,但是搜索"AA"不能查询到该商品,但是将商品名修改为AA BBB后就能查询到了. 怀疑是分词的问题,但看代码,在创建ES索引时在对应字段上也定义了分词器,但是不知道什么原因不好用,于是开始了问题调查
解决过程解决过程比较坎坷,调查问题时由于我不怎么会ES,所以只能边搜索边调查,好在最后解决了问题.由于时间过去很久了,没留存截图和代码,只能简要复述一下 查询字段: name 查询条件: AA 商品名为 AA_BBB时无法查询出,改为AA BBB可以查询到
还原DSL,尝试简化条件后检索 由于是业务相关的检索,调用接口查询ES时必然会除了关键词还有一些其他的查询条件,比如排序啊,分页啊,价格区间等等,所以结合日志还原DSL,省略掉其他的查询条件后一样无法查询到; 但直接使用ik分词对商品名进行分词后可以分词出所使用的查询关键词仔细查阅了一下构建DSL进行搜索的代码, 未查看到疑点, 语句构建时没有异常于是又查阅了一下ES索引创建的语句,发现在需要分词字段上都定义了分词器 于是尝试分析搜索的向量命中情况,发现确实未命中对应doc,说明分词或查询确实有问题此时已经焦头烂额了…后来,考虑到从头复现一次,于是在本地安装了一个ES,版本与测试环境相同,使用代码里的语句创建索引,并写入了两个商品,名称分别为AA BBB和AA_BBB尝试搜索后AA后发现依然无法查询到AA_BBB的文档,很诡异. 于是尝试查看了一下所创建的索引mapping,发现name字段上并没有analyzer!查看测试环境的实际业务索引,确实也没有analyzer! 至此已经发现了问题所在——ES索引创建定义analyzer异常既然定位到问题了,就可以找到解决问题的方法了,于是查阅了一下资料,发现系统所使用的ES7版本,在创建索引时,除了在mapping中定义字段及analyzer时,还需要在定义setting时需要增加analysis配置,否则定义的分词器会不生效,即在创建mapping时需要有 如下配置 "analysis": { "analyzer": { "ik": { "tokenizer": "ik_max_word" } } } 至此,重新创建索引后检查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,并尝试优化一下现有的搜索逻辑,现在的搜索只是一个很基础的商品名称搜索,其实还有很多优化点,例如权重、字典、分词器等,开个坑吧!等我学会了来填!
记录一个ES分词器不生效的解决过程由讯客互联游戏开发栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“记录一个ES分词器不生效的解决过程”
下一篇
PH热榜|2025-02-20