Elasticsearch:查询指定年份范围外的文档问题
1. 正常年份范围查询(包含指定年份)
当需要查询指定年份落在[year_start, year_end]区间内的文档时,使用以下bool filter查询可正常实现:
{ "query": { "bool": { "filter": [ { "range": { "year_start": { "lte": year } } }, { "range": { "year_end": { "gte": year } } } ] } } }
2. 反向查询的问题(不包含指定年份)
尝试用must_not包裹上述filter来查询指定年份不在[year_start, year_end]区间内的文档时,结果不符合预期。原因是must_not会排除满足任一单个条件的文档,而非排除同时满足两个条件的文档——只要文档满足year_start <= year或year_end >= year其中一个,就会被排除,这和“排除同时满足两个条件的文档”的需求逻辑不符。
3. 测试案例:查询包含2022但不包含2023的文档
测试数据准备
先执行以下批量写入操作初始化测试数据:
DELETE test PUT test/_bulk?refresh {"index": {}} {"year_start": "2022", "year_end": "2022" } {"index": {}} {"year_start": "2023", "year_end": "2024" } {"index": {}} {"year_start": "2021", "year_end": "2025" } {"index": {}} {"year_start": "2016", "year_end": "2020" }
原查询的问题
原查询用must确保文档包含2022,再用should限制不包含2023,但should在仅搭配must的bool查询中默认是可选条件——即使should的条件都不满足,只要must条件达标,文档依然会被返回。比如year_start:2021, year_end:2025的文档,同时包含2022和2023,但因满足must条件仍会被查出,不符合预期。
正确查询写法
写法一:用must_not排除包含2023的文档
包含2023的逻辑是year_start <=2023且year_end >=2023,用must_not包裹这个条件即可:
GET test/_search { "query": { "bool": { "must": [ { "range": { "year_start": { "lte": 2022 } } }, { "range": { "year_end": { "gte": 2022 } } } ], "must_not": [ { "bool": { "filter": [ { "range": { "year_start": { "lte": 2023 } } }, { "range": { "year_end": { "gte": 2023 } } } ] } } ] } } }
该查询会返回预期结果:仅{"year_start": "2022", "year_end": "2022"}这一条文档。
写法二:用minimum_should_match强制should条件生效
包含2022但不包含2023的文档,要么区间在2023前结束(year_end <2023),要么区间在2023后开始(year_start >2023)。给should添加minimum_should_match:1,确保至少满足一个should条件:
GET test/_search { "query": { "bool": { "must": [ { "range": { "year_start": { "lte": 2022 } } }, { "range": { "year_end": { "gte": 2022 } } } ], "should": [ { "range": { "year_end": { "lt": 2023 } } }, { "range": { "year_start": { "gt": 2023 } } } ], "minimum_should_match": 1 } } }
此写法同样能得到正确结果。
内容的提问来源于stack exchange,提问作者pruna

