分布式专题——46 ElasticSearch高级查询语法Query DSL实战
1 概述与数据准备
-
Query DSL 是 Elasticsearch 提供的领域专用语言(Domain Specified Language),通过 Rest API 传递 JSON 格式的请求体(RequestBody) 与 ES 交互,具备丰富的查询语法,让数据检索更强大、简洁;
-
官方文档链接:[Query DSL | Elasticsearch Guide 8.14] | Elastic;
-
基本语法:用于对指定索引执行检索操作
GET /<index_name>/_search {json请求体数据}
-
数据准备:
DELETE /employee PUT /employee {"settings": {"number_of_shards": 1,"number_of_replicas": 1},"mappings": {"properties": {"name": {"type": "keyword" # keyword 类型适用于精确匹配,不进行分词},"sex": {"type": "integer"},"age": {"type": "integer"},"address": { # 主类型为 text,并指定分词器为 ik_max_word(IK 分词器的“细粒度分词”模式,尽可能拆分出更多词汇)"type": "text","analyzer": "ik_max_word","fields": { # 多字段映射 fields.keyword:类型为 keyword,用于对 address 进行精确匹配场景"keyword": {"type": "keyword"}}},"remark": {"type": "text", # 主类型为 text,指定分词器为 ik_smart(IK 分词器的“粗粒度分词”模式,拆分出相对少而关键的词汇)"analyzer": "ik_smart","fields": { # 多字段映射 fields.keyword:类型为 keyword,用于精确匹配场景"keyword": {"type": "keyword"}}}}} }# 批量导入多条文档,每两条 JSON 为一组(第一条是操作描述,第二条是文档内容) POST /employee/_bulk {"index":{"_index":"employee","_id":"1"}} {"name":"张三","sex":1,"age":25,"address":"广州天河公园","remark":"java developer"} {"index":{"_index":"employee","_id":"2"}} {"name":"李四","sex":1,"age":28,"address":"广州荔湾大厦","remark":"java assistant"} {"index":{"_index":"employee","_id":"3"}} {"name":"王五","sex":0,"age":26,"address":"广州白云山公园","remark":"php developer"} {"index":{"_index":"employee","_id":"4"}} {"name":"赵六","sex":0,"age":22,"address":"长沙橘子洲","remark":"python assistant"} {"index":{"_index":"employee","_id":"5"}} {"name":"张龙","sex":0,"age":19,"address":"长沙麓谷企业广场","remark":"java architect assistant"} {"index":{"_index":"employee","_id":"6"}} {"name":"赵虎","sex":1,"age":32,"address":"长沙麓谷兴工国际产业园","remark":"java architect"}
在 ElasticSearch 中,多字段映射(Multi-fields Mapping) 是一种让一个字段同时拥有多种数据类型或分词策略的技术,以便在不同业务场景下(精确匹配、模糊检索、聚合分析等)灵活使用该字段;
以
address
字段为例,业务中可能同时存在两种需求:- 需求一:模糊检索(如搜索“广州”,希望匹配到“广州天河公园”“广州荔湾大厦”等包含“广州”的地址)
- 需求二:精确匹配(如搜索“广州天河公园”,仅希望匹配完全一致的地址)
如果只给
address
定义一种类型,无法同时满足这两种场景。此时就需要多字段映射:"address": {"type": "text","analyzer": "ik_max_word","fields": {"keyword": {"type": "keyword"}} }
- 主字段
address
:类型是text
,分词器为ik_max_word
(细粒度分词),用于模糊检索场景(比如全文搜索“广州”,能匹配到所有包含“广州”的地址) - 子字段
address.keyword
:类型是keyword
(不分词,精确存储原始值),用于精确匹配场景(比如筛选地址为“广州天河公园”的文档,或对地址进行聚合统计)
2 match_all
:匹配所有文档
2.1 match_all
查询
-
match_all
是 Elasticsearch 中用于匹配索引中所有文档的特殊查询类型,不附加任何查询条件; -
基本语法:
GET /<your-index-name>/_search {"query": {"match_all": {}} }
-
高级用法(结合评分排序和结果条数):可通过
size
控制返回文档数,通过sort
排序。示例:GET /<your-index-name>/_search {"query": {"match_all": {}},"size": 10,"sort": [{"_score": {"order": "desc"}}] }
2.2 _source
字段控制
-
_source
用于控制查询结果中是否返回原始文档数据,支持灵活的字段过滤策略; -
不返回源数据,仅查元字段:
GET /<your-index-name>/_search {"query": {"match_all": {}},"_source": false }
-
返回指定字段:仅返回
field1
和field2
字段的内容GET /<your-index-name>/_search {"query": {"match_all": {}},"_source": ["field1","field2"] }
-
返回匹配前缀的字段:仅返回以
obj.
开头的字段(如obj.name
、obj.age
等)GET /<your-index-name>/_search {"query": {"match_all": {}},"_source": "obj.*" }
2.3 结果分页(size
、from
)
-
用于控制查询结果的条数和分页,实现类似数据库的分页查询;
-
size
返回指定条数:仅返回 3 条文档GET /employee/_search {"query": {"match_all": {}},"size": 3 }
-
from&size
分页查询:from
表示“跳过前 N 条”,size
表示“返回 M 条”。下面示例代码中“跳过 0 条,返回 5 条”,即第 1-5 条文档GET /employee/_search {"query": {"match_all": {}},"from": 0,"size": 5 }
2.4 排序(sort
)
-
用于指定字段的排序规则(升序
asc
或降序desc
),支持多字段组合排序; -
根据单个字段排序:按
age
降序排列GET /employee/_search {"query": {"match_all": {}},"sort": [{"age": "desc"}] }
-
排序同时分页:先按
age
降序,再跳过 2 条、返回 5 条GET /employee/_search {"query": {"match_all": {}},"sort": [{"age": "desc"}],"from": 2,"size": 5 }
-
结合
_source
过滤字段排序:排序的同时,仅返回name
和address
字段GET /employee/_search {"query": {"match_all": {}},"_source": ["name","address"] }
3 精确匹配
- 精确匹配是指搜索内容不经过文本分析,直接用于文本匹配,类似数据库的 SQL 精确查询;
- 它主要适用于结构化数据(如 ID、状态、标签等),且搜索对象多为索引的
non-text
类型字段(如keyword
、integer
等)。
3.1 term
查询——单字段精确匹配
-
term
查询用于单字段的精准匹配场景; -
适用字段:仅适用于未经过分词处理的
keyword
类型字段(若用于text
类型字段,不会报错,但结果通常不符合预期,因为text
字段会被分词); -
基本语法:
GET /{index_name}/_search {"query": {"term": {"{field.keyword}": {"value": "your_exact_value"}}} }
- 其中,
{index_name}
是索引名,{field.keyword}
是keyword
类型的字段(如下面示例中的name
、address.keyword
),value
是要精确匹配的值;
- 其中,
-
term
查询的示例与注意事项-
示例1:匹配
keyword
类型字段(正确用法)。查询姓名为“张三”的员工(name
字段是keyword
类型):GET /employee/_search {"query": {"term": {"name": {"value": "张三"}}} }
-
示例2:误用
text
类型字段(错误示范)。若直接对address
(text
类型)执行term
查询“广州白云”,会因address
被分词(如拆分为“广州”“白云”等)而无法匹配到“广州白云山公园”这条数据:GET /employee/_search {"query":{"term": {"address": {"value": "广州白云"}}} }
-
示例3:正确使用
text
字段的keyword
多字段。对address
的keyword
多字段address.keyword
执行精确匹配“广州白云山公园”,即可命中数据:GET /employee/_search {"query":{"term": {"address.keyword": {"value": "广州白云山公园"}}} }
-
-
term
处理多值字段(数组)的逻辑-
当字段是多值数组(如
interest: ["跑步","篮球"]
)时,term
查询的匹配逻辑是**“包含”而非“等于”**; -
示例:批量导入
people
索引的多值文档后,查询interest.keyword
包含“跑步”的文档,会匹配到interest
为["跑步","篮球"]
和["跳舞","唱歌","跑步"]
的两条数据:POST /people/_bulk {"index":{"_id":1}} {"name":"小明","interest":["跑步","篮球"]} {"index":{"_id":2}} {"name":"小红","interest":["跳舞","画画"]} {"index":{"_id":3}} {"name":"小丽","interest":["跳舞","唱歌","跑步"]}POST /people/_search {"query": {"term": {"interest.keyword": {"value": "跑步"}}} }
-
-
term
查询的性能优化(constant_score
)。term
查询默认会进行相关度算分(如 TF-IDF),但在仅需“精确过滤”的场景下,算分是额外开销。可通过constant_score
将其转换为filter
,实现以下优化:-
忽略 TF-IDF 算分,减少性能开销;
-
利用 Elasticsearch 的缓存机制,提升后续查询速度;
-
示例:
GET /employee/_search {"query": {"constant_score": {"filter": {"term": {"address.keyword": "广州白云山公园"}}}} }
-
3.2 terms
查询——多值精确匹配
-
terms
检索是 Elasticsearch 中用于多值精准匹配的查询方式,允许在单个查询中指定多个词条对字段进行精确匹配。它针对未分析的字段(如keyword
、数字、日期等结构化数据字段)生效,适用于筛选包含多个特定值的场景(如多标签、多状态的文档); -
适合从文档中查找包含多个特定值的字段,典型场景包括:
-
筛选具有多个特定标签的内容(如同时匹配“标签A”和“标签B”的文档)
-
匹配多个状态值(如“待处理”“已完成”的任务)
-
对数字、日期等结构化数据的多值精确检索
-
-
在 Elasticsearch 8.x 中,
terms
查询的语法结构如下:GET /<index_name>/_search {"query": {"terms": {"<field_name>": ["value1","value2","value3",...]}} }
-
<index_name>
:要查询的索引名称; -
<field_name>
:要执行terms
查询的字段名(需为keyword
或其他未分词的结构化字段类型); -
方括号内的
value1, value2...
:希望匹配的多个精确值列表;
-
-
以
employee
索引的remark.keyword
字段为例,查询同时匹配“java assistant”和“java architect”的文档:POST /employee/_search {"query": {"terms": {"remark.keyword": ["java assistant", "java architect"]}} }
3.3 range
查询——范围查询
-
range
检索是 Elasticsearch 中用于指定字段值在给定范围内的文档检索类型,适用于数字、日期或其他可排序数据类型的字段,支持gt
(大于)、gte
(大于等于)、lt
(小于)、lte
(小于等于)等比较操作符,实现灵活的区间查询; -
在 Elasticsearch 8.x 中,
range
查询的语法结构如下:GET /<index_name>/_search {"query": {"range": {"<field_name>": {"gte": <lower_bound>,"lte": <upper_bound>,"gt": <greater_than_bound>,"lt": <less_than_bound>}}} }
-
<index_name>
:要查询的索引名称 -
<field_name>
:要执行range
查询的字段名(需为数字、日期等可排序类型) -
gte
:大于等于(Greater Than or Equal) -
lte
:小于等于(Less Than or Equal) -
gt
:严格大于(Greater Than) -
lt
:严格小于(Less Than) -
<lower_bound>
等:指定的数值或日期边界
-
-
以
employee
索引的age
字段为例,查询年龄在 25 到 28 之间(包含 25 和 28)的员工:POST /employee/_search {"query": {"range": {"age": {"gte": 25,"lte": 28}}} }
-
日期范围查询(含测试数据准备)
-
生成测试数据:创建
notes
索引,定义created_at
为date
类型(格式yyyy-MM-dd HH:mm:ss
),并批量导入带创建日期的笔记文档PUT /notes {"settings": {"number_of_shards": 1,"number_of_replicas": 0},"mappings": {"properties": {"title": {"type": "text"},"content": {"type": "text"},"created_at": {"type": "date", "format": "yyyy-MM-dd HH:mm:ss"}}} }POST /notes/_bulk {"index":{"_id":"1"}} {"title":"Note 1","content":"This is the first note.","created_at":"2023-07-01 12:00:00"} {"index":{"_id":"2"}} {"title":"Note 2","content":"This is the second note.","created_at":"2023-07-05 15:30:00"} {"index":{"_id":"3"}} {"title":"Note 3","content":"This is the third note.","created_at":"2023-07-10 08:45:00"} {"index":{"_id":"4"}} {"title":"Note 4","content":"This is the fourth note.","created_at":"2023-07-15 20:15:00"}
-
查询
created_at
在 2023-07-05 到 2023-07-10 之间的笔记:POST /notes/_search {"query": {"range": {"created_at": {"gte": "2023-07-05 00:00:00","lte": "2023-07-10 23:59:59"}}} }
-
Elasticsearch 支持相对时间表达式,用于动态指定时间范围,常见表达式:
-
now
:当前时间点; -
now-1d
:当前时间点前推 1 天; -
now-1w
:当前时间点前推 1 周; -
now-1M
:当前时间点前推 1 个月; -
now-1y
:当前时间点前推 1 年; -
now+1h
:当前时间点后推 1 小时。
-
-
示例:查询
product
索引中,date
在“当前时间前推 2 年”之后的产品文档:GET /product/_search {"query": {"range": {"date": {"gte": "now-2y"}}} }
-
3.4 exists
——是否存在查询
-
exists
检索用于筛选具有特定字段值的文档,判断文档中是否存在某个字段,或该字段是否包含非空值。它能有效过滤缺少关键信息的文档,聚焦包含所需数据的结果; -
典型场景包括:
-
数据完整性检查(如筛选包含“联系方式”字段的用户文档)
-
查询具有特定属性的文档(如包含“标签”字段的内容)
-
对可选字段进行筛选(如区分有“备注”和无“备注”的订单)
-
-
基本语法:
GET /<index_name>/_search {"query": {"exists": {"field": "missing_field"}} }
-
<index_name>
:要查询的索引名称 -
field
:指定要检查是否存在的字段名
-
-
以
employee
索引为例,查询存在remark
字段的文档:GET /employee/_search {"query": {"exists": {"field": "remark"}} }
3.5 ids
——根据一组id查询
-
ids
检索是 Elasticsearch 中用于根据一组文档 ID 快速召回相关数据的查询方法,能高效实现特定文档的检索,尤其适用于已知具体文档 ID 的场景; -
典型场景包括:
-
已知多个文档 ID,需批量获取这些文档的详细信息
-
基于业务逻辑(如用户收藏、订单列表)中记录的 ID 集合,快速检索对应文档
-
-
基本语法
GET /<index_name>/_search {"query": {"ids": {"values": ["id1", "id2", "id3", ...]}} }
-
<index_name>
:要查询的索引名称 -
values
:指定要检索的文档 ID 列表(支持字符串、数字等 ID 类型)
-
-
以
employee
索引为例,查询 ID 为1
和2
的员工文档:GET /employee/_search {"query": {"ids": {"values": [1,2]}} }
3.6 prefix
——前缀匹配
-
prefix
查询是对分词后的term
进行前缀搜索,具有以下关键特性:-
不会对搜索的字符串分词,传入的前缀就是要查找的前缀
-
默认不计算相关性分数,所有匹配文档的相关分数均为 1
-
原理是遍历所有倒排索引,比较每个词项是否以指定前缀开头
-
-
用于搜索指定字段中以特定前缀开头的文档,语法结构如下:
GET /<index_name>/_search {"query": {"prefix": {"your_field_name": {"value": "your_prefix_string"}}} }
-
<index_name>
:目标索引名称 -
your_field_name
:要检索的字段名(仅适用于keyword
类型字段) -
your_prefix_string
:要匹配的前缀字符串
-
-
适用场景:常用于自动补全、搜索提示等功能(如用户输入“java”,提示“java developer”“java architect”等);
-
字段限制:仅适用于
keyword
类型字段(若用于text
类型字段,因分词机制会导致结果不符合预期); -
以
employee
索引的address
字段为例,对比两种查询方式的差异:-
错误示例(使用
text
类型的address
字段):GET /employee/_search {"query": {"prefix": {"address": {"value": "广州白云山"}}} }
- 因
address
是text
类型(会分词),该查询无法命中“广州白云山公园”这类文档(分词后“广州白云山”不是独立词项);
- 因
-
正确示例(使用
keyword
类型的address.keyword
字段):GET /employee/_search {"query": {"prefix": {"address.keyword": {"value": "广州白云山"}}} }
address.keyword
是keyword
类型(不分词,存储原始值),因此能匹配到“广州白云山公园”这类以“广州白云山”为前缀的文档。
-
3.7 wildcard
——通配符匹配
-
wildcard
检索是 Elasticsearch 中支持通配符匹配的查询类型,允许通过通配符表达式匹配文档的字段值,适用于对“部分已知内容的文本字段”进行模糊检索(如文件名、产品型号等有规律的字段); -
通配符规则
-
星号(
*
):表示零或多个字符,可匹配任意长度的字符串 -
问号(
?
):表示一个字符,可匹配任意单个字符
-
-
适用场景:对部分已知、有规律的字段进行模糊检索(如“产品型号以
ABC*
开头”“文件名包含??.pdf
”等场景); -
性能注意事项:通配符查询可能带来较高的计算负担,尤其是在海量文档场景下,需谨慎使用;
-
基本语法
GET /<index_name>/_search {"query": {"wildcard": {"your_field_name": {"value": "your_search_pattern"}}} }
-
<index_name>
:目标索引名称 -
your_field_name
:要检索的字段名(通常为keyword
类型,确保精确匹配通配符模式) -
your_search_pattern
:包含通配符(*
或?
)的检索模式
-
-
以
employee
索引的address.keyword
字段为例,查询地址中包含“州”且以“公园”结尾的文档:GET /employee/_search {"query": {"wildcard": {"address.keyword": {"value": "*州*公园"}}} }
3.8 regexp
——正则匹配查询
-
regexp
检索是一种基于正则表达式的检索方法,功能强大但性能开销较高,建议非必要情况下避免使用,以保证查询性能的高效稳定。它比wildcard
查询更灵活强大,可用于搜索满足特定模式的文本; -
用于在指定字段中执行正则表达式匹配,语法结构如下:
GET /<index_name>/_search {"query": {"regexp": {"your_field_name": {"value": "your_search_pattern"}}} }
-
<index_name>
:目标索引名称 -
your_field_name
:要检索的字段名 -
your_search_pattern
:正则表达式模式(需符合 Elasticsearch 支持的正则语法)
-
-
适用场景:需对文本进行复杂模式匹配的场景(如匹配特定格式的手机号、邮箱,或符合自定义规则的字符串);
-
性能建议:因正则表达式的计算开销大,非必要时避免使用,优先选择
match
、term
等轻量查询; -
以
employee
索引的remark
字段为例,查询remark
中以java.
开头、后续跟任意字符的文档:GET /employee/_search {"query": {"regexp": {"remark": {"value": "java.*"}}} }
3.9 fuzzy
——支持编辑距离的模糊查询
-
fuzzy
检索是一种支持编辑距离的模糊查询功能,可在用户输入存在拼写错误或上下文不一致时,返回与搜索词相似的文档。它通过编辑距离算法度量输入词与文档词条的相似程度,兼顾搜索结果相关性与容错能力; -
编辑距离是指“从一个单词转换到另一个单词需要编辑单字符的次数”。例如:
-
“中文集团”到“中威集团”编辑距离为 1(仅需修改一个字符);
-
若
fuzziness
设为 2,还会匹配编辑距离为 2 的“东东集团”;
-
-
基本语法与参数说明:
GET /<index_name>/_search {"query": {"fuzzy": {"your_field": {"value": "search_term","fuzziness": "AUTO","prefix_length": 1}}} }
-
fuzziness
:用于设置编辑距离,默认值为AUTO
,支持数值[0, 1, 2]
(超出范围会报错); -
prefix_length
:搜索词的前缀长度,在此长度内不应用模糊匹配(默认 0,即整个词都模糊匹配);
-
-
以
employee
索引的address
字段为例,查询与“白运山”编辑距离为 1 的文档:GET /employee/_search {"query": {"fuzzy": {"address": {"value": "白运山","fuzziness": 1}}} }
3.10 terms_set
——解决多值字段中的文档匹配问题
-
terms_set
检索是 Elasticsearch 中用于解决多值字段文档匹配问题的检索类型,能处理具有多个属性、分类或标签的复杂数据。它允许检索“至少匹配指定数量词项”的文档,匹配数量可设为固定值或基于其他字段/脚本动态计算; -
适用于多值字段的精细匹配场景,典型场景包括:
-
标签系统(如筛选同时包含多个标签的内容)
-
技能匹配(如招聘中匹配满足一定技能数量的候选人)
-
电子商务系统(如筛选具有多个属性的商品)等
-
-
基本语法与参数说明
GET /<index_name>/_search {"query": {"terms_set": {"<field_name>": {"terms": ["<term1>", "<term2>", ...],"minimum_should_match_field": "<字段名>" or"minimum_should_match_script": {"source": "<脚本>"}}}} }
-
<field_name>
:要查询的多值字段(通常为keyword
类型) -
terms
:用于匹配的词项列表 -
minimum_should_match_field
:指定一个字段,其值作为“最少需要匹配的词项数” -
minimum_should_match_script
:通过自定义脚本动态计算最少需要匹配的词项数
-
-
创建
movies
索引,定义多值字段tags
(keyword
类型)和用于动态匹配的tags_count
(integer
类型),并批量导入测试文档:PUT /movies {"mappings": {"properties": {"title": { "type": "text" },"tags": { "type": "keyword" },"tags_count": { "type": "integer" }}} }POST /movies/_bulk {"index":{"_id":1}} {"title":"电影1", "tags":["喜剧","动作","科幻"], "tags_count":3} {"index":{"_id":2}} {"title":"电影2", "tags":["喜剧","爱情","家庭"], "tags_count":3} {"index":{"_id":3}} {"title":"电影3", "tags":["动作","科幻","家庭"], "tags_count":3}
-
使用固定数量的 term 进行匹配
-
方式1:通过
minimum_should_match
指定固定匹配数(如至少匹配 2 个词项):GET /movies/_search {"query": {"terms_set": {"tags": {"terms": ["喜剧", "动作", "科幻"],"minimum_should_match": 2}}} }
- 该查询会匹配
tags
中至少包含“喜剧、动作、科幻”中 2 个词项的文档(如“电影1”包含 3 个,“电影3”包含“动作、科幻”2 个,均会命中);
- 该查询会匹配
-
方式2:通过
minimum_should_match_script
脚本指定固定匹配数(如脚本返回“2”):GET /movies/_search {"query": {"terms_set": {"tags": {"terms": ["喜剧", "动作", "科幻"],"minimum_should_match_script": {"source": "2"}}}} }
- 效果与上述
minimum_should_match: 2
一致;
- 效果与上述
-
-
使用动态计算的 term 数量进行匹配
-
方式1:通过
minimum_should_match_field
关联字段(如tags_count
),以字段值作为匹配数:GET /movies/_search {"query": {"terms_set": {"tags": {"terms": ["喜剧", "动作", "科幻"],"minimum_should_match_field": "tags_count"}}} }
- 因
tags_count
为 3,该查询要求tags
至少匹配 3 个词项,仅“电影1”会命中。
- 因
-
方式2:通过
minimum_should_match_script
动态计算匹配数(如tags_count * 0.7
):GET /movies/_search {"query": {"terms_set": {"tags": {"terms": ["喜剧", "动作", "科幻"],"minimum_should_match_script": {"source": "doc['tags_count'].value*0.7"}}}} }
- 计算得
3 * 0.7 ≈ 2.1
(向下取整为 2),即要求至少匹配 2 个词项,“电影1”“电影3”会命中。
- 计算得
-
4 全文检索
- 全文检索查询旨在基于相关性搜索和匹配文本数据,会对输入文本进行分词、词干处理、标准化等分析操作,将文本拆分为词项(单个单词);
- 这类检索主要适用于非结构化文本数据(如文章、评论等)。
4.1 match
——分词查询
-
match
查询是一种全文搜索查询,底层逻辑分为三步:-
分词:输入的查询文本会被分词器拆分为单个词项(如“广州白云山公园”会被拆分为“广州”“白云山”“公园”等词项,分词规则由字段的分词器决定)
-
匹配计算:Elasticsearch 会对比查询词项与倒排索引中的词项,计算文档的相关性得分(衡量文档与查询的匹配程度)
-
结果返回:按相关性得分排序,返回最匹配的文档
-
-
基本语法
GET /<index_name>/_search {"query": {"match": {"<field_name>": "<query_string>"}} }
-
<index_name>
:目标索引名称 -
<field_name>
:要检索的文本字段(如address
、remark
等) -
<query_string>
:要搜索的文本字符串
-
-
以
employee
索引的address
字段为例,分析不同match
查询的行为:-
分词后“或(OR)”的效果(默认行为)
GET /employee/_search {"query": {"match": {"address": "广州白云山公园"}} }
address
字段的分词器是ik_max_word
,会将“广州白云山公园”拆分为多个词项(如“广州”“白云山”“公园”等);match
查询默认采用“或”逻辑,只要文档包含任意一个词项就会匹配,因此会返回包含“广州”“白云山”“公园”中任意一个词项的文档;
-
分词后“与(AND)”的效果(通过
operator
参数控制)GET /employee/_search {"query": {"match": {"address": {"query": "广州白云山公园","operator": "and"}}} }
operator
参数设为and
后,要求文档同时包含所有分词后的词项才会匹配,因此只有同时包含“广州”“白云山”“公园”的文档才会命中;
-
最少匹配词项数(通过
minimum_should_match
参数控制)GET /employee/_search {"query": {"match": {"address": {"query": "广州公园","minimum_should_match": 2}}} }
minimum_should_match
参数用于控制至少需要匹配的分词数量;- 此例中要求至少匹配“广州”“公园”这 2 个词项,因此只有同时包含这两个词项的文档才会返回。
-
4.2 multi_match
——多字段查询
-
multi_match
查询用于在多个字段上执行相同的搜索操作,接收一个查询字符串,在指定的字段集合中检索该字符串。它支持灵活的匹配类型和操作符,可根据不同搜索需求调整行为,适用于需要跨多个字段进行全文检索的场景; -
基本语法:
GET /<index_name>/_search {"query": {"multi_match": {"query": "<query_string>","fields": ["<field1>", "<field2>", ...]}} }
-
<index_name>
:要搜索的索引名称 -
<query_string>
:要在多个字段中搜索的字符串 -
<field1>, <field2>, ...
:要搜索的字段列表(支持多个文本字段)
-
-
以
employee
索引为例,查询同时在address
和remark
字段中包含“长沙java”的文档:GET /employee/_search {"query": {"multi_match": {"query": "长沙java","fields": ["address","remark"]}} }
- 该查询会对“长沙java”进行分词(如拆分为“长沙”“java”),然后在
address
字段(文本类型,分词器ik_max_word
)和remark
字段(文本类型,分词器ik_smart
)中分别检索这些词项,最终返回在任一字段中匹配到词项的文档(并按相关性得分排序)。
- 该查询会对“长沙java”进行分词(如拆分为“长沙”“java”),然后在
4.3 match_phrase
——短语查询
-
match_phrase
查询用于执行短语搜索,不仅匹配整个短语,还会严格考虑短语中各个词的顺序和位置。它适用于需要精确匹配短语的场景(如用户查询需与文档文本表达方式严格一致时); -
基本语法
GET /<index_name>/_search {"query": {"match_phrase": {"<field_name>": {"query": "<phrase>"}}} }
-
<index_name>
:目标索引名称 -
<field_name>
:要检索的文本字段 -
<phrase>
:要搜索的精确短语
-
-
通过分词分析理解
match_phrase
的匹配规则:-
对“广州白云山”使用
ik_max_word
分词器,结果为["广州", "白云山", "白云", "云山"]
,各词的position
(位置)分别为0
、1
、2
、3
;POST _analyze {"analyzer":"ik_max_word","text":"广州白云山" }# 结果 {"tokens" : [{"token" : "广州","start_offset" : 0,"end_offset" : 2,"type" : "CN_WORD","position" : 0},{"token" : "白云山","start_offset" : 2,"end_offset" : 5,"type" : "CN_WORD","position" : 1},{"token" : "白云","start_offset" : 2,"end_offset" : 4,"type" : "CN_WORD","position" : 2},{"token" : "云山","start_offset" : 3,"end_offset" : 5,"type" : "CN_WORD","position" : 3}] }
-
match_phrase
默认要求词项严格相邻且顺序一致。例如:- 查询“广州白云山”时,因“广州”(position 0)和“白云山”(position 1)相邻,能匹配到包含该短语的文档;
- 查询“广州白云”时,“广州”(position 0)和“白云”(position 2)不相邻,因此无匹配结果;
-
-
对于以上问题,可以使用**
slop
参数**;-
该参数用于指定短语中词之间允许的最大位移数量(默认 0,即严格相邻)。若设置非零
slop
,则允许词项在一定范围内错位; -
示例:查询“广州云山”时,“广州”(position 0)和“云山”(position 3)间隔为 3,通过设置
slop: 2
允许位移,即可匹配到结果:GET /employee/_search {"query": {"match_phrase": {"address": {"query": "广州云山","slop": 2}}} }
-
4.4 query_string
——支持与或非表达式的查询
-
query_string
是一种灵活的复杂查询类型,支持使用 Lucene 查询语法构建检索逻辑,具备以下特性:-
支持逻辑运算符:
AND
(与)、OR
(或)、NOT
(非) -
支持通配符、模糊搜索、正则表达式等高级功能
-
可在单个或多个字段上执行搜索,能处理复杂查询逻辑
-
-
适用于高级搜索、数据分析、报表等场景,尤其适合需满足“与或非”复杂逻辑的专业领域或高级查询需求;
-
基本语法
GET /<index_name>/_search {"query": {"query_string": {"query": "<your_query_string>","default_field": "<field_name>"}} }
-
<your_query_string>
:包含逻辑运算符、通配符等的查询逻辑 -
<default_field>
:默认搜索字段(省略则搜索所有可索引字段)
-
-
以
employee
索引为例,分三类场景解读:-
未指定字段查询:查询所有可索引字段中满足“赵六 AND 橘子洲”的文档(
AND
需大写)GET /employee/_search {"query": {"query_string": {"query": "赵六 AND 橘子洲"}} }
- 该查询会在所有可索引字段中检索同时包含“赵六”和“橘子洲”的文档;
-
指定单个字段查询:在
address
字段中查询包含“白云山”或“橘子洲”的文档GET /employee/_search {"query": {"query_string": {"default_field": "address","query": "白云山 OR 橘子洲"}} }
- 注意:字段的分词特性会影响查询行为——若字段分词(如
address
是text
类型,用ik_max_word
分词),查询条件会按分词逻辑检索;若字段不分词(如keyword
类型),则按精确逻辑检索;
- 注意:字段的分词特性会影响查询行为——若字段分词(如
-
指定多个字段查询:在
name
和address
字段中执行复杂逻辑查询:“张三” 或 “(广州 AND 王五)”GET /employee/_search {"query": {"query_string": {"fields": ["name","address"],"query": "张三 OR (广州 AND 王五)"}} }
- 该查询会在
name
字段匹配“张三”,或在address
字段同时匹配“广州”和“王五”的文档中命中结果。
- 该查询会在
-
4.5 simple_query_string
-
simple_query_string
是query_string
的“容错版”,具备以下特点:-
忽略错误语法:即使查询语法有问题,也不会导致查询失败
-
支持部分逻辑:不支持
AND
OR
NOT
关键字(会被当作普通字符串),改用特殊符号替代逻辑运算 -
生产环境推荐:因语法宽松、容错性强,适合生产环境使用
-
-
支持的逻辑运算符(替代
query_string
的关键字)-
+
:替代AND
(表示“必须包含”) -
|
:替代OR
(表示“或包含”) -
-
:替代NOT
(表示“必须不包含”)
-
-
基本语法
GET /<index_name>/_search {"query": {"simple_query_string": {"query": "<query_string>","fields": ["<field1>", "<field2>", ...],"default_operator": "OR" // 或 "AND"}} }
-
<query_string>
:包含逻辑运算符(+
|
-
)的查询表达式 -
fields
:指定搜索的字段列表 -
default_operator
:未指定运算符时的默认逻辑(OR
表示“或”,AND
表示“与”)
-
-
以
employee
索引为例,解读两种查询场景:-
通过
default_operator
指定默认逻辑。在name
和address
字段中,要求同时包含“广州”和“公园”(默认运算符设为AND
):GET /employee/_search {"query": {"simple_query_string": {"fields": ["name","address"],"query": "广州公园","default_operator": "AND"}} }
-
通过
+
显式指定“与”逻辑。在name
和address
字段中,显式要求同时包含“广州”和“公园”(用+
替代AND
):GET /employee/_search {"query": {"simple_query_string": {"fields": ["name","address"],"query": "广州 + 公园"}} }
-
4.6 小结
-
精确匹配与全文检索的本质区别,主要体现在以下两个方面:
-
精确匹配:不对检索文本进行分词处理,将整个文本视为一个完整的词条进行匹配。例如
term
查询对keyword
类型字段的匹配,直接以原始字符串为单位进行精确比对; -
全文检索:需要对文本进行分词处理,分词后每个词条将单独进行检索,并通过布尔逻辑(如与、或、非等)进行组合检索,以找到最相关的结果。例如
match
查询会先将查询文本分词,再在倒排索引中对分词后的词条进行关联检索和相关性算分。
-
5 bool query
布尔查询
-
布尔查询(
bool query
)通过布尔逻辑条件组织多条查询语句,只有符合整个布尔条件的文档才会被检索出来,是 Elasticsearch 实现复杂查询逻辑的核心工具; -
布尔查询包含两种不同的执行上下文,决定了查询的性能和相关性计算方式:
-
搜索上下文(query context):需计算文档与搜索条件的相关度得分(通过复杂公式计算,有性能开销),适合带文本分析的全文检索场景(如
match
查询); -
过滤上下文(filter context):仅判断文档是否匹配条件,不计算相关度得分,且结果可缓存以加快响应,适合术语级精确匹配场景(如
term
、range
查询);
-
-
布尔查询的4种组合类型:
类型 说明 上下文类型 must
包含的所有查询条件必须全部满足,需计算相关度得分 搜索上下文 should
若不存在 must
/filter
,则至少满足一个条件;匹配越多相关度越高搜索上下文 filter
包含的所有过滤条件必须全部满足,不计算相关度得分,结果可缓存 过滤上下文 must_not
包含的所有过滤条件必须全部不满足,不计算相关度得分,结果可缓存 过滤上下文 -
例:
-
索引与数据准备。创建
books
索引,定义字段映射(如title
为text
类型、language
为keyword
类型等),并批量导入图书文档:PUT /books {"settings": {"number_of_replicas": 1,"number_of_shards": 1},"mappings": {"properties": {"id": { "type": "long" },"title": { "type": "text", "analyzer": "ik_max_word" },"language": { "type": "keyword" },"author": { "type": "keyword" },"price": { "type": "double" },"publish_time": { "type": "date", "format": "yyyy-MM-dd" },"description": { "type": "text", "analyzer": "ik_max_word" }}} }POST /_bulk {"index":{"_index":"books","_id":"1"}} {"id":"1","title":"Java编程思想","language":"java","author":"Bruce Eckel","price":70.20,"publish_time":"2007-10-01","description":"Java学习必读经典,殿堂级著作!赢得了全球程序员的广泛赞誉。"} {"index":{"_index":"books","_id":"2"}} {"id":"2","title":"Java程序性能优化","language":"java","author":"葛一鸣","price":46.5,"publish_time":"2012-08-01","description":"让你的Java程序更快、更稳定。深入剖析软件设计层面、代码层面、JVM虚拟机层面的优化方法"} {"index":{"_index":"books","_id":"3"}} {"id":"3","title":"Python科学计算","language":"python","author":"张若愚","price":81.4,"publish_time":"2016-05-01","description":"零基础学python,光盘中作者独家整合开发winpython运行环境,涵盖了Python各个扩展库"} {"index":{"_index":"books","_id":"4"}} {"id":"4","title":"Python基础教程","language":"python","author":"Helant","price":54.50,"publish_time":"2014-03-01","description":"经典的Python入门教程,层次鲜明,结构严谨,内容翔实"} {"index":{"_index":"books","_id":"5"}} {"id":"5","title":"JavaScript高级程序设计","language":"javascript","author":"Nicholas C. Zakas","price":66.4,"publish_time":"2012-10-01","description":"JavaScript技术经典名著"}
-
must
组合示例(多条件必须满足,计算相关度)。查询title
包含“java编程”且description
包含“性能优化”的图书:GET /books/_search {"query": {"bool": {"must": [{ "match": { "title": "java编程" } },{ "match": { "description": "性能优化" } }]}} }
- 仅“Java程序性能优化”会命中(同时满足两个条件),且会按相关度得分排序;
-
should
组合示例(至少满足一个条件,计算相关度)。查询title
包含“java编程”或description
包含“性能优化”的图书,且要求至少满足 1 个条件:GET /books/_search {"query": {"bool": {"should": [{ "match": { "title": "java编程" } },{ "match": { "description": "性能优化" } }],"minimum_should_match": 1}} }
- “Java编程思想”(满足
title
条件)、“Java程序性能优化”(满足两个条件)都会命中,匹配越多的文档相关度得分越高;
- “Java编程思想”(满足
-
filter
组合示例(多条件必须满足,不计算得分,可缓存)。查询language
为“java”且publish_time
大于等于“2010-08-01”的图书:GET /books/_search {"query": {"bool": {"filter": [{ "term": { "language": "java" } },{ "range": { "publish_time": { "gte": "2010-08-01" } } }]}} }
- “Java程序性能优化”(
publish_time
为 2012-08-01)会命中,该查询不计算相关度得分,且filter
条件可被缓存以提升后续查询速度。
- “Java程序性能优化”(
-
6 highlight
高亮
-
highlight
关键字用于将符合查询条件的文档中匹配的关键词进行高亮标记,提升搜索结果的可读性,帮助用户快速定位匹配内容; -
highlight
相关属性-
pre_tags
:高亮内容的前缀标签(如 HTML 标签的开始部分) -
post_tags
:高亮内容的后缀标签(如 HTML 标签的结束部分) -
tags_schema
:设置为styled
时,可使用 Elasticsearch 内置的高亮样式 -
require_field_match
:多字段高亮时需设置为false
,表示只要任意一个指定字段匹配查询条件,就对该文档的匹配部分高亮;若为true
,则所有指定字段都必须匹配才会高亮
-
-
例:
-
索引与数据准备。创建
products
索引,指定默认分词器为ik_max_word
,并导入两条产品文档:PUT /products {"settings": {"index": {"analysis.analyzer.default.type": "ik_max_word"}} }PUT /products/_doc/1 {"proId": "2","name": "牛仔男外套","desc": "牛仔外套男装春季衣服男春装夹克修身休闲男生潮牌工装潮流头号青年春秋棒球服男 7705浅蓝常规 XL","timestamp": 1576313264451,"createTime": "2019-12-13 12:56:56" }PUT /products/_doc/2 {"proId": "6","name": "HLA海澜之家牛仔裤男","desc": "HLA海澜之家牛仔裤男2019时尚有型舒适HKNAD3E109A 牛仔蓝(A9)175/82A(32)","timestamp": 1576314265571,"createTime": "2019-12-18 15:56:56" }
-
默认高亮(匹配字段自动高亮)。查询
name
字段精确为“牛仔”的产品,并对所有字段的匹配内容进行默认高亮:GET /products/_search {"query": {"term": {"name": {"value": "牛仔"}}},"highlight": {"fields": {"*": {}}} }
- 结果中,
name
和desc
字段中匹配“牛仔”的内容会被 Elasticsearch 默认的高亮标签(如<em>
)包裹;
- 结果中,
-
自定义高亮HTML标签。使用
pre_tags
和post_tags
自定义高亮样式(如红色字体),并通过multi_match
跨name
和desc
字段查询“牛仔”:GET /products/_search {"query": {"multi_match": {"fields": ["name","desc"],"query": "牛仔"}},"highlight": {"pre_tags": ["<span style='color:red'>"],"post_tags": ["</span>"],"fields": {"*": {}}} }
- 结果中,匹配“牛仔”的内容会被
<span style='color:red'>
和</span>
包裹,呈现红色高亮;
- 结果中,匹配“牛仔”的内容会被
-
多字段高亮(指定多个字段并控制匹配逻辑)。查询
name
字段精确为“牛仔”的产品,对name
和desc
字段进行高亮,并设置require_field_match: false
(只要任意字段匹配就高亮):GET /products/_search {"query": {"term": {"name": {"value": "牛仔"}}},"highlight": {"pre_tags": ["<font color='red'>"],"post_tags": ["</font>"],"require_field_match": "false","fields": {"name": {},"desc": {}}} }
- 结果中,
name
和desc
字段中匹配“牛仔”的内容会被红色font
标签包裹,且只要其中一个字段匹配就会触发高亮。
- 结果中,
-
7 geo_point
地理空间位置查询
-
地理空间位置查询是基于地理位置信息筛选数据的检索方式,广泛应用于地理信息系统(GIS)、位置服务(如旅行、房地产、物流、零售等行业)。在 Elasticsearch 中,它通过地理坐标(经纬度)实现“按距离检索”等空间逻辑;
-
Elasticsearch 中使用
geo_point
字段类型存储地理坐标,该类型可存储纬度(lat
)和经度(lon
),用于表示地球上的一个点; -
geo_distance
查询用于筛选“距离指定坐标点一定范围内”的文档,关键参数包括:-
distance
:最大距离(支持单位如km
、m
等) -
distance_type
:距离计算方式,arc
(地球表面弧长,推荐)或plane
(直线距离) -
location
:参考坐标点(包含lat
和lon
)
-
-
例:
-
索引与数据准备。创建
tourist_spots
索引,定义name
(文本类型)和location
(geo_point
类型),并插入多条带地理坐标的景点文档:PUT /tourist_spots {"mappings": {"properties": {"name": {"type": "text","analyzer": "ik_max_word","search_analyzer": "ik_max_word"},"location": {"type": "geo_point"},"city": {"type": "keyword"}}} }POST /tourist_spots/_doc {"name": "故宫博物院","location": { "lat": 39.9159, "lon": 116.3945 },"city": "北京" }POST /tourist_spots/_doc {"name": "西湖","location": { "lat": 30.2614, "lon": 120.1479 },"city": "杭州" }POST /tourist_spots/_doc {"name": "雷峰塔","location": { "lat": 30.2511, "lon": 120.1347 },"city": "杭州" }POST /tourist_spots/_doc {"name": "苏堤春晓","location": { "lat": 30.2584, "lon": 120.1383 },"city": "杭州" }
-
北京附近景点查询(空间查询)。查询“故宫博物院”坐标点 10km 范围内的景点:
GET /tourist_spots/_search {"query": {"bool": {"must": { "match_all": {} },"filter": {"geo_distance": {"distance": "10km","distance_type": "arc","location": { "lat": 39.9159, "lon": 116.3945 }}}}} }
- 仅“故宫博物院”会命中(位于自身坐标 10km 范围内);
-
杭州西湖附近景点查询(空间查询)。查询“西湖”坐标点 5km 范围内的景点:
GET /tourist_spots/_search {"query": {"bool": {"must": { "match_all": {} },"filter": {"geo_distance": {"distance": "5km","distance_type": "arc","location": { "lat": 30.2614, "lon": 120.1479 }}}}} }
- “雷峰塔”“苏堤春晓”会命中(均在西湖 5km 范围内)。
-
8 ElasticSearch 8.x 向量检索
-
Elasticsearch 8.x 引入向量检索(Vector Search)特性,通过 KNN(K-Nearest Neighbors,K-最近邻)算法支持向量近邻检索。其核心思路是将文档(或数据项)表示为高维向量,通过向量相似度计算找到最相似的结果,适用于机器学习、数据分析、推荐系统等领域;
-
在 Elasticsearch 中,向量数据存储在
dense_vector
类型的字段中,需指定向量的维度(dims
); -
KNN 检索的关键参数
-
field
:存储向量的字段名(dense_vector
类型) -
query_vector
:用于检索的目标向量 -
k
:要返回的最相似文档数量 -
num_candidates
:检索时的候选文档数量(用于优化性能)
-
-
例:
-
索引与数据准备。创建
image-index
索引,定义image-vector
(dense_vector
类型,维度 3)及其他辅助字段,并批量插入带向量的图像文档:PUT /image-index {"mappings": {"properties": {"image-vector": {"type": "dense_vector","dims": 3},"title": { "type": "text" },"file-type": { "type": "keyword" },"my_label": { "type": "text" }}} }POST /image-index/_bulk {"index":{}} {"image-vector": [-5, 9, -12], "title": "Image A", "file-type": "jpeg", "my_label": "red"} {"index":{}} {"image-vector": [10, -2, 3], "title": "Image B", "file-type": "png", "my_label": "blue"} {"index":{}} {"image-vector": [4, 0, -1], "title": "Image C", "file-type": "gif", "my_label": "red"}
-
KNN 向量检索示例。基于目标向量
[-5, 10, -12]
,检索最相似的 10 个文档:POST /image-index/_search {"knn": {"field": "image-vector","query_vector": [-5, 10, -12],"k": 10,"num_candidates": 100},"fields": ["title", "file-type"] }
- 该查询会通过 KNN 算法计算目标向量与
image-vector
字段中向量的相似度,返回最相似的文档(如“Image A”会因向量高度相似被优先命中)。
- 该查询会通过 KNN 算法计算目标向量与
-