Elasticsearch QueryDSL 教程
什么是 Elasticsearch QueryDSL
Elasticsearch Query DSL 是 Elasticsearch 用来构建和执行搜索查询的 JSON 格式查询语言,支持丰富的查询和过滤条件,帮助用户精准检索和分析数据。它类似于 MySQL 中的 SQL 查询语句(SELECT 语句),都是用来定义从数据库或索引中筛选和返回数据的规则和条件。
MySql 和 Elasticsearch 的区别
MySQL 概念 | Elasticsearch 概念 | 说明 |
---|---|---|
数据库(Database) | 索引(Index) | ES 中一个 Index 就相当于 MySQL 中的一个数据库,里面存储类结构相似的数据。比如 users 索引存放用户数据,orders 索引存放订单数据。 |
表(Table) | 类型(Type,7.x 后已废弃) | 早期 ES 在一个索引里可以有多种 Type,像数据库里的一张张表。但从 7.x 开始官方废弃 Type,一个 Index 只能有一种数据结构,相当于 一个索引 = 一张表。 |
行(Row) | 文档(Document) | ES 存储的最小单位是一个 Document,相当于 MySQL 里的一行数据。文档用 JSON 格式存储。 |
列(Column) | 字段(Field) | Document 里的每个 key/value 就是一个 Field,对应 MySQL 里的列。 |
主键(Primary Key) | _id | 每个 Document 都有唯一的 _id ,相当于主键,可以由 ES 自动生成,也可以手动指定。 |
SQL | Query DSL | MySQL 用 SQL 查询,ES 用 Query DSL(一种 JSON 风格的查询语法)。 |
数据类型介绍
一. 核心数据类型(Core datatypes)
类型名 | 说明 | 典型用途 |
---|---|---|
| 分词后的字符串,用于全文检索 | 商品描述、文章内容、评论、日志文本等 |
| 不分词的字符串,适合精确匹配和聚合 | 标签、状态码、分类、用户名、ID、邮箱等 |
| 布尔值(true / false) | 是否启用,开关标识 |
| 32位整数 | 年龄、数量、评分等 |
| 64位整数 | 大整数计数、时间戳 |
| 16位整数 | 较小范围数值 |
| 8位整数 | 非常小的整数范围 |
| 双精度浮点数 | 价格、权重、坐标等 |
| 单精度浮点数 | 轻量级浮点数 |
| 半精度浮点数 | 对精度要求较低的浮点数 |
| 以整数存储浮点数,乘以缩放因子存储 | 金额、价格(避免浮点误差) |
| 日期时间类型,支持多种格式 | 订单时间、日志时间 |
| 纳秒精度日期时间 | 高精度时间戳 |
二. 复杂数据类型(Complex datatypes)
类型名 | 说明 | 典型用途 |
---|---|---|
object | JSON 对象,可嵌套多层字段 | 复杂文档结构,如用户信息中嵌套地址对象 |
nested | 嵌套类型,支持多值数组中每个元素作为独立对象查询 | 商品规格列表、订单明细列表 |
三. 特殊数据类型(Specialized datatypes)
类型名 | 说明 | 典型用途 |
---|---|---|
geo_point | 地理坐标点(经纬度) | 地理位置定位、地图检索 |
geo_shape | 复杂地理形状(多边形、圆等) | 地理范围查询、地理围栏 |
ip | IPv4 或 IPv6 地址 | 服务器 IP、客户端 IP |
completion | 自动补全建议类型 | 搜索自动补全 |
token_count | 统计分词后词数 | 统计分析 |
murmur3 | MurmurHash3 哈希码 | 唯一标识生成 |
binary | 二进制数据 | 图片、文件二进制存储 |
join | 父子关系建模 | 一对多、多对多文档关联 |
HTTP 请求方法介绍
方法 | 作用 | 典型 Elasticsearch 场景 |
---|---|---|
GET | 获取数据或状态,不改变数据 | 查询文档、获取索引信息、获取集群状态 |
POST | 提交数据,创建资源或执行复杂操作 | 新增文档(自动生成ID)、执行复杂搜索请求、更新文档(部分更新)、批量操作 |
PUT | 创建或完全替换资源 | 创建索引、创建或覆盖指定ID的文档 |
DELETE | 删除资源 | 删除文档、删除索引、删除模板 |
PATCH | Elasticsearch 不常用 | ES 原生API一般不用 PATCH,但理论上用于部分更新(ES用POST+_update代替) |
HEAD | 获取资源的元信息,不返回主体 | 检查索引是否存在,检查文档是否存在 |
Endpoint 端点接口介绍
API 路径 | 作用说明 | 典型用途 |
---|---|---|
/{index}/_doc | 文档增删改查的基础接口 | 添加文档(POST)、获取文档(GET)、删除文档(DELETE) |
/{index}/_update | 文档的部分更新 | 局部修改文档字段,执行脚本等 |
/{index}/_search | 搜索接口 | 全文检索、复杂查询 |
/{index} | 索引的创建、删除、获取索引信息等 | 创建索引(PUT)、删除索引(DELETE)、获取索引信息(GET) |
/_bulk | 批量操作接口 | 批量新增、更新、删除文档 |
/_mapping | 获取或更新索引映射(字段类型定义) | 查看或调整字段类型和属性 |
/_settings | 获取或更新索引设置 | 调整分片、副本、刷新频率等 |
/_cat | 便捷的索引、节点、集群信息展示接口 | 查看集群状态、索引列表、节点健康等 |
/_cluster | 集群级别管理接口 | 查看集群健康、状态,管理集群设置 |
/_alias | 管理索引别名 | 创建、删除、查看别名 |
/_count | 统计文档数量 | 查询符合条件的文档总数 |
/_reindex | 索引重建 | 将数据从一个索引复制到另一个索引 |
/_flush | 手动刷新索引 | 强制把缓冲区数据写入磁盘,减少数据丢失风险 |
索引 (Index)(表) 相关命令
索引 就是 mysql 中的表,索引 = 表
创建索引
方式一:// 只创建索引结构
PUT /my_index // my_index:自定义的索引(表)名称
{"mappings": { // mappings 创建索引的关键字,表示 映射定义开始"properties": { // properties 创建索引的关键字,表示 定义字段集合"name": { // 自定义字段名称"type": "text" // 字段类型,text:分词字符串 全文检索字符串},"age": { // 自定义字段名称"type": "integer" // 字段类型,integer:32位整数类型},"status": { // 自定义字段名称"type": "keyword" // 字段类型,keyword:不分词字符串,用于精确匹配}}}
}
方式二:// 添加一行数据,索引结构不存在时自动创建索引。缺点:自动映射不够精准,复杂场景建议手动建索引。
POST /my_index/_doc
{"name": "Alice","age": 25
}
查询索引列表
查询所有索引
GET /_cat/indices
查询指定的索引,* 通配符的方式,通配符可以使用在开头、中间、结尾
GET /_cat/indices/前缀*?v
GET /_cat/indices/*后缀?v
GET /_cat/indices/*xxx*yyy*?v
GET /_cat/indices/*x*y*z*?v
查看索引数据结构
查看表结构
命令:
GET /索引名/_mapping
示例:
GET /my_index/_mapping
修改索引数据结构
!!! 不能直接修改已存在字段的类型
一旦索引创建且字段映射确定后,字段的类型是固定的,不能修改字段类型,比如把 text 改成 keyword 是不允许的。或者删除一个字段也是不可以的。
如果要修改字段类型,通常需要新建一个索引,重新定义 mapping,然后把数据 reindex(迁移)过去。
reindex 迁移示例:
需求:假设旧索引有字段 fullname,新索引改成了 name。
原索引:
PUT /old_users
{"mappings": {"properties": {"fullname": { "type": "text" },"age": { "type": "integer" },"status": { "type": "keyword" }}}
}
新索引:
PUT /new_users
{"mappings": {"properties": {"name": { "type": "text" },"age": { "type": "integer" },"status": { "type": "keyword" },"email": { "type": "keyword" }}}
}
迁移命令:
POST /_reindex
{"source": {"index": "old_users"},"dest": {"index": "new_users"},"script": {"source": """ctx._source.name = ctx._source.remove('fullname');"""}
}# ctx._source.name 新索引 新字段
# ctx._source.remove('fullname') 旧索引 旧字段
删除索引
删除表
删除指定的索引
命令:
DELETE /索引名
删除多个索引
命令:使用 * 通配符
DELETE /*索*引*名*
添加数据 相关命令
添加一条数据,不指定id,es自动生成id
命令:
POST /my_index/_doc // my_index: 索引名,_doc: 添加数据关键字
{"name": "Alice", // key: value,字段名和字段数据"age": 25,"status": "active","joinDate": "2025-08-11"
}
添加成功返回
{"_index": "my_index","_id": "vDD0lpgBikDxJFOszVbZ", // id 自动生成"_version": 1,"result": "created","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 0,"_primary_term": 1
}
添加一条数据,指定id
PUT:指定 _id(这里是 1),如果 ID 存在,会覆盖(替换整条记录),如果 ID 不存在,会添加
命令:
POST /my_index/_doc/1 // my_index: 索引名,_doc: 添加数据关键字,1:你指定的id
{"name": "Bob", // key: value,字段名和字段数据"age": 30,"status": "inactive","joinDate": "2024-01-01"
}
添加成功返回
{"_index": "my_index","_id": "1", // id 自己指定的"_version": 1,"result": "created","_shards": {"total": 2,"successful": 1,"failed": 0},"_seq_no": 1,"_primary_term": 1
}
局部插入或更新
- 如果 _id=3 的文档已经存在
- 用 doc 里的内容部分更新它(只改 age 字段,不会影响其他字段)。
- 如果 _id=3 的文档不存在
- 直接把 doc 里的内容当作一个新文档插入(相当于 upsert 功能)。
POST /my_index/_update/3 // _update:更新数据关键字
{"doc": { "age": 28 },"doc_as_upsert": true // 开启更新插入
}
批量添加
跳过
查询数据 相关命令
查询类型说明
一、基础查询类型(基础检索)
查询类型 | 说明 | 备注 |
---|---|---|
match_all | 返回所有文档 | 全量扫描 |
match | 全文匹配,分词后匹配 | 适合 text 类型字段 |
term | 精确匹配(不分词) | 适合 keyword 、数字等 |
terms | 多值精确匹配 | 多个精确值匹配 |
range | 范围查询(如大于、小于、介于) | 数字、日期等 |
exists | 判断字段是否存在 | |
prefix | 前缀匹配 | |
wildcard | 通配符匹配(支持 * 、? ) | 慎用,性能较差 |
regexp | 正则表达式匹配 | 性能较差 |
fuzzy | 模糊匹配(拼写错误容忍) | |
ids | 根据文档 ID 精确查找 |
二、逻辑组合查询(复合查询)
查询子句 | 说明 | 备注 |
---|---|---|
bool | 复合查询,组合多个子句 | must 、should 、must_not 、filter |
- must | 必须匹配,逻辑 AND | 查询条件必须满足 |
- should | 应该匹配,逻辑 OR | 满足一条即匹配 |
- must_not | 必须不匹配,逻辑 NOT | 排除满足条件文档 |
- filter | 过滤查询,不计分,缓存优化 | 用于限制结果 |
constant_score | 包裹查询,返回固定分数,忽略相关性评分 | 用于过滤条件 |
dis_max | 多个查询取最高得分 |
三、全文检索增强
查询类型 | 说明 |
---|---|
multi_match | 多字段全文匹配 |
common_terms | 高频词优化,减少高频词干扰 |
query_string | 支持复杂查询语法 |
simple_query_string | 简化版查询语法,容错性强 |
四、地理位置相关查询
查询类型 | 说明 |
---|---|
geo_distance | 距离查询 |
geo_bounding_box | 矩形区域查询 |
geo_polygon | 多边形区域查询 |
五、脚本和特殊查询
查询类型 | 说明 |
---|---|
script | 自定义脚本查询 |
function_score | 自定义相关性评分 |
more_like_this | 找相似文档 |
percolate | 反向查询,存储查询以匹配新文档 |
nested | 嵌套文档查询 |
has_child / has_parent | 父子文档关系查询 |
span_queries | 精细控制词语位置的查询 |
六、查询参数与选项
参数 | 说明 |
---|---|
size | 返回文档数量,默认 10 |
from | 分页起始位置 |
_source | 控制返回字段 |
sort | 排序字段和顺序 |
timeout | 查询超时时间 |
track_total_hits | 是否精确统计总命中数,默认最多10000 |
min_score | 过滤分数低于阈值的文档 |
highlight | 高亮显示查询关键字 |
collapse | 按字段折叠结果(类似 SQL 的 group by) |
七、常用聚合(Aggregation)
聚合类型 | 说明 |
---|---|
aggs / aggregations | 聚合的入口,定义统计分析操作。统计、分组、分桶 |
buckets | 聚合结果中的分组容器,每个桶代表一类数据,里面有符合条件的文档集合 |
terms | 按字段分组统计 |
range | 数值或日期范围分组 |
- ranges | range 聚合中定义的区间数组,比如 { "from": 20, "to": 30 } |
- key | 聚合结果返回的每个桶的名称,方便引用 |
- doc_count | 聚合结果返回的桶中符合条件的文档数量 |
metrics | 计算数值统计,如 avg , sum , min , max |
date_histogram | 时间分桶 |
nested | 嵌套结构聚合 |
filter | 条件过滤聚合 |
八、操作符
操作符 | 说明 |
---|---|
range | 范围查询 |
- gte | 大于等于 |
- gt | 大于 |
- lte | 小于等于 |
- lt | 小于 |
查询指定索引的全部数据
相当于 mysql 的:select
示例:
GET /索引名/_search // _search:查询关键字
{"query": {"match_all": {}},"size": 20
}
说明:
match_all 会返回所有文档,但是你只能看到10条数据。
默认情况下,Elasticsearch 出于性能考虑,一次查询只返回 10 条记录,而不是把所有数据都一下子返回给你。
如果你想一次性多拿一些,可以加上 size 参数。size 不能无限大,太大会影响性能。
如果数据量很大,要分页查,可以用 from + size:
分页查询 命令:
GET /索引名/_search
{"from": 0, // from 表示跳过多少条"size": 20, // size 表示取多少条"query": {"match_all": {}}
}
精确查询
term:精确查询的关键字
示例:
GET /索引名/_search
{"query": {"term": {"status": "active" // 查询 字段 status 的值是 active 的数据,适合 keyword 类型}}
}
全文检索(分词查询)
全文检索一个字段
match:单字段,全文检索的关键字,适合 text 类型
示例:
GET /索引名/_search
{"query": {"match": {"description": "quick fox" // description 要查询的字段}}
}
说明:
- Elasticsearch 会先对字段 description 里的文本和查询语句 "quick fox" 都做分词处理,比如拆成两个词 "quick" 和 "fox"。
- 然后它会找出所有包含这两个词(或其中一个词,相关度越高的排前面)的文档。
- 这种查询会根据词出现的频率、位置等因素计算相关度分数,结果按相关度排序。
全文检索多个字段
示例一:
multi_match:多字段,全文检索的关键字,适合 text 类型
GET /索引名/_search
{"query": {"multi_match": {"query": "quick fox","fields": ["description", "title"]}}
}
说明:
- query: 你要搜索的关键词 "quick fox"
- fields: 指定多个字段一起匹配,这里是 "description" 和 "title"
- Elasticsearch 会在这两个字段中分别分词匹配,然后结合相关度返回结果。
示例二:
bool + should 组合
GET /索引名/_search
{"query": {"bool": {"should": [{ "match": { "description": "quick fox" } },{ "match": { "title": "quick fox" } }]}}
}
说明:
是更通用的复合查询写法,表示文档满足任意一个字段匹配即可。
每个子查询独立执行,相关性得分相加。
可灵活组合更多复杂条件。
multi_match 和 bool + should 组合 的区别
- 如果只是简单地对多个字段全文搜索,multi_match 推荐使用,写法简洁且功能丰富。
- 如果需要更复杂的逻辑控制,比如不同字段不同查询类型、权重调整,或者要结合其它条件,使用 bool + should 更灵活。
组合查询
多条件查询
示例:
GET /索引名/_search
{"query": {"bool": {"must": [{ "term": { "status": "active" } },{ "range": { "age": { "gte": 18 } } }],"must_not": [{ "term": { "name": "Bob" } }],"should": [{ "match": { "description": "developer" } }],"filter": [{ "exists": { "field": "email" } }]}}
}
说明:
- must (必须满足)
- 表示:status 字段的值必须是 "active",同时 age 字段的值必须大于等于 18。
- must_not:必须不满足(排除)
- 表示:排除掉 name 字段的值是 "Bob" 的文档。
- should:满足则加分,满足1条即可(尽量满足)
- 表示:如果文档的 description 字段的值中包含 "developer",它会被认为更相关,得分更高,但不满足也没关系。
- filter:过滤条件,不影响评分,性能优
- 表示:只匹配那些有 email 字段存在的文档。
范围查询
示例:
查询日期范围内的数据
GET /索引名/_search
{"query": {"range": {"joinDate": {"gte": "2024-01-01","lte": "2025-12-31"}}}
}
分页查询
from + size 组合
示例:
跳过前10条,返回后面10条。
GET /索引名/_search
{"from": 10,"size": 10,"query": {"match_all": {}}
}
排序
sort 排序关键字
示例:
GET /索引名/_search
{"sort": [{ "age": "desc" },{ "_score": "desc" }],"query": {"match_all": {}}
}
说明:
- 先按年龄降序,再按相关度降序。
- _score:当你执行搜索查询时,Elasticsearch 会根据查询条件计算每个匹配文档的相关度分数(score)。这个分数越高,说明该文档与查询越相关,越匹配用户的搜索意图。_score 是这个分数的具体数值。
高亮显示
highlight:高亮关键字
示例:
GET /索引名/_search
{"query": {"match": { "description": "quick fox" }},"highlight": {"fields": {"description": {}}}
}
返回结果:
{"_source": {"name": "Alice","age": 25,"status": "active","joinDate": "2025-08-11","description": "The quick brown fox jumps over the lazy dog"},"highlight": {"description": ["The <em>quick</em> brown <em>fox</em> jumps over the lazy dog"]}
}
说明:
- highlight:这是高亮设置,告诉 Elasticsearch 需要对 description 字段中匹配的关键词做高亮处理。默认用 <em></em> 标签包裹匹配的词(你可以自定义标签)。
- 使用 highlight 后,返回的文档中会多一个 highlight 字段。前端拿到这个 highlight.description 的内容,可以用来在页面上用颜色、加粗等方式突出显示关键词,提升用户体验。
聚合查询
示例:
按年龄段分组统计数量
GET /my_index/_search
{"size": 0,"aggs": {"age_ranges": {"range": {"field": "age","ranges": [{ "to": 20 },{ "from": 20, "to": 30 },{ "from": 30 }]}}}
}
返回结果:
"aggregations": {"age_ranges": {"buckets": [{ "key": "*-20", "to": 20, "doc_count": 15 },{ "key": "20-30", "from": 20, "to": 30, "doc_count": 40 },{ "key": "30-*", "from": 30, "doc_count": 25 }]}
}
说明:
- "size": 0:不返回匹配的具体文档,只返回聚合结果。这样可以节省网络传输,适合只想看统计结果。
- aggs:aggs 是 aggregations(聚合) 的简写。用于对查询结果进行统计分析,比如计算总数、求平均、分组、范围统计等。返回的是统计结果,不是具体文档内容。
- age_ranges:是你给这个聚合起的名字,名字可以随便取。
- range:表示使用范围聚合
- "field": "age":表示要聚合的字段是 age 字段。
- ranges:聚合条件
- to: 20:统计所有 age 小于 20 的文档数量。
- from: 20, to: 30:统计所有 20 <= age < 30 的文档数量。
- from: 30:统计所有 age 大于等于 30 的文档数量。