ElasticSearch查询语句Query String详解:从入门到精通
上次分享了 ES 查询语句 DSL 的使用:Elasticsearch DSL 查询语法大全:从入门到精通
这次再分享一下 ES 查询语句中的另一部分,Query String 的用法。
前言
Query String 的特点就是语法简单、表达力强。
什么是Query String查询?
Query String查询是ElasticSearch基于Lucene查询语法实现的一种查询方式,它允许用户使用类似Google搜索的语法来表达复杂的查询逻辑。这种查询方式的特点是:
- 语法简洁:用字符串即可表达复杂条件
- 功能强大:支持布尔运算、通配符、正则、区间等
- 使用广泛:Kibana搜索框、日志分析平台等都在使用
Query String vs Query DSL
在深入之前,我们先了解一下Query String与Query DSL的区别:
| 特性 | Query String | Query DSL |
|---|---|---|
| 语法形式 | 字符串表达式 | JSON结构 |
| 学习曲线 | 较平缓 | 相对复杂 |
| 适用场景 | 快速搜索、Kibana | 精细控制、复杂业务 |
| 可读性 | 类似SQL,直观 | 结构化,清晰 |
| 灵活性 | 中等 | 更高 |
一、Query String查询基础
1.1 基本语法结构
Query String查询的基本结构如下:
json
{
"query": {
"query_string": {
"query": "你的查询表达式"
}
}
}
也可以指定默认字段:
json
{
"query": {
"query_string": {
"default_field": "content",
"query": "搜索内容"
}
}
}
1.2 关键字与运算符速查表
Query String查询支持以下关键字和运算符:
| 运算符 | 说明 | 示例 |
|---|---|---|
AND |
逻辑与 | title:elasticsearch AND content:query |
OR |
逻辑或 | status:active OR status:pending |
NOT |
逻辑非 | NOT status:deleted |
+ |
必须包含 | +title:elasticsearch |
- |
必须不包含 | -status:deleted |
() |
分组 | (status:active OR status:pending) AND type:order |
"" |
精确短语匹配 | "exact phrase match" |
二、从复杂示例开始
让我们先从一个综合示例开始,直观感受Query String查询的强大之处:
json
{
"query_string": {
"query": "price:12 AND (count:19 OR calcNum:20) AND ((duty:n AND num:[1 TO 10]) OR (duty:m AND num:>20))"
}
}
逐步拆解
第一部分:精确匹配
makefile
price:12
- 含义:查询
price字段等于12的文档
第二部分:条件组合
makefile
(count:19 OR calcNum:20)
- 含义:查询
count为19或calcNum为20的文档
第三部分:嵌套逻辑
php
((duty:n AND num:[1 TO 10]) OR (duty:m AND num:>20))
- 含义:满足以下两个条件之一:
duty为n且num在1到10之间duty为m且num大于20
完整语义:查询满足以下所有条件的文档:
price等于12count等于19 或calcNum等于20- (
duty为n且num在1-10区间) 或 (duty为m且num大于20)
三、单字段查询详解
3.1 精确值查询
精确值查询是最基础的查询方式,用于匹配字段等于特定值的文档。
单值查询:
plain
fieldName:字段值
示例:
json
// 查询status字段为"active"的文档
{
"query_string": {
"query": "status:active"
}
}
// 查询用户ID为12345的文档
{
"query_string": {
"query": "userId:12345"
}
}
多值查询(任意匹配):
plain
fieldName:(字段值1 OR 字段值2 OR 字段值3)
json
// 查询状态为active或pending的订单
{
"query_string": {
"query": "status:(active OR pending)"
}
}
3.2 非空查询
在实际业务中,经常需要查询字段是否存在(非空)的文档。
语法:
plain
_exists_:fieldName // 字段存在且不为空
_missing_:fieldName // 字段不存在或为空(ES 5.x后已弃用,建议用 NOT _exists_:)
使用示例:
json
// 查询有email字段的用户
{
"query_string": {
"query": "_exists_:email"
}
}
// 查询没有phone字段的用户
{
"query_string": {
"query": "NOT _exists_:phone"
}
}
// 组合使用:有email但没有phone的用户
{
"query_string": {
"query": "_exists_:email AND NOT _exists_:phone"
}
}
常见应用场景:
- 过滤已填写手机号的用户
- 查询有附件的邮件
- 筛选已完成的订单(有完成时间)
3.3 通配符查询
通配符查询可以匹配符合特定模式的值,适合模糊搜索场景。
⚠️ 重要提示:通配符只能搜索经过分词的内容。例如"铁人三项比赛"经过分词后变成["铁人", "三项", "比赛"],使用"项*赛"是搜索不到的,因为"项"和"赛"不在同一个词中。
支持的通配符:
| 通配符 | 说明 | 示例 | 匹配结果 |
|---|---|---|---|
? |
匹配单个字符 | te?t |
test, text, tent |
* |
匹配零个或多个字符 | test* |
test, tester, testing |
使用示例:
plain
// 匹配以"铁"开头,后面跟一个任意字符的内容
fieldName:铁?
// 匹配:铁人、铁马、铁矿
// 不匹配:铁、铁人三项
// 匹配以"铁"开头的所有内容
fieldName:铁*
// 匹配:铁、铁人、铁人三项、铁矿
实际应用场景:
json
// 查询以"张"开头的用户名
{
"query_string": {
"query": "username:张*"
}
}
// 查询特定格式的订单号(如:ORD-12345)
{
"query_string": {
"query": "orderNumber:ORD-?????"
}
}
// 查询所有图片文件
{
"query_string": {
"query": "fileName:*.jpg OR fileName:*.png OR fileName:*.gif"
}
}
⚠️ 性能警告 :通配符查询(尤其是前导通配符如
*test)可能导致性能问题,因为需要扫描大量数据。在生产环境谨慎使用!
3.4 区间查询
区间查询用于查询字段值在某个范围内的文档,常用于数值、日期等类型字段。
⚠️ 注意 :日期区间在某些情况下可能测试无效,建议与标准Query DSL的
range查询结合使用。
区间边界符号说明:
| 符号 | 说明 | 示例 |
|---|---|---|
[ |
包含左边界值 | [1 TO 10] 包含1和10 |
] |
包含右边界值 | [1 TO 10] 包含1和10 |
{ |
不包含左边界值 | {1 TO 10} 不包含1 |
} |
不包含右边界值 | {1 TO 10} 不包含10 |
使用示例:
plain
// 数值区间
price:[1 TO 100] // 价格在1到100之间(闭区间)
price:[1 TO 100} // 价格在1到99之间(含1不含100)
price:[1 TO *] // 价格大于等于1
price:[* TO 100] // 价格小于等于100
// 日期区间
createDate:[2021-01-01 TO 2021-12-31] // 2021年全年
createDate:[2021-01-01 TO 2021-02-01} // 2021年1月
实际应用示例:
json
// 查询价格在100-500之间的商品
{
"query_string": {
"query": "price:[100 TO 500]"
}
}
// 查询2023年的订单
{
"query_string": {
"query": "orderDate:[2023-01-01 TO 2023-12-31]"
}
}
3.5 比较运算符查询
支持的比较运算符:
| 运算符 | 说明 | 示例 |
|---|---|---|
> |
大于 | age:>18 |
>= |
大于等于 | score:>=60 |
< |
小于 | price:<100 |
<= |
小于等于 | stock:<=10 |
使用示例:
plain
// 单独使用
price:>100 // 价格大于100
score:>=60 // 分数大于等于60
// 组合使用
age:(>=18 AND <60) // 年龄在18-59岁之间
price:(>100 AND <500) // 价格在100-500之间(不含边界)
四、多字段查询详解
4.1 多字段搜索基础
当需要在多个字段中搜索相同内容时,可以使用fields参数指定目标字段。
json
// 在标题和内容中搜索"elasticsearch"
{
"query_string": {
"fields": ["title", "content"],
"query": "elasticsearch"
}
}
4.2 字段权重(Boost)
在多字段搜索中,可以为不同字段设置不同的权重,影响搜索结果的排序。
json
// 标题权重为内容的两倍
{
"query_string": {
"fields": ["title^2", "content"],
"query": "elasticsearch"
}
}
// 标题权重最高,摘要次之,内容最低
{
"query_string": {
"fields": ["title^3", "summary^2", "content"],
"query": "search engine"
}
}
权重计算说明:
- 默认权重为1
title^2表示标题字段的相关性分数乘以2- 权重越高,匹配该字段的结果排名越靠前
4.3 minimum_should_match 参数
使用minimum_should_match参数可以控制最少需要匹配的条件数量。
json
{
"query_string": {
"fields": ["title", "content"],
"query": "elasticsearch tutorial beginner",
"minimum_should_match": 2
}
}
这个查询表示:搜索包含"elasticsearch"、"tutorial"、"beginner"这三个词中至少2个的文档。
参数值类型:
| 参数值 | 说明 | 示例 |
|---|---|---|
| 整数 | 必须匹配的词数 | minimum_should_match: 2 |
| 百分比 | 必须匹配的词百分比 | minimum_should_match: "75%" |
| 组合 | 复杂组合规则 | minimum_should_match: "2<75%" |
4.4 default_field 和 default_operator
default_field:指定默认搜索字段
json
{
"query_string": {
"default_field": "content",
"query": "elasticsearch"
}
}
default_operator:指定默认逻辑运算符(默认为OR)
json
// 使用AND作为默认运算符
{
"query_string": {
"fields": ["title", "content"],
"query": "elasticsearch tutorial",
"default_operator": "AND"
}
}
五、布尔逻辑与分组
5.1 布尔运算符
Query String支持三种布尔运算符,优先级从高到低为:NOT > AND > OR。
| 运算符 | 说明 | 示例 |
|---|---|---|
AND |
与,两边都必须满足 | title:java AND content:spring |
OR |
或,两边满足其一即可 | status:active OR status:pending |
NOT |
非,排除条件 | NOT status:deleted |
使用示例:
plain
// AND:必须同时满足
title:elasticsearch AND content:query
// OR:满足其一即可
status:active OR status:pending OR status:processing
// NOT:排除
NOT status:deleted
// 组合使用
(title:java OR title:python) AND NOT status:draft
5.2 必须包含与排除(+/-)
除了AND/OR/NOT,还可以使用+和-符号:
| 符号 | 说明 | 等价写法 |
|---|---|---|
+ |
必须包含 | 类似AND |
- |
必须排除 | 类似NOT |
使用示例:
plain
// 必须包含title为elasticsearch,content可以包含query
+title:elasticsearch content:query
// 必须不包含status为deleted
-status:deleted
// 组合:必须包含elasticsearch,可以包含tutorial,必须不包含draft
+title:elasticsearch tutorial -status:draft
5.3 分组与优先级
使用括号()可以改变运算优先级。
plain
// 不使用括号(OR优先级低于AND)
status:active AND type:order OR type:payment
// 等价于:
// 使用括号改变优先级
status:active AND (type:order OR type:payment)
// 含义:状态为active,且类型为order或payment
六、短语查询与模糊匹配
6.1 精确短语匹配
使用双引号进行精确短语匹配,要求词的顺序完全一致。
json
// 精确匹配短语 "quick brown fox"
{
"query_string": {
"query": "content:\"quick brown fox\""
}
}
// 区别于普通查询
{
"query_string": {
"query": "content:quick brown fox"
// 这会匹配包含 quick、brown 或 fox 的文档
}
}
6.2 短语邻近查询
使用~指定词之间的最大距离。
json
// "quick fox"允许中间间隔最多1个词
{
"query_string": {
"query": "content:\"quick fox\"~1"
}
}
// 匹配:"quick brown fox"、"quick red fox"
// 不匹配:"quick small brown fox"(间隔超过1个词)
6.3 模糊查询
使用~进行模糊匹配,用于处理拼写错误。
json
// 匹配与"elasticsearch"编辑距离为2以内的词
{
"query_string": {
"query": "title:elasticsearch~2"
}
}
// 默认编辑距离为1
{
"query_string": {
"query": "title:search~"
}
}
// 匹配:search、serch、searh
七、正则表达式查询
Query String支持正则表达式查询,用于复杂的模式匹配。
7.1 基本语法
正则表达式需要包裹在斜杠/中:
plain
fieldName:/正则表达式/
7.2 常用正则示例
json
// 匹配以"ORD-"开头,后跟数字的订单号
{
"query_string": {
"query": "orderNumber:/ORD-[0-9]+/"
}
}
// 匹配邮箱格式
{
"query_string": {
"query": "email:/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/"
}
}
// 匹配手机号(简单格式)
{
"query_string": {
"query": "phone:/1[3-9][0-9]{9}/"
}
}
// 匹配IP地址
{
"query_string": {
"query": "ip:/[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}/"
}
}
⚠️ 性能警告 :正则表达式查询可能非常消耗资源,尤其是复杂的正则模式或在大文本字段上使用。建议尽量使用简单的正则模式,避免使用
.*开头的模式。
八、转义与特殊字符
8.1 需要转义的特殊字符
以下字符在Query String中有特殊含义,搜索这些字符本身时需要转义:
scss
+ - = && || > < ! ( ) { } [ ] ^ " ~ * ? : \ /
8.2 转义方法
使用反斜杠\进行转义:
json
// 搜索包含"(active)"的文档
{
"query_string": {
"query": "status:\\(active\\)"
}
}
// 搜索包含"question?"的文档
{
"query_string": {
"query": "content:question\\?"
}
}
九、性能优化建议
9.1 避免性能杀手
以下是可能导致性能问题的查询模式:
| 查询模式 | 问题 | 建议 |
|---|---|---|
前导通配符 *term |
需要扫描所有词 | 避免使用或使用n-gram |
| 复杂正则表达式 | CPU密集 | 简化模式或使用其他查询 |
大范围通配符 term* |
匹配大量词 | 限制词数量 |
| 过多字段的multi-match | 计算量大 | 减少字段数量 |
9.2 优化建议
1. 使用更精确的查询
json
// 不推荐:前导通配符
{
"query_string": {
"query": "name:*smith"
}
}
// 推荐:使用后缀通配符或精确查询
{
"query_string": {
"query": "name:smith*"
}
}
2. 合理使用filter上下文
对于不需要评分的过滤条件,使用filter上下文:
json
{
"bool": {
"must": [
{
"query_string": {
"query": "content:elasticsearch"
}
}
],
"filter": [
{
"term": { "status": "active" }
}
]
}
}
3. 限制查询字段数量
json
// 不推荐:查询所有字段
{
"query_string": {
"query": "search term"
}
}
// 推荐:指定相关字段
{
"query_string": {
"fields": ["title", "content"],
"query": "search term"
}
}
十、实际应用场景
10.1 日志搜索(Kibana场景)
在Kibana或日志分析平台中,Query String是最常用的查询方式:
json
// 查询特定时间范围内的错误日志
{
"query_string": {
"query": "level:ERROR AND timestamp:[2024-01-01 TO 2024-01-31] AND (service:order OR service:payment)"
}
}
// 查询包含异常堆栈的日志
{
"query_string": {
"query": "message:*Exception* AND level:ERROR"
}
}
10.2 电商搜索
json
// 查询特定价格区间、品牌、且有库存的商品
{
"query_string": {
"query": "price:[100 TO 500] AND brand:(Apple OR Samsung) AND stock:>0 AND status:active"
}
}
// 查询标题或描述包含关键词的商品
{
"query_string": {
"fields": ["title^2", "description"],
"query": "wireless bluetooth headphone",
"minimum_should_match": "75%"
}
}
10.3 用户搜索
json
// 查询活跃的高级用户
{
"query_string": {
"query": "status:active AND level:(gold OR platinum) AND lastLogin:[2024-01-01 TO *]"
}
}
// 模糊搜索用户名(处理拼写错误)
{
"query_string": {
"query": "username:zhang~2"
}
}
十一、常见问题与解决方案
Q1:为什么通配符查询搜不到结果?
可能原因:
- 字段经过了分词处理,通配符只能匹配分词后的词项
- 使用了前导通配符
*term,建议改用后缀通配符
解决方案:
- 对于需要精确匹配的字段,使用
keyword类型 - 使用n-gram分词器支持前缀搜索
Q2:日期区间查询不生效?
可能原因:
- 日期格式与映射定义不一致
- 时区问题
解决方案:
json
// 使用标准的range查询替代
{
"range": {
"timestamp": {
"gte": "2024-01-01",
"lte": "2024-01-31",
"format": "yyyy-MM-dd"
}
}
}
Q3:查询特殊字符时报错?
解决方案: 正确转义特殊字符:
json
{
"query_string": {
"query": "content:\\(important\\)"
}
}
总结
本文全面介绍了ElasticSearch Query String查询的各个方面:
核心知识点回顾
| 类别 | 主要内容 |
|---|---|
| 基础语法 | 字段查询、布尔运算、分组 |
| 单字段查询 | 精确值、非空、通配符、区间、比较 |
| 多字段查询 | fields参数、权重、minimum_should_match |
| 高级特性 | 短语查询、模糊匹配、正则表达式 |
| 性能优化 | 避免前导通配符、使用filter上下文 |
最佳实践建议
- 明确字段:始终指定搜索字段,避免全字段搜索
- 合理分词:根据业务需求选择合适的分词器和字段类型
- 性能优先:避免使用前导通配符和复杂正则
- 组合使用:Query String与Query DSL结合,发挥各自优势
掌握Query String语法,可以让我们更灵活地构建搜索功能,特别是在日志分析、快速检索等场景下提高工作效率。希望本文对你的ElasticSearch学习和实践有所帮助!