Elasticsearch 从 0 到 1(三):DSL 查询入门
前两篇讲了 ES 的定位和索引建模:
这一篇开始写查询。
ES 查询使用 Query DSL,本质上是用 JSON 描述查询条件。
比如商品搜索:
关键词:蓝牙耳机分类:数码耳机价格:100-300排序:销量优先分页:第 1 页,每页 10 条对应到 DSL,就是把这些条件组合起来。
match:全文搜索
match 用于全文搜索,适合查 text 字段。
GET /products/_search{ "query": { "match": { "title": "蓝牙耳机" } }}如果 title 是 text 字段,查询词会走分词器。
适合场景:
- 商品标题搜索;
- 文章内容搜索;
- 描述字段搜索。
term:精确匹配
term 用于精确匹配,适合查 keyword、数字、布尔等字段。
GET /products/_search{ "query": { "term": { "brand": "SoundGo" } }}不要拿 term 直接查一个会分词的 text 字段,否则结果可能不符合预期。
简单记:
match:用于全文搜索term:用于精确匹配range:范围查询
范围查询常用于价格、时间、销量。
GET /products/_search{ "query": { "range": { "price": { "gte": 100, "lte": 300 } } }}常见操作符:
| 操作符 | 含义 |
|---|---|
gt | 大于 |
gte | 大于等于 |
lt | 小于 |
lte | 小于等于 |
bool:组合查询
真实业务里很少只有一个条件,更多是多个条件组合。
这时用 bool。
GET /products/_search{ "query": { "bool": { "must": [ { "match": { "title": "蓝牙耳机" } } ], "filter": [ { "term": { "category": "数码耳机" } }, { "range": { "price": { "gte": 100, "lte": 300 } } } ] } }}常见子句:
| 子句 | 含义 |
|---|---|
must | 必须匹配,会影响相关性评分 |
filter | 必须匹配,不参与评分,适合过滤 |
should | 可选匹配,常用于提升相关性 |
must_not | 必须不匹配 |
业务里常用组合:
关键词搜索放 must分类、品牌、价格范围放 filter排除条件放 must_notquery context 和 filter context
ES 查询里有两个重要概念:
- query context:关心是否匹配,也关心匹配得多好;
- filter context:只关心是否匹配,不计算相关性。
比如关键词搜索“蓝牙耳机”,不同商品相关性不同,需要评分。
但分类等于“数码耳机”、价格在 100 到 300,这类条件只是过滤,不需要评分。
所以商品搜索里通常这样分:
关键词:must筛选条件:filter排序
按销量倒序:
GET /products/_search{ "query": { "match": { "title": "蓝牙耳机" } }, "sort": [ { "sales": "desc" } ]}按价格升序:
"sort": [ { "price": "asc" }]如果没有显式排序,ES 默认会按 _score 相关性排序。
分页
普通分页使用 from 和 size:
GET /products/_search{ "from": 0, "size": 10, "query": { "match": { "title": "蓝牙耳机" } }}第 1 页:
from = 0size = 10第 2 页:
from = 10size = 10注意:深分页会越来越慢。
比如 from = 100000, size = 10,ES 需要在各分片上取出很多数据再合并排序。
深分页通常要考虑 search_after,生产优化放到第六篇讲:
高亮
搜索结果里常见的关键词标红,就是高亮。
GET /products/_search{ "query": { "match": { "title": "蓝牙耳机" } }, "highlight": { "fields": { "title": {} }, "pre_tags": ["<em>"], "post_tags": ["</em>"] }}返回结果里会有 highlight 字段。
前端展示时可以优先使用高亮标题。
聚合查询
聚合用于统计分析。
比如按品牌统计商品数量:
GET /products/_search{ "size": 0, "aggs": { "brand_count": { "terms": { "field": "brand" } } }}按价格区间统计:
GET /products/_search{ "size": 0, "aggs": { "price_ranges": { "range": { "field": "price", "ranges": [ { "to": 100 }, { "from": 100, "to": 300 }, { "from": 300 } ] } } }}商品搜索页左侧的品牌、分类、价格区间筛选,就可以用聚合来做。
一个完整商品搜索示例
GET /products/_search{ "from": 0, "size": 10, "query": { "bool": { "must": [ { "match": { "title": "蓝牙耳机" } } ], "filter": [ { "term": { "category": "数码耳机" } }, { "term": { "status": "ON_SALE" } }, { "range": { "price": { "gte": 100, "lte": 300 } } } ] } }, "sort": [ { "sales": "desc" }, { "price": "asc" } ], "highlight": { "fields": { "title": {} } }}这个查询已经接近真实商品搜索接口了。
这一篇先记住什么
match用于全文搜索;term用于精确匹配;range用于范围查询;bool用于组合条件;must会影响评分,filter更适合筛选条件;from + size可以分页,但深分页要小心;- 高亮用
highlight; - 聚合用
aggs,适合统计品牌、分类、价格区间。
下一篇把这些 DSL 接到 Spring Boot 项目里:
参考
If this article helped you, please share it with others!
Some information may be outdated






