Elasticsearch DSL 查询语法大全:从入门到精通

Elasticsearch DSL 查询语法大全:从入门到精通

Elasticsearch 是当下比较流行的一个分布式搜索引擎。

之前有个项目里偶然接触到了,后续项目都配合 ES,实现了很好的效果。

这篇文章是我在做项目过程中的笔记复盘,整理了 ES 中最常用的 DSL 查询语法。

真心希望能给用到 ES 的你,提供一些帮助~😀


前言

为什么选择 Elasticsearch?

客户数据量大,一直用 MySQL 做数据存储,但LIKE查询的效率实在太低。

对于我们开发来说,这个加载时间还可以,但是在客户那里变得不可接受。

所以,上 ES 成了我们的首选。

什么是 DSL?

DSL(Domain Specific Language)是 ES 提供的查询语言,使用了 JSON 格式。相比于 SQL,DSL 更加好写,能够表达复杂的查询逻辑:

  • 叶子查询:在特定字段中查找特定值(如 match、term、range)
  • 复合查询:组合多个查询条件(如 bool、dis_max)
  • 嵌套查询:处理复杂的对象关系

这篇文章里总结了 8 种常用查询类型,辅助常见的检索场景。


8种常用查询类型

1. match_all 查询所有

基本用法

match_all 是最简单的查询,返回索引中的所有文档:

json 复制代码
{
  "query": {
    "match_all": {}
  }
}

实际应用场景

虽然看起来很简单,但 match_all 在实际开发中有很多用途:

  1. 数据导出:批量导出索引中的所有数据
  2. 索引验证:检查索引是否正确创建、数据是否正常写入
  3. 统计分析:结合聚合函数进行全量数据分析

配合分页使用

json 复制代码
{
  "query": {
    "match_all": {}
  },
  "from": 0,
  "size": 20
}

⚠️ 重要提示:Elasticsearch 默认限制返回结果不超过 10000 条。如果需要查询超过这个限制的数据,可以修改索引设置

json 复制代码
PUT /your_index/_settings
{
  "index.max_result_window": 100000
}

2. match 模糊匹配

核心概念

match 查询是ES中最常用的全文搜索查询。它会将查询字符串进行分词,然后在倒排索引中查找匹配的文档。

这与 SQL 中的 LIKE 查询有本质区别:

  • SQL LIKE:字符串级别的模糊匹配
  • ES match:分词后的词汇级别匹配

单词匹配

最简单的用法,匹配单个词:

json 复制代码
{
  "query": {
    "match": {
      "title": "elasticsearch"
    }
  }
}

中文分词处理

中文内容会被分词器先分词,再搜索:

json 复制代码
{
  "query": {
    "match": {
      "content": " Elasticsearch搜索引擎"
    }
  }
}

假设使用 IK 分词器,"Elasticsearch搜索引擎" 会被拆分为:

  • "elasticsearch"
  • "搜索引擎"

只要文档中包含这两个词中的任意一个,就会被匹配到。

💡 分词器选择建议

分词器 特点 适用场景
standard 默认分词器,按词切分 英文文档
ik_smart 智能切分,粗粒度 中文搜索(推荐)
ik_max_word 最大化切分,细粒度 精确中文搜索
jieba 结巴分词 中文搜索

多词匹配与 operator 参数

当查询字符串包含多个词时,ES 默认使用 or 逻辑:

json 复制代码
{
  "query": {
    "match": {
      "content": "java python"
    }
  }
}

这会匹配包含 "java" "python" 的所有文档。

如果需要同时包含所有词,使用 operator: "and"

json 复制代码
{
  "query": {
    "match": {
      "content": {
        "query": "java python",
        "operator": "and"
      }
    }
  }
}

现在只会匹配同时包含 "java" 和 "python" 的文档。

minimum_should_match 精确控制

当查询词很多时,and 可能过于严格。这时可以使用 minimum_should_match 控制最少匹配词数:

json 复制代码
{
  "query": {
    "match": {
      "content": {
        "query": "elasticsearch search engine distributed java python",
        "minimum_should_match": "3"
      }
    }
  }
}

表示 5 个词中至少匹配 3 个。也支持百分比写法:

json 复制代码
"minimum_should_match": "75%"    // 匹配 75% 的词

3. match_phrase 精确匹配

核心概念

match_phrase 用于短语级别的精确匹配。与 match 不同,它不仅要求所有词都存在,还要求词的顺序和位置与查询一致。

基本用法

json 复制代码
{
  "query": {
    "match_phrase": {
      "content": " Elasticsearch搜索引擎"
    }
  }
}

这只会匹配包含完整短语 "Elasticsearch搜索引擎" 的文档,而不会匹配 "搜索引擎Elasticsearch"。

