Elasticsearch基于地址组件的AND/OR条件查询异常排查
问题原因排查
1. 无效结果返回原因
ES的bool查询有明确规则:当查询中存在must/filter子句时,should子句的minimum_should_match默认值为0,也就是不需要满足任何should条件即可返回结果。你返回的3条记录,单元号、门牌号、郊区、州、邮编完全一致,都满足must子句的所有条件,因此哪怕街道不匹配的记录也会被返回,只是得分更低。
2. 大小写不匹配原因
你查询使用的是.keyword类型字段,该类型默认是区分大小写的,小写的查询词和字段中首字母大写的内容无法完成匹配。
解决方案
临时修复方案(无需修改索引结构)
直接修改查询语句即可解决现有问题:
- 将原
must子句改为filter子句:精确匹配的地址组件不需要参与得分计算,放到filter里既可以提升查询性能,还能触发ES的过滤器缓存,重复查询速度更快 - 新增
minimum_should_match: 1配置:强制should子句至少满足一个,过滤掉街道不匹配的记录 - 把
match查询改为term查询并开启case_insensitive参数:实现精确匹配且不区分大小写
修改后的查询语句如下:
{ "query": { "bool": { "filter": [ { "term": { "unitnumber.keyword": { "value": "54", "case_insensitive": true } } }, { "term": { "streetnumber.keyword": { "value": "5", "case_insensitive": true } } }, { "term": { "suburb.keyword": { "value": "Lyons", "case_insensitive": true } } }, { "term": { "state.keyword": { "value": "ACT", "case_insensitive": true } } }, { "term": { "postcode.keyword": { "value": "2606", "case_insensitive": true } } } ], "should": [ { "term": { "streetname.keyword": { "value": "Burnie Street", "case_insensitive": true } } }, { "term": { "streetname.keyword": { "value": "Burnie St", "case_insensitive": true } } } ], "minimum_should_match": 1 } }, "size": 1000 }
注:case_insensitive参数需要ES 7.10及以上版本支持,如果你的版本更低,可以在查询前统一把查询词转为首字母大写的格式,和存储的内容格式对齐即可。
长期优化方案(修改索引结构,提升查询体验)
手动拼接街道全称和缩写的方式扩展性很差,建议修改索引映射,配置自定义分析器实现自动缩写匹配,同时统一处理大小写问题:
- 新增地址同义词过滤器,把常见的地址缩写(St/Street、Ave/Avenue、Rd/Road等)配置为同义词
- 给所有地址组件的keyword类型配置小写normalizer,天然实现大小写不敏感,不需要每次查询加参数
索引配置示例如下:
{ "settings": { "analysis": { "filter": { "address_synonym": { "type": "synonym", "synonyms": [ "street, st", "avenue, ave", "road, rd", "drive, dr" // 可自行补充更多地址缩写规则 ] }, "case_insensitive_normalizer": { "type": "normalizer", "filter": ["lowercase"] } }, "analyzer": { "street_analyzer": { "tokenizer": "standard", "filter": [ "lowercase", "address_synonym" ] } } } }, "mappings": { "properties": { "streetname": { "type": "text", "analyzer": "street_analyzer" }, "unitnumber": { "type": "keyword", "normalizer": "case_insensitive_normalizer" }, "streetnumber": { "type": "keyword", "normalizer": "case_insensitive_normalizer" }, "suburb": { "type": "keyword", "normalizer": "case_insensitive_normalizer" }, "state": { "type": "keyword", "normalizer": "case_insensitive_normalizer" }, "postcode": { "type": "keyword", "normalizer": "case_insensitive_normalizer" } // 其余字段保持原有配置即可 } } }
修改索引并重建数据后,查询语句可以简化为:
{ "query": { "bool": { "filter": [ {"term": {"unitnumber": "54"}}, {"term": {"streetnumber": "5"}}, {"term": {"suburb": "Lyons"}}, {"term": {"state": "ACT"}}, {"term": {"postcode": "2606"}}, {"match": {"streetname": "Burnie Street"}} ] } }, "size": 1000 }
此时不管你输入的是Burnie Street、burnie st还是其他大小写组合,都可以正确匹配到对应的街道数据,不需要手动拼接缩写条件。
其他优化建议
- 可以新增
copy_to字段把所有地址组件合并为一个完整地址字段,支持用户输入完整地址进行模糊搜索 - 如果有位置相关需求,可以新增经纬度的
geo_point类型字段,支持范围搜索、距离排序等能力 - 可以补充地址别名的同义词规则,适配同一个地点的不同叫法
内容的提问来源于stack exchange,提问作者KidWithAComputer
相关产品推荐
相关产品推荐

