Published on

在 Elasticsearch (ES) 进行查询 | DSL

Authors
  • avatar
    Name
    Shelton Ma
    Twitter

0. DSL

  1. 全文模糊搜索

    POST /app-logs/_search
    {
      "query": {
        "match": {
          "message": "server error"
        }
      }
    }
    
  2. 精确搜索

    POST /users/_search
    {
      "query": {
        "term": {
          "userId": "1234"
        }
      }
    }
    
  3. 范围查询 + 时间排序

    POST /app-logs/_search
    {
      "query": {
        "range": {
          "timestamp": {
            "gte": "2024-01-01T00:00:00",
            "lte": "2024-12-31T23:59:59"
          }
        }
      },
      "sort": [
        { "timestamp": { "order": "desc" } }
      ],
      "size": 10
    }
    
  4. 多条件组合查询(bool)

    # 查找最近一天内,日志级别是 error 的记录
    POST /app-logs/_search
    {
      "query": {
        "bool": {
          "must": [
            { "match": { "level": "error" } },
            { "range": { "timestamp": { "gte": "now-1d/d" } } }
          ]
        }
      }
    }
    
  5. 聚合统计(类似 SQL 的 COUNT + GROUP BY)

    POST /app-logs/_search
    {
      "size": 0,
      "aggs": {
        "error_by_user": {
          "terms": {
            "field": "userId.keyword",
            "size": 5
          }
        }
      }
    }
    

常用汇总

{
  "query": {
    "bool": {
      "must": [...],
      "filter": [...],
      "should": [...],
      "must_not": [...]
    }
  },
  "sort": [...],
  "from": 0,
  "size": 10,
  "aggs": {...}
}

1. 查询优化

  1. 使用 _source 控制返回字段

    默认查询返回整个_source 数据,如果字段很多,响应体会很大,影响性能. 可以用_source 控制返回字段

    # 效果:只返回 user_id 和 message,减少数据传输,提高查询速度.
    GET logs-*/_search
    {
      "_source": ["user_id", "message"],
      "query": {
        "match": { "message": "error" }
      }
    }
    
  2. 使用 stored_fields 仅返回指定字段 如果字段 已存储,可以使用 stored_fields 提高查询效率:

    # 适用于 "_source": false 但存储了某些字段的情况.
    GET logs-*/_search
    {
      "stored_fields": ["user_id", "message"],
      "query": {
        "match": { "message": "error" }
      }
    }
    
  3. 使用 doc_values 提高排序/聚合性能

    • 默认情况下,字符串字段 (text) 不能用于排序,但 keyword 类型支持

      GET logs-*/_search
      {
        "sort": [
          { "timestamp": "desc" }
        ]
      }
      
    • 优化方式

      # 时间字段使用 date 类型
      # 字符串字段使用 keyword 而非 text
      # 确保字段启用了 doc_values
      PUT logs-*/_mapping
      {
        "properties": {
          "timestamp": { "type": "date", "format": "epoch_millis" },
          "user_id": { "type": "keyword", "doc_values": true }
        }
      }
      
  4. 避免 wildcard,使用 keyword 进行前缀搜索

    1. wildcard 查询效率低,推荐使用 prefix

      GET logs-*/_search
      {
        "query": {
          "prefix": {
            "user_id": "abc"
          }
        }
      }
      
    2. 或者使用 wildcard 但限制 keyword

      GET logs-*/_search
      {
        "query": {
          "wildcard": {
            "user_id.keyword": "abc*"
          }
        }
      }
      
  5. 使用 filter 而不是 must(避免 score 计算)

  6. 使用 keyword 查询精确值

  7. 使用 exists 避免 null 检查

  8. 分页查询时,避免 deep pagination. 比如基于时间的查询

    {
      "size": 10,
      "query": { "match_all": {} },
      "sort": [{ "timestamp": "desc" }],
      "search_after": [1689123456]
    }
    
  9. 使用 terms 批量查询多个值

    {
      "query": {
        "terms": { "status": ["active", "pending"] }
      }
    }
    
  10. 使用 aggregation 进行数据统计, 而不是 match_all + 手动遍历数据

2. 资源管理

1. 限制 size,避免深分页

2. 使用 scroll 处理大数据量

3. 关闭 _source 仅返回 doc_values