实际对比示例

假设文档内容为:"Elasticsearch是一个强大的搜索引擎"

查询方式 查询内容 是否匹配 原因
match "搜索引擎" ✅ 匹配 分词后匹配
match_phrase "搜索引擎" ✅ 匹配 短语完整存在
match_phrase "搜索 引擎" ✅ 匹配 顺序一致
match_phrase "引擎 搜索" ❌ 不匹配 顺序不一致

slop 参数:灵活的短语匹配

match_phrase 默认要求词之间紧密相连。但实际场景中,用户可能记不清确切表达,这时可以用 slop 参数:

json 复制代码
{
  "query": {
    "match_phrase": {
      "content": {
        "query": "quick fox",
        "slop": 2
      }
    }
  }
}

slop: 2 表示 "quick" 和 "fox" 之间最多可以间隔 2 个词的位置移动。

匹配示例

  • "quick brown fox" ✅ (间隔1个词)
  • "quick very brown fox" ✅ (间隔2个词)
  • "quick very very brown fox" ❌ (间隔超过2个词)

应用场景

match_phrase 特别适合以下场景:

  1. 精确短语搜索:搜索固定名称、专有名词
  2. 引号搜索:用户使用双引号包裹的搜索词
  3. 标题匹配:文章标题、商品名称的精确匹配

4. term 完全匹配

核心概念

term 查询是精确值匹配,它不会对查询字符串进行分词处理,而是直接在倒排索引中查找完全匹配的项。

重要区别:match vs term

特性 match term
是否分词 ✅ 分词 ❌ 不分词
匹配方式 分词后匹配 完全匹配
适用字段 text 类型 keyword、数值、日期

基本用法

json 复制代码
{
  "query": {
    "term": {
      "status": "published"
    }
  }
}

keyword 字段的精确匹配

这是 term 查询最常见的使用场景。keyword 类型字段不会被分词,适合精确匹配:

json 复制代码
{
  "query": {
    "term": {
      "category.keyword": "技术文章"
    }
  }
}

💡 text 与 keyword 的区别

假设有一个字段 title,值为 "Elasticsearch 搜索引擎":

json 复制代码
// title 是 text 类型
{ "match": { "title": "搜索" } }        // ✅ 匹配,会分词
{ "term": { "title": "搜索" } }         // ❌ 可能不匹配,取决于分词结果

// title.keyword 是 keyword 类型
{ "term": { "title.keyword": "Elasticsearch 搜索引擎" } }  // ✅ 完全匹配
{ "term": { "title.keyword": "搜索" } }                    // ❌ 不匹配

terms 多值匹配(类似 SQL IN)

当需要匹配多个值时,使用 terms

json 复制代码
{
  "query": {
    "terms": {
      "status": ["published", "draft", "archived"]
    }
  }
}

等同于 SQL:WHERE status IN ('published', 'draft', 'archived')

结合 bool 查询

实际开发中,terms 常与其他条件组合使用:

json 复制代码
{
  "query": {
    "bool": {
      "must": [
        {
          "terms": {
            "category": ["tech", "life", "travel"]
          }
        },
        {
          "range": {
            "publish_date": {
              "gte": "2024-01-01"
            }
          }
        }
      ]
    }
  }
}

5. multi_match 多字段匹配

核心概念

当需要在多个字段中搜索同一内容时,使用 multi_match。它会在指定字段中执行 match 查询,然后合并结果。

基本用法

json 复制代码
{
  "query": {
    "multi_match": {
      "query": "elasticsearch",
      "fields": ["title", "content", "tags"]
    }
  }
}

这会在 titlecontenttags 三个字段中搜索 "elasticsearch"。

字段权重 boosting

可以为不同字段设置不同的权重:

json 复制代码
{
  "query": {
    "multi_match": {
      "query": "elasticsearch",
      "fields": ["title^3", "content^1", "tags^2"]
    }
  }
}
  • title^3:标题字段权重为 3(最重要)
  • tags^2:标签字段权重为 2
  • content^1:内容字段权重为 1(默认)

查询类型 type 参数

multi_match 支持多种查询类型:

json 复制代码
{
  "query": {
    "multi_match": {
      "query": "quick brown fox",
      "fields": ["title", "content"],
      "type": "best_fields"
    }
  }
}
type 说明 适用场景
best_fields 默认值,取最高分字段 精确匹配场景
most_fields 匹配字段越多分数越高 多同义词字段
cross_fields 跨字段匹配,视为一个字段 人名分字段存储
phrase 在每个字段执行 match_phrase 短语搜索
phrase_prefix 短语前缀匹配 搜索建议

