Elasticsearch 的查询 DSL(Domain Specific Language)是基于 JSON 的检索语言,所有搜索请求都通过它来描述。掌握 Query DSL 是用好 Elasticsearch 的关键——它决定了检索结果准不准、快不快。本文从基本概念讲起,把最常用的七种查询类型整理成可直接照抄的示例,并补充实际使用中容易踩的坑。
一、基本概念
| 概念 | 说明 | 关系型数据库类比 |
|---|---|---|
| 索引(Index) | 文档的集合,是检索的基本单位 | 表(Table) |
| 文档(Document) | 存储在索引中的数据单元,JSON 格式 | 行(Row) |
| 字段(Field) | 文档中的键值对 | 列(Column) |
| 映射(Mapping) | 字段的类型定义,决定该字段能被怎样查询 | 表结构(Schema) |
| 类型(Type) | 7.0 之前一个索引可有多个类型;7.0 起一个索引只能有一个类型 _doc,8.0 已彻底移除该概念 |
已废弃 |
检索请求统一发到 _search 接口,一个完整的查询体除了 query,还可以带排序、分页、字段过滤和聚合:
GET /my_index/_search
{
"query": { "match": { "title": "elasticsearch" } },
"_source": ["title", "create_time"],
"from": 0,
"size": 10,
"sort": [{ "create_time": "desc" }]
}二、常用查询类型速查
| 查询类型 | 用途 | 是否分词 | 适用字段 |
|---|---|---|---|
match |
全文检索,最常用的查询 | 是 | text |
term |
精确匹配单个词条 | 否 | keyword / 数值 / 布尔 |
range |
范围查询 | 否 | 数值 / 日期 / keyword |
bool |
逻辑组合多个查询条件 | — | — |
wildcard |
通配符匹配(*、?) |
否 | keyword |
query_string |
Lucene 语法的字符串查询 | 视写法而定 | text |
nested |
查询嵌套对象内部字段 | 视内层查询而定 | nested 类型字段 |
三、示例详解
1. Match Query:全文检索
对查询词做分词后再匹配,是文本字段的默认选择。
{
"query": {
"match": {
"field_name": "search_term"
}
}
}常用参数:
{
"query": {
"match": {
"title": {
"query": "elasticsearch query dsl",
"operator": "and",
"minimum_should_match": "2"
}
}
}
}operator:默认or(分词后任一词条命中即返回),改为and则要求全部命中,召回率降低但精确度提高。minimum_should_match:最少命中几个词条,比and更灵活,例如"75%"。
2. Term Query:精确匹配
{
"query": {
"term": {
"field_name": "exact_value"
}
}
}注意:term 不做分词,匹配的是倒排索引里的词条,而不是原始字段值。因此它只应该用在 keyword、数值、布尔、日期这类字段上;对一个 text 字段用 term 查整句话,几乎一定查不到结果。
3. Range Query:范围查询
{
"query": {
"range": {
"timestamp": {
"gte": "2023-10-01T00:00:00Z",
"lt": "2023-10-31T23:59:59Z"
}
}
}
}四个边界参数:gt(大于)、gte(大于等于)、lt(小于)、lte(小于等于)。日期字段还支持日期数学表达式:
{
"query": {
"range": {
"create_time": { "gte": "now-7d/d", "lte": "now" }
}
}
}now-7d/d 表示「七天前的零点」,/d 是取整到天,适合做「最近七天」这类需求。
4. Bool Query:组合查询
{
"query": {
"bool": {
"must": [
{ "match": { "field1": "value1" } },
{ "range": { "field2": { "gte": 10, "lte": 20 } } }
],
"should": [
{ "term": { "field3": "value3" } }
],
"must_not": [
{ "term": { "field4": "value4" } }
]
}
}
}四个子句的语义与打分行为:
| 子句 | 语义 | 参与打分 |
|---|---|---|
must |
必须匹配 | 是 |
filter |
必须匹配 | 否,可被缓存,过滤条件优先用它 |
should |
可选匹配,命中可加权 | 是 |
must_not |
必须不匹配 | 否 |
两个容易踩的点:
- 只关心「有没有」而不关心「有多相关」的条件(比如状态、时间范围、租户 ID),放进
filter而不是must,可以跳过打分并被缓存,性能更好。 should的命中数量要求取决于上下文:当bool里没有must/filter时,默认必须命中至少一个should;一旦同时存在must/filter,should默认变成纯加分项,命中 0 个也能返回。需要强制时显式写"minimum_should_match": 1。
5. Wildcard Query:通配符查询
{
"query": {
"wildcard": {
"field_name": "*pattern*"
}
}
}* 匹配任意多个字符,? 匹配单个字符。也可用对象写法指定参数:
{
"query": {
"wildcard": {
"field_name": {
"value": "abc*",
"case_insensitive": true
}
}
}
}性能提示:通配符匹配的是词典中的词条,以 * 开头(如 *pattern*)无法利用前缀索引,相当于遍历全部词条,字段基数大时会明显变慢。前缀匹配场景优先考虑 prefix 查询或 wildcard 字段类型(7.9+);右侧模糊匹配、需要打字即搜的场景更适合 match_phrase_prefix 或 search_as_you_type。
6. Query String Query:Lucene 语法查询
{
"query": {
"query_string": {
"query": "field1:value1 AND field2:value2"
}
}
}支持 AND / OR / NOT、field:value、括号分组、通配符等 Lucene 语法,适合把用户输入或配置项直接当查询表达式解析。使用时注意:
- 语法错误会直接抛异常(默认
lenient: false),面向用户输入的场景建议改用语法更宽松、不会报错的simple_query_string。 - 不指定字段时会用
default_field,或用fields指定多个候选字段。
7. Nested Query:嵌套对象查询
{
"query": {
"nested": {
"path": "address",
"query": {
"bool": {
"must": [
{ "match": { "address.city": "New York" } },
{ "match": { "address.state": "NY" } }
]
}
}
}
}
}普通对象数组在底层是「扁平化」存储的,address.city 和 address.state 的对应关系会丢失,查「city 是 A 且 state 是 B」可能匹配到并不存在的组合。用 nested 查询可以保持数组内每个对象的独立性,但前提是映射里显式声明了嵌套类型:
PUT /my_index
{
"mappings": {
"properties": {
"address": { "type": "nested" }
}
}
}如果字段没有声明为 nested,执行嵌套查询会直接报 [nested] failed to find nested object under path,而后续再改类型需要重建索引(reindex),所以建模阶段就要想清楚。
四、其他常见坑
- 分页深度限制:
from + size受index.max_result_window限制,默认最多只能翻到第 10000 条。深分页应改用search_after或滚动查询(scroll/ PIT)。 - text 与 keyword 双字段:字符串字段动态映射后会同时生成
text(分词,供全文检索)和keyword子字段(不分词,供排序、聚合、精确匹配),写查询时容易搞混,注意区分field与field.keyword。 - 排序和聚合不能用 text 字段:对
text字段做排序或 terms 聚合会报Fielddata is disabled,应该用它的.keyword子字段。 - 先看映射再写查询:用
GET /my_index/_mapping确认字段类型,能避免大部分「查不到结果」的问题。
五、速查卡
// 全文检索
{ "match": { "title": "关键词" } }
// 精确匹配(keyword 字段)
{ "term": { "status": "ACTIVE" } }
// 范围
{ "range": { "price": { "gte": 10, "lte": 20 } } }
// 组合:must 打分 + filter 过滤 + should 加分 + must_not 排除
{ "bool": {
"must": [ ... ],
"filter": [ ... ],
"should": [ ... ],
"must_not": [ ... ]
} }
// 通配符
{ "wildcard": { "name": "abc*" } }
// Lucene 语法
{ "query_string": { "query": "field1:v1 AND field2:v2" } }
// 嵌套对象
{ "nested": { "path": "address", "query": { ... } } }
评论区