zl程序教程

您现在的位置是:首页 >  其他

当前栏目

使用normalizer优化keyword字段的查询

2023-03-15 22:47:25 时间

我们知道elasticsearch提供了很多的字段类型,当我们索引结构化的简单字段的时候可以使用keyword类型,例如id,email、主机名、状态码、标签、邮政编码等;

但是keyword字段类型在索引的时候,并不会对字段的值进行一些预处理,也就是直接保留字段的原值。

当我们使用如下文档进行索引的时候,es到底是怎样进行索引处理的呢?

 {
    "id":1,
    "name":"code"
}

 Es字段的索引是通过对应类型的mapper进行处理,通过KeywordFieldMapper的parseCreateField的396行可以看到索引的时候,es直接将name字段的value即”code”作为二进制保存,没有进行任何的处理。 

当我使用如下的查询语句进行查询的时候,es是怎么来构建查询语句的呢?

{
    "query":{
        "term":{
            "name":"code"
        }
    }
}

Es首先会获取name字段对应的字段类型,然后调用字段的termQuery来构建查询语句;

这里name字段对应的是KeywordFieldMapper,其并没有覆盖实现父类的的termQuery方法,所以直接执行父类TermBasedFieldType的对应方法,方法中使用indexedValueForSearch对查询关键字进行处理;

我们可以看到KeywordFieldMapper实现了indexedValueForSearch, 由于初始化的时候将search/index的Analyzer都设置为Lucene.KEYWORD_ANALYZER,所以这里会执行305行调用父类TermBasedFieldType的indexedValueForSearch。

我们可以看到父类TermBasedFieldType也未对value进行任何的处理;

这样会带来一个问题,如果字段对应的值并不是枚举类型,而是用户输入的标签等,如果用户输入的单词包含大小写,那么搜索的时候也必须输入一模一样的关键字,否则使用下边的查询语句是命中不了结果的;

{
    "query":{
        "term":{
            "name":"Code"
        }
    }
}

通过查询es的文档,我们可以看到keyword类型提供了一个normalizer的配置参数,可以在index之前进行一些预处理工作;在自定义的normalizer中可以定义character filters和token filters,这样我们可以像下边这样配置一个lowercase的token filters即可以实现忽略大小写。

{
    "settings":{
        "analysis":{
            "normalizer":{
                "lowercase_normalizer":{
                    "type":"custom",
                    "filter":[
                        "lowercase"
                    ]
                }
            }
        }
    },
    "mappings":{
        "_doc":{
            "properties":{
                "id":{
                    "type":"integer"
                },
                "name":{
                    "type":"keyword",
                    "normalizer":"lowercase_normalizer"
                }
            }
        }
    }

在CustomNormalizerProvider中我们可以看到解析配置的char filters和token filters,并组合生成Custome analyzer。

在生成name字段的mapper的时候,根据配置的normalizer name获取对象的对象,并设置为field type的normalizer和search analyzer。

当我们index下边的文档的时候,我们看下es是怎么处理的

{
    "id":1,
    "name":"Code"
}

可以看到index文档的时候,会获取keyword对应的field type的normalizer,然后对name字段的value进行处理。

当我们搜索的时候,由于已经重置了keyword 对应的field type的searchAnalyzer,所以会直接调用新的自定的normalizer的normalize对输入的关键字进行与处理。

最终不管我们输入的name的值大小写的形式怎样,我们都可以搜索到