实际应用场景

场景:电商商品搜索

商品信息分散在多个字段:商品名称、商品描述、品牌、分类等。用户输入关键词时,需要在所有相关字段中搜索:

json 复制代码
{
  "query": {
    "multi_match": {
      "query": "iPhone 手机",
      "fields": ["product_name^3", "brand^2", "description", "category"],
      "type": "best_fields"
    }
  }
}

6. range 范围匹配

核心概念

range 查询用于数值、日期等范围条件,类似于 SQL 的 BETWEEN>< 等操作符。

基本用法

json 复制代码
{
  "query": {
    "range": {
      "price": {
        "gte": 100,
        "lte": 500
      }
    }
  }
}

查询价格在 100-500 之间的商品。

范围操作符详解

操作符 含义 英文全称 示例
gt 大于 Greater Than price: { "gt": 100 }
gte 大于等于 Greater Than or Equal price: { "gte": 100 }
lt 小于 Less Than price: { "lt": 500 }
lte 小于等于 Less Than or Equal price: { "lte": 500 }

日期范围查询

日期是 range 查询最常见的场景:

json 复制代码
{
  "query": {
    "range": {
      "publish_date": {
        "gte": "2024-01-01",
        "lt": "2024-12-31"
      }
    }
  }
}

日期数学表达式

ES 支持强大的日期数学运算,非常适合日志分析:

json 复制代码
// 查询最近 7 天的数据
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": "now-7d",
        "lt": "now"
      }
    }
  }
}
表达式 含义
now 当前时间
now-1h 1小时前
now-7d 7天前
now-1M 1个月前
now/d 今天开始
now/w 本周开始

时间区间查询示例

json 复制代码
{
  "query": {
    "range": {
      "created_at": {
        "gte": "2021-05-20T10:00:00",
        "lte": "2021-05-22T18:00:00",
        "time_zone": "+08:00"
      }
    }
  }
}

7. exists 判断字段是否存在

核心概念

exists 查询用于判断文档中是否存在某个字段,相当于 SQL 的 IS NULLIS NOT NULL

为什么需要 exists?

在 Elasticsearch 中,以下情况字段被认为"不存在":

  • 字段未定义
  • 字段值为 null[]
  • 字段存在但值为 null 且有 null_value 配置

查询字段不存在的文档

json 复制代码
{
  "query": {
    "bool": {
      "must_not": {
        "exists": {
          "field": "deleted_at"
        }
      }
    }
  }
}

等同于 SQL:WHERE deleted_at IS NULL

应用场景:查询未删除的记录(软删除场景)

查询字段存在的文档

json 复制代码
{
  "query": {
    "bool": {
      "must": {
        "exists": {
          "field": "email"
        }
      }
    }
  }
}

等同于 SQL:WHERE email IS NOT NULL

应用场景

  • 查询已绑定邮箱的用户
  • 查询已设置头像的用户
  • 查询有评论的文章

结合其他条件使用

json 复制代码
{
  "query": {
    "bool": {
      "must": [
        {
          "exists": {
            "field": "avatar_url"
          }
        },
        {
          "range": {
            "created_at": {
              "gte": "2024-01-01"
            }
          }
        }
      ]
    }
  }
}

查询有头像且在 2024 年之后注册的用户。


8. nested 嵌套字段查询

为什么需要 nested?

在 ES 中,对象数组会被"扁平化"存储。这会导致跨对象匹配的问题。

问题示例

假设有一个博客文档,包含评论数组:

json 复制代码
{
  "title": "Elasticsearch 教程",
  "comments": [
    { "name": "Alice", "age": 25 },
    { "name": "Bob", "age": 35 }
  ]
}

如果使用普通查询查找 name=Aliceage=35 的文档:

json 复制代码
{
  "query": {
    "bool": {
      "must": [
        { "match": { "comments.name": "Alice" } },
        { "match": { "comments.age": 35 } }
      ]
    }
  }
}

❌ 这个查询会错误地匹配到文档!因为 ES 内部存储为:

  • comments.name: ["Alice", "Bob"]
  • comments.age: [25, 35]

解决方案:nested 类型

首先,映射定义中需要声明 nested 类型:

json 复制代码
{
  "mappings": {
    "properties": {
      "comments": {
        "type": "nested",
        "properties": {
          "name": { "type": "text" },
          "age": { "type": "integer" }
        }
      }
    }
  }
}

nested 查询基本用法

json 复制代码
{
  "query": {
    "nested": {
      "path": "comments",
      "query": {
        "bool": {
          "must": [
            { "match": { "comments.name": "Alice" } },
            { "match": { "comments.age": 25 } }
          ]
        }
      }
    }
  }
}

