1. Elasticsearch基础查询语法概述
Elasticsearch作为一款开源的分布式搜索和分析引擎,其查询语法是每个开发者必须掌握的核心技能。我在实际项目中使用ES已有五年多时间,从最初的简单搜索到现在的复杂聚合分析,深刻体会到掌握基础查询语法的重要性。
ES查询主要分为两类:结构化查询(Query DSL)和过滤查询(Filter DSL)。结构化查询会计算相关性分数(_score),而过滤查询只关心文档是否匹配,性能更高。新手常犯的错误是不区分两者场景,导致查询性能低下。
基础查询语法包含以下几个核心组成部分:
- 查询条件(queries):match、term、range等
- 复合查询:bool、dis_max等
- 排序和分页:sort、from/size
- 高亮显示:highlight
- 聚合分析:aggs
2. 基础查询类型详解
2.1 匹配查询(Match Query)
match查询是最常用的全文搜索查询,会对查询文本进行分词处理。例如搜索商品名称:
json复制GET /products/_search
{
"query": {
"match": {
"name": "智能手机 5G"
}
}
}
这里ES会将"智能手机 5G"分词为["智能","手机","5G"],然后搜索包含这些词项的文档。match查询默认使用OR逻辑,可以通过operator参数改为AND:
json复制"match": {
"name": {
"query": "智能手机 5G",
"operator": "and"
}
}
实际项目中,我建议对重要字段使用AND操作符,可以提高结果精准度。但要注意这可能会减少结果数量。
2.2 精确查询(Term Query)
term查询用于精确值匹配,不会对查询文本进行分词。适合搜索未分词的字段如枚举值、状态码等:
json复制GET /orders/_search
{
"query": {
"term": {
"status": "completed"
}
}
}
常见误区是尝试对分词字段使用term查询,这通常得不到预期结果。例如对商品名称使用term查询"智能手机"可能匹配不到任何文档,因为实际存储的是分词后的词项。
2.3 范围查询(Range Query)
range查询用于数值或日期范围过滤:
json复制GET /products/_search
{
"query": {
"range": {
"price": {
"gte": 1000,
"lte": 5000
}
}
}
}
支持的操作符包括:
- gt:大于
- gte:大于等于
- lt:小于
- lte:小于等于
日期范围查询时,建议使用日期数学表达式,更灵活:
json复制"range": {
"create_time": {
"gte": "now-1d/d",
"lt": "now/d"
}
}
3. 复合查询实战
3.1 布尔查询(Bool Query)
bool查询是最强大的复合查询,可以组合多个查询条件:
json复制GET /products/_search
{
"query": {
"bool": {
"must": [
{ "match": { "name": "手机" } }
],
"filter": [
{ "range": { "price": { "gte": 2000 } } },
{ "term": { "brand": "华为" } }
],
"must_not": [
{ "term": { "status": "out_of_stock" } }
],
"should": [
{ "match": { "tags": "5G" } },
{ "match": { "tags": "旗舰" } }
],
"minimum_should_match": 1
}
}
}
各子句含义:
- must:必须匹配,贡献分数
- filter:必须匹配,但不贡献分数
- must_not:必须不匹配
- should:至少匹配minimum_should_match指定的数量
实际项目中,我通常将不影响相关性的条件放在filter中,可以提高查询性能,因为filter结果可以被缓存。
3.2 排序与分页
ES支持多字段排序和深度分页:
json复制GET /products/_search
{
"query": { "match_all": {} },
"sort": [
{ "price": { "order": "desc" } },
{ "sales": { "order": "desc" } }
],
"from": 100,
"size": 10
}
深度分页(from值较大)会导致性能问题,因为ES需要计算和排序所有匹配文档。对于深度分页,推荐使用search_after参数:
json复制"search_after": [1299, 500],
"sort": [
{ "price": { "order": "desc" } },
{ "id": { "order": "asc" } }
]
4. 高亮与聚合分析
4.1 高亮显示
高亮可以让搜索结果中匹配的关键词突出显示:
json复制GET /articles/_search
{
"query": {
"match": { "content": "Elasticsearch" }
},
"highlight": {
"fields": {
"content": {
"pre_tags": ["<em>"],
"post_tags": ["</em>"],
"number_of_fragments": 3,
"fragment_size": 150
}
}
}
}
高亮配置要点:
- 可以自定义高亮标签
- number_of_fragments控制返回的片段数量
- fragment_size控制每个片段长度
- 对于大文本字段,高亮可能影响性能
4.2 聚合分析
聚合是ES强大的分析功能,常见类型包括:
- 指标聚合:avg、sum、max、min等
- 桶聚合:terms、date_histogram等
- 管道聚合:对其它聚合结果再聚合
示例:统计各品牌商品数量和平均价格
json复制GET /products/_search
{
"size": 0,
"aggs": {
"brands": {
"terms": {
"field": "brand",
"size": 10
},
"aggs": {
"avg_price": {
"avg": { "field": "price" }
}
}
}
}
}
聚合分析时,设置"size":0可以只返回聚合结果不返回命中文档,提高性能。
5. 查询性能优化建议
基于多年ES使用经验,分享几个查询优化技巧:
- 尽量使用filter而不是query,filter结果可以被缓存
- 避免使用script查询,性能较差
- 对于分页,优先考虑search_after而不是from/size
- 合理使用index.max_result_window设置(默认10000)
- 对经常查询的字段使用keyword类型而不是text
- 使用profile API分析查询性能瓶颈
一个常见的性能问题是使用通配符查询:
json复制"wildcard": {
"name": "*手机*"
}
这种查询会导致全索引扫描,在数据量大时性能极差。更好的做法是使用专门的搜索分析器或ngram分词。
6. 实际项目中的查询设计
在电商搜索项目中,我们设计了这样的商品搜索查询:
json复制GET /products/_search
{
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "华为手机",
"fields": ["name^3", "description"],
"type": "best_fields"
}
}
],
"filter": [
{ "term": { "category": "phones" } },
{ "range": { "price": { "gte": 2000, "lte": 8000 } } },
{ "terms": { "color": ["black", "silver"] } },
{ "exists": { "field": "inventory" } }
],
"should": [
{ "term": { "is_premium": true } },
{ "range": { "rating": { "gte": 4 } } }
],
"minimum_should_match": 1
}
},
"sort": [
{ "_score": { "order": "desc" } },
{ "sales": { "order": "desc" } }
],
"highlight": {
"fields": {
"name": {},
"description": {}
}
},
"aggs": {
"brands": {
"terms": { "field": "brand" }
},
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 2000 },
{ "from": 2000, "to": 5000 },
{ "from": 5000 }
]
}
}
}
}
这个查询结合了多种技术:
- 多字段匹配(name字段权重更高)
- 精确过滤(品类、价格区间、颜色等)
- 业务逻辑(必须有库存)
- 相关性提升(优质商品和高评分商品)
- 结果排序(相关性和销量)
- 高亮显示
- 聚合分析(品牌分布和价格区间)
在实际运行中,我们还会添加查询缓存和结果缓存,进一步优化性能。
