问题现象
索引里面有数据,而没有查询出来。
如下图所示,术语库(索引)中里面有一条数据的原文是“层”,而根据完整的原文来查询该原文中的术语,并未将该术语查询出来。
根据原文查询该原文中的术语,并未查询到上面的术语:
原因分析
这是由于es分词器导致的。由于es在执行查询时,会先将原文进行分词,再将分词后的结果拿去匹配。
查看分词结果:
由于待查询的字段采用的分词器是optimizeIK,所以这里使用该分词器查询原文分词后的结果
分词结果:
由于分词结果并没有“层”这条数据,所以也不会匹配出“层”这条术语。
解决方案
两种解决方案,分别是增加自定义词库和查询前分词
增加自定义词库
也就是自定义一个词库,让es遇到自定义词库中的词时也进行分词。比如将上述的“层”加入自定义词库当中,如何es分词时则会将“层”给分词出来。这需要改动es的配置文件IKAnalyzer.cfg.xml,修改文件里面的扩展配置指向自定义词库。
这种方案的缺点是不能一次性解决该问题,因为需要遇到此问题时,才能将未查询到的词组加入自定义词库,而且需要重启es,会影响线上功能。
查询前分词
所谓查询前分词,也就是不直接使用原文进行查询,而是先将原文通过多个分词器进行分词,再使用分词后的结果进行查询。比如将原文分别进行standard分词器分词和optimizeIK分词器分词,将两种分词器的分词结果进行合并,然后再进行查询。
先分词:
使用分词后的结果进行匹配:
es命令:
GET /tb/_search
{"query": {"bool": {"must": [{"terms": {"dbId": ["19841159f25845008fad2512aa91f48b"]}}],"should": [{"match_phrase": {"original": "侏"}},{"match_phrase": {"original": "罗"}}....],"minimum_should_match": 1, "filter": [{"script": {"script": {"lang": "painless","source": "String str = params.val;String strLower = params.valLower;String val = doc['original.keyword'].value;return str.contains(val) || strLower.contains(val.toLowerCase());","params": {"val": "侏罗层系主力区块年注采比变化","valLower": "侏罗层系主力区块年注采比变化"}}}}]}}
}
缺点是如果分词结果太多,会比较消耗性能
目前是采用第二种方案来解决该问题的。