现在只会匹配 name=Alice 且 age=25 在同一个嵌套对象中的文档。

参数说明

参数 说明
path 嵌套字段的路径
query 嵌套对象内部的查询
score_mode 分数计算方式:avgmaxsumnone
ignore_unmapped 字段不存在时是否报错

nested 聚合查询

json 复制代码
{
  "size": 0,
  "aggs": {
    "comments_agg": {
      "nested": {
        "path": "comments"
      },
      "aggs": {
        "min_age": {
          "min": {
            "field": "comments.age"
          }
        },
        "age_stats": {
          "stats": {
            "field": "comments.age"
          }
        }
      }
    }
  }
}

聚合结果示例

json 复制代码
{
  "aggregations": {
    "comments_agg": {
      "doc_count": 2,
      "min_age": { "value": 25 },
      "age_stats": {
        "min": 25,
        "max": 35,
        "avg": 30,
        "sum": 60
      }
    }
  }
}

实际应用场景

  1. 订单商品查询:订单中包含多个商品,按商品属性筛选
  2. 文章评论:查询特定用户的评论
  3. 用户标签:用户有多个标签,按标签组合筛选
  4. 问答系统:问题有多个答案,查询特定条件的答案

总结

查询类型速查表

查询类型 使用场景 是否分词 类似 SQL 最佳实践
match_all 查询所有文档 - SELECT * 配合分页使用
match 全文搜索、模糊匹配 ✅ 是 LIKE 配合 operator 精确控制
match_phrase 精确短语匹配 ❌ 否 LIKE '%phrase%' 使用 slop 增加灵活性
term 精确值匹配 ❌ 否 = 用于 keyword、数值字段
terms 多值匹配 ❌ 否 IN (...) 配合 bool 组合条件
multi_match 多字段搜索 ✅ 是 多字段 OR 使用权重优化排序
range 范围查询 - BETWEEN 善用日期数学表达式
exists 判断字段是否存在 - IS NULL 软删除、可选字段场景
nested 嵌套对象查询 - JOIN 查询 避免跨对象匹配问题

最佳实践建议

1. 查询选择原则
数据类型 推荐查询 原因
全文搜索 match 分词匹配,结果更相关
精确匹配 term + keyword 性能最优
短语搜索 match_phrase 保持词序
范围查询 range 支持日期计算
嵌套对象 nested 避免数据错误
2. 性能优化建议
  • 避免通配符开头的查询 :如 *test?test,会导致全索引扫描
  • 合理使用 filter 上下文:filter 不计算分数,可利用缓存
  • 控制返回字段 :使用 _source 只返回需要的字段
  • 分页优化 :深度分页使用 search_after 替代 from/size
json 复制代码
// filter 上下文示例
{
  "query": {
    "bool": {
      "must": { "match": { "content": "elasticsearch" } },
      "filter": [
        { "term": { "status": "published" } },
        { "range": { "publish_date": { "gte": "2024-01-01" } } }
      ]
    }
  }
}
3. 常见陷阱
陷阱 问题 解决方案
text 字段用 term 匹配不到 使用 match 或 .keyword
深度分页 性能问题 使用 search_after
nested 不用 nested 查询 结果错误 必须使用 nested 查询
大量 terms 查询慢 减少列表长度或优化索引

掌握这些查询语法,足以应对 90% 以上 的 ES 检索场景。

建议收藏这篇文章,作为日常开发的查询语法速查手册!💪


参考资源


如果觉得本文有帮助,欢迎点赞收藏 ⭐,有问题欢迎在评论区留言讨论!

相关推荐
拳打南山敬老院3 小时前
Context 不是压缩出来的,而是设计出来的
前端·后端·aigc
初次攀爬者3 小时前
Kafka + ZooKeeper架构基础介绍
后端·zookeeper·kafka
LucianaiB3 小时前
Openclaw 安装使用保姆级教程(最新版)
后端
华仔啊3 小时前
Stream 代码越写越难看?JDFrame 让 Java 逻辑回归优雅
java·后端
哈密瓜的眉毛美3 小时前
零基础学Java|第五篇:进制转换与位运算、原码反码补码
后端
开心就好20254 小时前
免 Xcode 的 iOS 开发新选择?聊聊一款更轻量的 iOS 开发 IDE kxapp 快蝎
后端·ios
Java编程爱好者4 小时前
为什么国内大厂纷纷”弃坑”MySQL,转投PostgreSQL阵营?
后端
神奇小汤圆4 小时前
金三银四Java面试题及答案汇总(2026持续更新)
后端
颜酱4 小时前
理解二叉树最近公共祖先(LCA):从基础到变种解析
javascript·后端·算法