使用normalizer优化keyword字段的查询
我们知道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的值大小写的形式怎样,我们都可以搜索到
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现