keyword字段默认大小写敏感,需在创建索引时通过normalizer(如lowercase filter)配置实现忽略大小写,且无法对已有索引直接修改,必须重建索引并切换别名。

直接结论:keyword 类型字段默认大小写敏感,要实现不区分大小写查询,必须在 mapping 中为该字段配置 normalizer,且不能在已有索引上直接修改,必须重建索引。
为什么 keyword 字段查不到小写输入?
因为 keyword 类型不走分词器,存储什么样就存什么样。比如文档里存的是 "New York",用 term 查询 "new york" 就完全匹配不上——ES 不会自动转小写,也不做任何文本处理。
常见错误现象:
-
term查询返回空结果,即使肉眼看着“一样” -
terms聚合出现多个大小写变体("NEW YORK"、"New York"、"new york"分成不同桶) - 用
match_phrase能搜到,但聚合/排序/精确过滤仍失败——说明你误用了text字段做聚合
怎么加 normalizer 实现大小写不敏感?
关键不是改查询语句,而是改 mapping 结构。必须在创建索引时定义 normalizer,并绑定到目标 keyword 字段。
实操步骤:
- 在
settings里声明一个自定义normalizer,类型为custom,只加lowercasefilter - 在
properties中为字段指定"type": "keyword"并设置"normalizer": "lowercase" - 字段值写入时会自动转小写存储,后续
term、terms、range等操作都基于这个归一化后的值
示例片段:
PUT /my_index
{
"settings": {
"analysis": {
"normalizer": {
"lowercase": {
"type": "custom",
"filter": ["lowercase"]
}
}
}
},
"mappings": {
"properties": {
"city": {
"type": "keyword",
"normalizer": "lowercase"
}
}
}
}
重建索引时最容易漏掉的三件事
ES 不允许对已有字段修改 normalizer,所以必须重建。但很多人卡在这几步:
- 没删旧索引前就建新索引同名 —— ES 会报
resource_already_exists_exception - 同步数据时没用
_reindexAPI,而是手动遍历导出导入,漏掉_version或_routing导致数据不一致 - 忘了把别名切换过去:新索引建好后,要用
POST /_aliases把原别名指向新索引,否则应用还在查旧的
别名切换命令示例:
POST /_aliases
{
"actions": [
{ "remove": { "index": "my_index_v1", "alias": "my_index" }},
{ "add": { "index": "my_index_v2", "alias": "my_index" }}
]
}
Laravel 里调用时要注意什么?
客户端代码本身不用改,但你要确保:
- 模型里映射的字段(如
toSearchableArray()返回的city)是字符串,不是对象或 null ——normalizer只处理字符串 - 如果用 Scout,确认它写入的是新索引(检查
scout:import是否指向正确 index name) - 查询时仍用
term,不是match;用terms聚合也自然去重合并了
比如 Laravel 中这样查:
$client->search([
'index' => 'my_index',
'body' => [
'query' => ['term' => ['city' => 'new york']]
]
]);
真正容易被忽略的是:normalizer 只作用于索引阶段,查询 term 时不会再次转换——所以你传进去的值也得是小写(或统一转小写),否则白配。实际项目中建议在 PHP 层统一 mb_strtolower($input) 再传给 ES。











