Elasticsearch 最强大的地方,不只是它能“存数据”和“搜数据”,而是它提供了一套非常灵活的查询语言,也就是 DSL。
很多人第一次接触 Elasticsearch 时,会觉得它和 SQL 很不一样。SQL 更像是面向“表”的查询,而 Elasticsearch DSL 更像是面向“文档”和“倒排索引”的检索描述语言。
这篇文章想讲清楚几个最常用的问题:
- 什么是 Elasticsearch DSL
match、term、bool分别怎么用- 查询和过滤有什么区别
- 排序、分页、聚合应该怎么写
- 平时最容易踩哪些坑
一、什么是 DSL
DSL 全称是 Domain Specific Language,也就是“领域特定语言”。
在 Elasticsearch 中,DSL 本质上就是一段 JSON,用来描述:
- 我要查哪些文档
- 条件是什么
- 怎么排序
- 怎么分页
- 是否要做聚合统计
例如最简单的一条查询:
1 | GET /product/_search |
这段 DSL 的含义就是:
- 在
product索引中搜索 - 查找
title字段中和iphone相关的文档
所以可以把 DSL 理解成 Elasticsearch 的查询描述语言。
二、一个完整搜索请求通常包含什么
一个典型的 Elasticsearch 搜索请求,通常由下面几部分组成:
query:查询条件sort:排序规则from/size:分页_source:返回哪些字段aggs:聚合分析
例如:
1 | GET /product/_search |
这一条请求已经很接近真实业务了:
- 关键词搜索
iphone - 只看 Apple 品牌
- 只要 5000 到 10000 的商品
- 按价格升序排序
- 返回前 10 条
三、Query 和 Filter 的区别
这是 Elasticsearch DSL 最值得先理解的地方。
虽然它们看起来都在“筛选文档”,但语义完全不同。
1. Query
query 更强调“相关性”。
它通常会计算 _score,也就是相关性得分。例如:
- 关键词是否命中
- 命中了几个词
- 词频如何
- 文档是否更接近查询意图
所以 query 适合全文检索场景,例如:
- 搜文章标题
- 搜商品名称
- 搜评论内容
2. Filter
filter 更强调“条件过滤”。
它只关心:
- 满足
- 或不满足
而不会参与相关性评分,通常也更高效,很多情况下还能利用缓存。
所以 filter 更适合结构化条件,例如:
- 品牌是否等于 Apple
- 价格是否大于 5000
- 状态是否为已上架
- 时间是否在某个区间内
一句话总结:
- 需要相关性计算,用
query - 只是精确筛选,用
filter
四、最常用的查询类型
1. match
match 是最常见的全文检索查询。
1 | GET /article/_search |
它会对查询词进行分词,再基于倒排索引检索。
适合字段:
text
不适合字段:
keyword- 数值字段
- 日期字段
2. term
term 用于精确匹配,不做分词。
1 | GET /product/_search |
适合场景:
- ID
- 枚举值
- 状态字段
keyword类型字段
这是最常见的坑之一:
- 对
text字段做全文搜索,不该用term - 对精确值做过滤,通常不该用
match
3. terms
terms 相当于 SQL 里的 IN。
1 | GET /product/_search |
4. range
range 用于范围查询。
1 | GET /product/_search |
常见关键字:
gt:大于gte:大于等于lt:小于lte:小于等于
5. match_phrase
match_phrase 用于短语匹配,强调词序。
1 | GET /article/_search |
适合需要“连续短语命中”的场景。
五、bool 查询
实际业务中,查询条件往往不会只有一个。Elasticsearch 里最常用的组合方式就是 bool。
bool 最常见的四个子句是:
mustshouldfiltermust_not
1. must
必须满足,并参与评分。
1 | "must": [ |
2. filter
必须满足,但不参与评分。
1 | "filter": [ |
3. should
最好满足,相当于加分项。
1 | "should": [ |
4. must_not
必须不满足。
1 | "must_not": [ |
5. 一个完整示例
1 | GET /product/_search |
六、排序和分页
1. 排序
1 | GET /product/_search |
常见排序字段:
_score- 数值字段
- 日期字段
keyword字段
如果对 text 字段排序,通常会有问题,一般要排序对应的 .keyword 子字段。
2. 分页
最常见的分页方式是:
1 | GET /product/_search |
含义是:
- 从第 0 条开始
- 返回 10 条
但需要注意,from + size 很大时,深分页性能会明显变差。
如果是深分页场景,通常更推荐:
search_afterscroll
其中:
scroll更适合离线导出search_after更适合在线翻页
七、聚合查询 aggs
Elasticsearch 不只是搜索引擎,它也很擅长做统计分析。这个能力主要通过 aggs 来实现。
1. terms 聚合
统计每个品牌下有多少文档:
1 | GET /product/_search |
这里 size: 0 的意思是:
- 不返回具体文档
- 只返回聚合结果
2. avg 聚合
统计平均价格:
1 | GET /product/_search |
3. 分组后再统计
先按品牌分组,再统计每个品牌的平均价格:
1 | GET /product/_search |
这是报表和分析场景里非常常见的写法。
八、几个常见业务场景
1. 全文搜索 + 条件过滤
1 | GET /article/_search |
2. 精确筛选
1 | GET /order/_search |
3. 搜索 + 聚合
1 | GET /product/_search |
这种写法在搜索页做筛选面板时特别常见。
九、最容易踩的坑
1. match 和 term 用反了
这是最常见的问题。
- 搜
text字段,通常用match - 查 ID、状态、枚举值,通常用
term
2. 对 text 字段做聚合或排序
很多人会发现:
- 排序报错
- 聚合结果不符合预期
原因通常是字段类型是 text,会被分词。
这时一般要用:
field.keyword
3. 深分页性能差
如果 from 很大,Elasticsearch 会越来越慢。
所以不是所有分页都适合一直用 from + size。
4. 把过滤条件写进 must
如果某个条件只是单纯筛选,而不是为了影响相关性评分,那么它更适合写进 filter。
十、一个综合示例
下面是一个比较完整的搜索 DSL:
1 | GET /product/_search |
如果把它翻译成人话,就是:
- 搜索标题里和
iphone相关的商品 - 品牌必须是 Apple
- 价格在 5000 到 10000 之间
- 不要下架商品
- 命中
pro的结果优先级更高 - 按相关性和价格排序
- 同时统计不同分类下的分布
十一、小结
ElasticSearch DSL 最重要的不是死记语法,而是先建立一套正确的使用习惯:
- 全文搜索优先考虑
match - 精确过滤优先考虑
term/range/filter - 复杂逻辑用
bool来组织 - 统计分析用
aggs
如果把 Elasticsearch DSL 理解成“倒排索引上的 JSON 查询语法树”,很多看起来复杂的写法其实都会变得很自然。
真正写多了之后,你会发现最常用的其实就是这几类:
matchtermboolrangesortaggs
把这些掌握住,日常绝大多数 Elasticsearch 查询需求基本就能覆盖了。