问题现象
索引里面有数据,而没有查询出来。
如下图所示,术语库(索引)中里面有一条数据的原文是“层”,而根据完整的原文来查询该原文中的术语,并未将该术语查询出来。
根据原文查询该原文中的术语,并未查询到上面的术语:
原因分析
这是由于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": "侏罗层系主力区块年注采比变化"
}
}
}
}
]
}
}
}
缺点是如果分词结果太多,会比较消耗性能
目前是采用第二种方案来解决该问题的。