Elasticsearch多字段must匹配及日期范围查询优化方法
Elasticsearch 多条件查询精简优化方案
需求说明
需要实现指定规则的文档检索+排序,规则如下:
- 过滤逻辑:文档必须同时满足三个要求
- idfield匹配给定的多个可选值
- namefield匹配固定值
- datefield字段值落在指定的日期范围内
- 排序逻辑:最终返回结果按
datefield升序、idfield.keyword升序排列
测试用的样例数据结构如下:
datefield idfield namefield msgfield 2020-01-01 10:20:10 304 xyz msg1 2020-01-01 10:20:10 102 xyz msg2 2020-01-01 10:20:10 101 pqr msg3 ...... 2020-01-01 10:21:10 304 xyz msg4 2020-01-01 10:21:10 102 xyz msg5 ......
现有写法问题
当前使用的查询是把每个idfield对应的完整过滤条件单独封装为bool子句,通过should做OR拼接,示例查询如下:
{ "from": 0, "size": 2000, "sort": [ { "datefield": { "order": "asc" } }, { "idfield.keyword": { "order": "asc" } } ], "query": { "bool": { "should": [ { "bool": { "must": [ { "match": { "idfield": "304" }}, { "match": { "namefield": "xyz" }}, { "range": { "datefield": { "gte":"2020-01-01T10:20:10", "lte":"2020-01-01T10:30:20" } } } ] } }, { "bool": { "must": [ { "match": { "idfield": "102" }}, { "match": { "namefield": "xyz" }}, { "range": { "datefield": { "gte":"2020-01-01T10:20:10", "lte":"2020-01-01T10:30:20" } } } ] } } ] } } }
这种写法本质是多组全条件OR拼接:(idfield=304 AND namefield=xyz AND 日期符合要求) OR (idfield=102 AND namefield=xyz AND 日期符合要求) OR ...。
问题非常明显:namefield匹配、datefield范围过滤这两个固定条件在每个子句里重复定义,当需要匹配的idfield值增多时,查询语句会快速膨胀,冗余度极高,维护成本和执行开销都会上升。
优化后查询写法
根据布尔逻辑分配律,(A∧B∧C)∨(A∧B∧D)完全等价于A∧B∧(C∨D),所以可以把公共的固定条件提取到全局must里,多值匹配的idfield直接用terms查询实现,不需要重复拼接子句。优化后的查询如下:
{ "from": 0, "size": 2000, "sort": [ { "datefield": { "order": "asc" } }, { "idfield.keyword": { "order": "asc" } } ], "query": { "bool": { "must": [ // 固定namefield匹配,全局生效,仅需定义1次 { "match": { "namefield": "xyz" } }, // 固定日期范围过滤,全局生效,仅需定义1次 { "range": { "datefield": { "gte": "2020-01-01T10:20:10", "lte": "2020-01-01T10:30:20" } } }, // 多id匹配直接用terms传数组,等价于多个id值OR匹配 { "terms": { // 如果需要精确匹配避免分词干扰,推荐替换为 "idfield.keyword": ["304", "102"] "idfield": ["304", "102"] } } ] } } }
优化收益
- 零冗余:公共过滤条件只需要写一次,不管要匹配多少个idfield值,都不需要重复定义namefield和datefield的规则
- 易维护:新增、删除要匹配的id值,只需要修改terms查询里的数组即可,不需要新增整段重复的查询结构
- 性能更好:ES不需要解析大量重复的bool子句,查询解析、匹配执行的开销都更低
- 结果一致:优化前后的查询语义完全等价,不会出现匹配结果偏差
注意:如果idfield是text类型(存储时会自动分词),做精确值匹配的时候建议直接查询
idfield.keyword子字段,和排序使用的字段保持一致,避免分词导致的误匹配问题。
内容的提问来源于stack exchange,提问作者nay
相关产品推荐
相关产品推荐

