Elasticsearch查询Java实现映射文档

Elasticsearch查询Java实现映射文档

项目概述

本项目基于Elasticsearch 7.17.15版本实现了一套完整的日志查询和分析系统,采用了Spring Boot 2.7.18框架,提供了丰富的查询功能包括复合查询、高亮显示、异常分析等。

技术栈版本

  • Elasticsearch: 7.17.15
  • Spring Boot: 2.7.18
  • Spring Cloud: 3.1.8
  • Java: JDK 1.8

核心功能详细实现

1. 复合查询构建(BoolQueryBuilder)

项目实现了复杂的复合查询逻辑,支持must、should、must_not子句组合:

Java实现
/**
 * 构建复合查询条件
 * @param request 搜索请求参数
 * @return BoolQueryBuilder 复合查询构建器
 */
private BoolQueryBuilder buildQuery(LogSearchRequest request) {
    BoolQueryBuilder query = QueryBuilders.boolQuery();
  
    // 添加时间范围查询
    if (request.getStartTime() != null && request.getEndTime() != null) {
        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("@timestamp")
            .gte(request.getStartTime().atZone(ZoneOffset.ofHours(0))
                .withZoneSameInstant(ZoneOffset.UTC).format(DATE_FORMAT))
            .lte(request.getEndTime().atZone(ZoneOffset.ofHours(0))
                .withZoneSameInstant(ZoneOffset.UTC).format(DATE_FORMAT));
        query.must(rangeQuery);
    }
  
    // 添加关键词搜索(MultiMatchQuery)
    if (request.getKeyword() != null && !request.getKeyword().trim().isEmpty()) {
        MultiMatchQueryBuilder multiMatchQuery = QueryBuilders.multiMatchQuery(request.getKeyword().trim())
            .field("message", 5.0f)      // message字段权重最高
            .field("stack_trace", 4.0f)  // 堆栈跟踪权重较高
            .field("class_name", 3.0f)   // 类名权重中等
            .field("method_name", 3.0f)  // 方法名权重中等
            .field("service_name", 2.0f) // 服务名权重较低
            .type(MultiMatchQueryBuilder.Type.BEST_FIELDS)
            .fuzziness("AUTO")           // 自动模糊匹配
            .prefixLength(2)             // 前缀长度
            .maxExpansions(50);          // 最大扩展数
        query.must(multiMatchQuery);
    }
  
    // 添加精确匹配条件
    if (request.getLevel() != null) {
        query.must(QueryBuilders.termQuery("level", request.getLevel()));
    }
  
    if (request.getService() != null) {
        query.must(QueryBuilders.termQuery("service_name", request.getService()));
    }
  
    // 添加服务列表过滤
    if (request.getServices() != null && !request.getServices().isEmpty()) {
        query.must(QueryBuilders.termsQuery("service_name", request.getServices()));
    }
  
    return query;
}
对应DSL语法
{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "@timestamp": {
              "gte": "2023-01-01T00:00:00Z",
              "lte": "2023-01-31T23:59:59Z"
            }
          }
        },
        {
          "multi_match": {
            "query": "error keyword",
            "fields": ["message^5", "stack_trace^4", "class_name^3", "method_name^3", "service_name^2"],
            "type": "best_fields",
            "fuzziness": "AUTO",
            "prefix_length": 2,
            "max_expansions": 50
          }
        },
        {
          "term": {
            "level": "ERROR"
          }
        },
        {
          "term": {
            "service_name": "user-service"
          }
        }
      ],
      "should": [],
      "must_not": []
    }
  }
}
DSL语法说明

基本结构:

  • bool 查询容器包含 mustshouldmust_notfilter 子句
  • must 子句表示必须满足的条件(AND逻辑)
  • should 子句表示可选满足的条件(OR逻辑)
  • must_not 子句表示必须排除的条件(NOT逻辑)
  • filter 子句类似 must,但不参与评分计算

查询类型映射:

  • QueryBuilders.boolQuery()"bool": { }
  • QueryBuilders.rangeQuery("@timestamp")"range": { "@timestamp": { } }
  • QueryBuilders.multiMatchQuery()"multi_match": { }
  • QueryBuilders.termQuery()"term": { }
  • QueryBuilders.termsQuery()"terms": { }

2. 高亮显示功能实现

项目实现了完整的Elasticsearch高亮功能,支持多字段高亮显示:

Java实现
/**
 * 配置高亮显示功能
 * @param searchSourceBuilder 搜索源构建器
 */
private void configureHighlighting(SearchSourceBuilder searchSourceBuilder) {
    HighlightBuilder highlightBuilder = new HighlightBuilder();
  
    // 为message字段添加高亮配置
    HighlightBuilder.Field highlightMessage = new HighlightBuilder.Field("message");
    highlightMessage.preTags("<em class=\"highlight\">");
    highlightMessage.postTags("</em>");
    highlightMessage.fragmentSize(600);    // 片段大小
    highlightMessage.numOfFragments(3);    // 片段数量
    highlightBuilder.field(highlightMessage);

    // 为stack_trace字段添加高亮配置
    HighlightBuilder.Field highlightStackTrace = new HighlightBuilder.Field("stack_trace");
    highlightStackTrace.preTags("<em class=\"highlight\">");
    highlightStackTrace.postTags("</em>");
    highlightStackTrace.fragmentSize(600); // 片段大小
    highlightStackTrace.numOfFragments(3); // 片段数量
    highlightBuilder.field(highlightStackTrace);
  
    searchSourceBuilder.highlighter(highlightBuilder);
}
对应DSL语法
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "error",
            "fields": ["message^5", "stack_trace^4", "class_name^3", "method_name^3", "service_name^2"]
          }
        }
      ]
    }
  },
  "highlight": {
    "fields": {
      "message": {
        "pre_tags": ["<em class=\"highlight\">"],
        "post_tags": ["</em>"],
        "fragment_size": 600,
        "number_of_fragments": 3
      },
      "stack_trace": {
        "pre_tags": ["<em class=\"highlight\">"],
        "post_tags": ["</em>"],
        "fragment_size": 600,
        "number_of_fragments": 3
      }
    }
  }
}
高亮DSL语法说明

高亮配置项:

  • fields - 需要高亮显示的字段列表
  • pre_tags - 高亮内容的前缀标签
  • post_tags - 高亮内容的后缀标签
  • fragment_size - 高亮片段的最大字符数
  • number_of_fragments - 返回的高亮片段数量

Java API映射:

  • HighlightBuilder"highlight": { }
  • HighlightBuilder.Field("message")"message": { }
  • .preTags()"pre_tags": [ ]
  • .postTags()"post_tags": [ ]
  • .fragmentSize()"fragment_size": 600
  • .numOfFragments()"number_of_fragments": 3

3. 聚合查询实现

系统实现了多种聚合查询功能,用于数据统计和分析:

Java实现
/**
 * 构建聚合查询
 * @return SearchSourceBuilder 包含聚合的搜索源
 */
private SearchSourceBuilder buildAggregations(SearchSourceBuilder searchSourceBuilder) {
    // 按服务名称聚合
    searchSourceBuilder.aggregation(AggregationBuilders
        .terms("services")
        .field("service_name")
        .size(100));
  
    // 按日志级别聚合
    searchSourceBuilder.aggregation(AggregationBuilders
        .terms("levels")
        .field("level")
        .size(10));
  
    // 按时间范围聚合(日聚合)
    searchSourceBuilder.aggregation(AggregationBuilders
        .date_histogram("time_buckets")
        .field("@timestamp")
        .calendarInterval(DateHistogramInterval.DAY)
        .format("yyyy-MM-dd"));
  
    return searchSourceBuilder;
}
对应DSL语法
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "services": {
      "terms": {
        "field": "service_name",
        "size": 100
      }
    },
    "levels": {
      "terms": {
        "field": "level",
        "size": 10
      }
    },
    "time_buckets": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day",
        "format": "yyyy-MM-dd"
      }
    }
  }
}
聚合DSL语法说明

聚合类型:

  • terms 聚合 → 分组统计,类似于SQL的GROUP BY
  • date_histogram 聚合 → 按时间间隔分组
  • count 统计 → 自动计算每个桶的文档数量

Java API映射:

  • AggregationBuilders.terms("services")"services": { "terms": { } }
  • AggregationBuilders.date_histogram("time_buckets")"time_buckets": { "date_histogram": { } }
  • .field("service_name")"field": "service_name"
  • .size(100)"size": 100
  • .calendarInterval(DateHistogramInterval.DAY)"calendar_interval": "day"
  • .format("yyyy-MM-dd")"format": "yyyy-MM-dd"

4. 异常分析功能

项目实现了智能的异常分析功能,结合精确匹配和模糊搜索:

Java实现
/**
 * 异常原因分析方法 - 综合查询实现
 * @param definiteKeyword 精确查询关键字
 * @param possibleKeyword 可能的错误关键字(模糊匹配)
 * @return 分析结果
 */
public String analyzeError(String definiteKeyword, String possibleKeyword) {
    try {
        logger.info("Starting error analysis with definiteKeyword: {}, possibleKeyword: {}", 
            definiteKeyword, possibleKeyword);
    
        // 设置查询时间范围为最近7天
        LocalDateTime endTime = LocalDateTime.now();
        LocalDateTime startTime = endTime.minusDays(7);
    
        StringBuilder result = new StringBuilder();
        result.append("异常分析结果:\n\n");
    
        // 构建复合查询条件
        BoolQueryBuilder combinedQuery = QueryBuilders.boolQuery();
    
        // 添加时间范围查询
        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("@timestamp")
            .gte(startTime.atZone(ZoneOffset.ofHours(0)).withZoneSameInstant(ZoneOffset.UTC).format(DATE_FORMAT))
            .lte(endTime.atZone(ZoneOffset.ofHours(0)).withZoneSameInstant(ZoneOffset.UTC).format(DATE_FORMAT));
        combinedQuery.must(rangeQuery);
    
        // 添加精确匹配条件(使用match_phrase确保短语完全匹配)
        if (definiteKeyword != null && !definiteKeyword.trim().isEmpty()) {
            result.append("1. 精确关键字 '").append(definiteKeyword).append("' 的查询条件:\n");
            BoolQueryBuilder exactQuery = QueryBuilders.boolQuery();
            exactQuery.should(QueryBuilders.matchPhraseQuery("message", definiteKeyword.trim()));
            exactQuery.should(QueryBuilders.matchPhraseQuery("stack_trace", definiteKeyword.trim()));
            exactQuery.should(QueryBuilders.matchPhraseQuery("class_name", definiteKeyword.trim()));
            exactQuery.should(QueryBuilders.matchPhraseQuery("method_name", definiteKeyword.trim()));
            exactQuery.should(QueryBuilders.matchPhraseQuery("service_name", definiteKeyword.trim()));
            exactQuery.minimumShouldMatch(1);
            combinedQuery.should(exactQuery);
        }
    
        // 添加模糊匹配条件(使用multi_match)
        if (possibleKeyword != null && !possibleKeyword.trim().isEmpty()) {
            result.append("2. 可能关键字 '").append(possibleKeyword).append("' 的查询条件:\n");
            MultiMatchQueryBuilder fuzzyQuery = QueryBuilders.multiMatchQuery(possibleKeyword.trim())
                .field("message", 5.0f)
                .field("stack_trace", 4.0f)
                .field("class_name", 3.0f)
                .field("method_name", 3.0f)
                .field("service_name", 2.0f)
                .type(MultiMatchQueryBuilder.Type.BEST_FIELDS)
                .fuzziness("AUTO")
                .prefixLength(2)
                .maxExpansions(50);
            combinedQuery.should(fuzzyQuery);
        }
    
        // 设置至少一个should子句需要匹配
        combinedQuery.minimumShouldMatch(1);
    
        // 执行复合查询
        Map<String, Object> combinedResult = searchLogs(combinedRequest, combinedQuery);
        // ... 处理结果逻辑
    
        return result.toString();
    } catch (Exception e) {
        logger.error("Error analysis failed", e);
        throw new RuntimeException("异常分析失败: " + e.getMessage(), e);
    }
}
对应DSL语法
{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "@timestamp": {
              "gte": "2023-01-01T00:00:00Z",
              "lte": "2023-01-07T23:59:59Z"
            }
          }
        }
      ],
      "should": [
        {
          "bool": {
            "should": [
              {
                "match_phrase": {
                  "message": "exact error keyword"
                }
              },
              {
                "match_phrase": {
                  "stack_trace": "exact error keyword"
                }
              },
              {
                "match_phrase": {
                  "class_name": "exact error keyword"
                }
              },
              {
                "match_phrase": {
                  "method_name": "exact error keyword"
                }
              },
              {
                "match_phrase": {
                  "service_name": "exact error keyword"
                }
              }
            ],
            "minimum_should_match": 1
          }
        },
        {
          "multi_match": {
            "query": "fuzzy error keyword",
            "fields": ["message^5", "stack_trace^4", "class_name^3", "method_name^3", "service_name^2"],
            "type": "best_fields",
            "fuzziness": "AUTO",
            "prefix_length": 2,
            "max_expansions": 50
          }
        }
      ],
      "minimum_should_match": 1
    }
  }
}
异常分析DSL语法说明

匹配类型:

  • match_phrase → 短语精确匹配,要求所有词按照指定顺序连续出现
  • multi_match → 多字段模糊匹配
  • fuzziness: "AUTO" → 自动调整模糊匹配容错度
  • minimum_should_match → 控制should子句的最小匹配数

Java API映射:

  • QueryBuilders.matchPhraseQuery()"match_phrase": { }
  • QueryBuilders.multiMatchQuery()"multi_match": { }
  • .minimumShouldMatch(1)"minimum_should_match": 1
  • .fuzziness("AUTO")"fuzziness": "AUTO"
  • .prefixLength(2)"prefix_length": 2
  • .maxExpansions(50)"max_expansions": 50

5. 搜索排序和分页

Java实现
/**
 * 配置搜索排序和分页
 */
private void configureSearchSource(LogSearchRequest request, SearchSourceBuilder searchSourceBuilder) {
    // 设置分页
    searchSourceBuilder.from(request.getFrom());
    searchSourceBuilder.size(request.getSize());
  
    // 配置排序
    searchSourceBuilder.sort(SortBuilders.scoreSort().order(SortOrder.DESC));  // 按相关性得分排序
    searchSourceBuilder.sort("@timestamp", SortOrder.DESC);                    // 按时间倒序排序
  
    // 配置搜索源
    searchSourceBuilder.trackScores(true);  // 跟踪相关性得分
    searchSourceBuilder.timeout(TimeValue.timeValueSeconds(30));  // 设置超时时间
}
对应DSL语法
{
  "from": 0,
  "size": 20,
  "sort": [
    {
      "_score": {
        "order": "desc"
      }
    },
    {
      "@timestamp": {
        "order": "desc"
      }
    }
  ],
  "timeout": "30s",
  "track_scores": true
}

6. 常用查询类型对比

精确匹配 vs 模糊匹配
查询类型Java实现DSL语法适用场景
精确匹配QueryBuilders.termQuery("level", "ERROR")"term": { "level": "ERROR" }精确字段匹配
短语匹配QueryBuilders.matchPhraseQuery("message", "error occurred")"match_phrase": { "message": "error occurred" }短语精确匹配
模糊匹配QueryBuilders.matchQuery("message", "erorr")"match": { "message": "erorr" }文本相似度匹配
多字段匹配QueryBuilders.multiMatchQuery("error")"multi_match": { "query": "error" }跨字段搜索
范围查询类型
Java实现DSL语法示例
QueryBuilders.rangeQuery("@timestamp").gte("2023-01-01").lte("2023-01-31")"range": { "@timestamp": { "gte": "2023-01-01", "lte": "2023-01-31" } }时间范围
QueryBuilders.gteQuery("@timestamp").lt("now-7d")"range": { "@timestamp": { "gte": "now-7d" } }相对时间
QueryBuilders.numericRangeQuery("count").gte(100).lte(1000)"range": { "count": { "gte": 100, "lte": 1000 } }数值范围

性能优化和错误处理

1. 连接池配置优化

Java配置
/**
 * Elasticsearch客户端配置优化
 */
@Configuration
@EnableConfigurationProperties(LogProperties.class)
public class AppConfig {
  
    @Bean
    public RestHighLevelClient elasticsearchClient() {
        return new RestHighLevelClient(
            RestClient.builder(
                new HttpHost("localhost", 9200, "http"),
                new HttpHost("localhost", 9201, "http")
            )
            .setRequestConfigCallback(requestConfigBuilder -> 
                requestConfigBuilder
                    .setConnectTimeout(5000)      // 连接超时5秒
                    .setSocketTimeout(30000)      // 读取超时30秒
                    .setConnectionRequestTimeout(1000) // 连接请求超时1秒
            )
            .setHttpClientConfigCallback(httpClientBuilder -> 
                httpClientBuilder
                    .setMaxConnTotal(100)         // 最大连接数
                    .setMaxConnPerRoute(20)       // 每个路由最大连接数
            )
        );
    }
}
对应配置项说明
  • setConnectTimeout(5000) → 连接建立超时时间
  • setSocketTimeout(30000) → 数据读取超时时间
  • setMaxConnTotal(100) → 客户端最大连接池大小
  • setMaxConnPerRoute(20) → 每个主机最大连接数

2. 完整查询DSL示例

复杂查询DSL
{
  "index": "logstash-2023.01.01,logstash-2023.01.02",
  "from": 0,
  "size": 100,
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "@timestamp": {
              "gte": "2023-01-01T00:00:00Z",
              "lte": "2023-01-31T23:59:59Z"
            }
          }
        },
        {
          "multi_match": {
            "query": "database connection error",
            "fields": ["message^5", "stack_trace^4", "class_name^3"],
            "type": "best_fields",
            "fuzziness": "AUTO",
            "prefix_length": 2,
            "max_expansions": 50
          }
        }
      ],
      "must_not": [
        {
          "term": {
            "level": "DEBUG"
          }
        }
      ],
      "filter": [
        {
          "terms": {
            "service_name": ["user-service", "order-service"]
          }
        }
      ]
    }
  },
  "sort": [
    {
      "@timestamp": {
        "order": "desc"
      }
    }
  ],
  "highlight": {
    "fields": {
      "message": {
        "pre_tags": ["<em class=\"highlight\">"],
        "post_tags": ["</em>"],
        "fragment_size": 600,
        "number_of_fragments": 3
      },
      "stack_trace": {
        "pre_tags": ["<em class=\"highlight\">"],
        "post_tags": ["</em>"],
        "fragment_size": 600,
        "number_of_fragments": 3
      }
    }
  },
  "aggs": {
    "services": {
      "terms": {
        "field": "service_name",
        "size": 10
      }
    },
    "time_buckets": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day",
        "format": "yyyy-MM-dd"
      }
    }
  },
  "timeout": "30s",
  "track_scores": true
}
对应的完整Java实现
/**
 * 完整的日志搜索实现
 */
public Map<String, Object> searchLogs(LogSearchRequest request) {
    try {
        // 构建索引模式
        String indexPattern = buildIndexPattern(request.getStartTime(), request.getEndTime());
        logger.info("Searching logs in index pattern: {}", indexPattern);

        // 构建复合查询
        BoolQueryBuilder query = buildQuery(request);

        // 配置搜索源
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(query);
        searchSourceBuilder.from(request.getFrom());
        searchSourceBuilder.size(request.getSize());
        searchSourceBuilder.sort(SortBuilders.scoreSort().order(SortOrder.DESC));
        searchSourceBuilder.sort("@timestamp", SortOrder.DESC);
        searchSourceBuilder.trackScores(true);
        searchSourceBuilder.timeout(TimeValue.timeValueSeconds(30));

        // 配置高亮
        if (request.getKeyword() != null && !request.getKeyword().trim().isEmpty()) {
            configureHighlighting(searchSourceBuilder);
        }

        // 配置聚合
        searchSourceBuilder.aggregation(AggregationBuilders
            .terms("services")
            .field("service_name")
            .size(10));
        searchSourceBuilder.aggregation(AggregationBuilders
            .date_histogram("time_buckets")
            .field("@timestamp")
            .calendarInterval(DateHistogramInterval.DAY)
            .format("yyyy-MM-dd"));

        // 构建搜索请求
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(indexPattern.split(","));
        searchRequest.source(searchSourceBuilder);

        // 执行搜索
        SearchResponse response = esClient.search(searchRequest, RequestOptions.DEFAULT);
        return processSearchResponse(response, request);

    } catch (IOException e) {
        logger.error("Failed to search logs", e);
        throw new RuntimeException("ES search failed: " + e.getMessage(), e);
    }
}

3. 错误处理机制

异常处理DSL注意事项
/**
 * 错误处理机制
 */
private Map<String, Object> handleException(Exception e, SearchRequest searchRequest) {
    logger.error("Elasticsearch search failed", e);
  
    // 检查不同类型的异常
    if (e instanceof ConnectException) {
        throw new RuntimeException("ES服务连接失败,请检查网络和ES集群状态", e);
    } else if (e instanceof SocketTimeoutException) {
        throw new RuntimeException("ES查询超时,请优化查询条件或调整超时时间", e);
    } else if (e instanceof IndexNotFoundException) {
        throw new RuntimeException("索引不存在,请检查索引名称和权限", e);
    } else if (e instanceof SearchPhaseExecutionException) {
        throw new RuntimeException("查询语法错误,请检查查询条件", e);
    } else {
        throw new RuntimeException("未知错误: " + e.getMessage(), e);
    }
}
DSL查询验证
/**
 * 验证查询条件的正确性
 */
private void validateSearchRequest(LogSearchRequest request) {
    // 验证时间范围
    if (request.getStartTime() != null && request.getEndTime() != null) {
        if (request.getStartTime().isAfter(request.getEndTime())) {
            throw new IllegalArgumentException("开始时间不能晚于结束时间");
        }
    
        // 限制时间范围不超过30天
        if (request.getStartTime().isBefore(request.getEndTime().minusDays(30))) {
            throw new IllegalArgumentException("查询时间范围不能超过30天");
        }
    }
  
    // 验证分页参数
    if (request.getSize() > 1000) {
        throw new IllegalArgumentException("单页查询数量不能超过1000条");
    }
  
    // 验证关键词长度
    if (request.getKeyword() != null && request.getKeyword().length() > 100) {
        throw new IllegalArgumentException("关键词长度不能超过100个字符");
    }
}

配置说明

1. application.yml配置

# Elasticsearch配置
elasticsearch:
  hosts: localhost:9200,localhost:9201
  username: elastic
  password: password
  index-pattern: "logstash-*"
  connection-timeout: 5000
  socket-timeout: 30000
  max-conn-total: 100
  max-conn-per-route: 20

# 日志配置
logging:
  level:
    com.inspur.app.log.service.LogQueryService: DEBUG
    org.elasticsearch: WARN

2. 索引模式配置

Java实现
/**
 * 动态构建索引模式(基于时间范围)
 * @param start 开始时间
 * @param end 结束时间
 * @return 索引模式字符串
 */
private String buildIndexPattern(LocalDateTime start, LocalDateTime end) {
    Set<String> indices = new HashSet<>();
    LocalDateTime startDay = start.withHour(0).withMinute(0).withSecond(0).withNano(0);
    LocalDateTime endDay = end.withHour(0).withMinute(0).withSecond(0).withNano(0);
  
    // 如果结束时间大于当前时间,将结束时间设置为当前时间
    if (endDay.isAfter(LocalDateTime.now())) {
        endDay = LocalDateTime.now();
    }
  
    // 生成索引名称模式
    LocalDateTime current = startDay;
    while (!current.isAfter(endDay)) {
        String indexName = "logstash-" + current.format(INDEX_FORMAT);
        indices.add(indexName);
        current = current.plusDays(1);
    }
  
    return String.join(",", indices);
}

API接口文档

1. 日志搜索接口

/**
 * 日志搜索接口
 */
@RestController
@RequestMapping("/api/logs")
public class LogQueryController {
  
    /**
     * 搜索日志
     * @param request 搜索请求参数
     * @return 搜索结果
     */
    @PostMapping("/search")
    public ResponseEntity<Map<String, Object>> searchLogs(@RequestBody LogSearchRequest request) {
        try {
            validateSearchRequest(request);  // 验证请求参数
            Map<String, Object> result = logQueryService.searchLogs(request);
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            logger.error("Log search failed", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(Collections.singletonMap("error", "搜索失败: " + e.getMessage()));
        }
    }
  
    /**
     * 异常分析接口
     * @param request 分析请求参数
     * @return 分析结果
     */
    @PostMapping("/analyze-error")
    public ResponseEntity<Map<String, Object>> analyzeError(@RequestBody ErrorAnalysisRequest request) {
        try {
            Map<String, Object> result = logQueryService.analyzeError(request);
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            logger.error("Error analysis failed", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(Collections.singletonMap("error", "分析失败: " + e.getMessage()));
        }
    }
  
    /**
     * 获取过滤选项
     * @return 可用的过滤选项
     */
    @GetMapping("/filters")
    public ResponseEntity<Map<String, Object>> getFilters() {
        try {
            Map<String, Object> filters = new HashMap<>();
            filters.put("services", logQueryService.getDistinctServices());
            filters.put("levels", logQueryService.getDistinctLevels());
            return ResponseEntity.ok(filters);
        } catch (Exception e) {
            logger.error("Failed to get filters", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(Collections.singletonMap("error", "获取过滤选项失败: " + e.getMessage()));
        }
    }
}

最佳实践和注意事项

1. 查询性能优化

DSL优化建议:

  • 使用 filter 而不是 must 来执行精确匹配,避免计算相关性得分
  • 合理设置 size 参数,避免一次返回过多数据
  • 使用索引模式而不是通配符,提高查询效率
  • 为常用字段建立合适的mapping,包括analyzer设置

Java API优化:

// 好的做法:使用filter执行精确匹配
BoolQueryBuilder query = QueryBuilders.boolQuery();
query.filter(QueryBuilders.termQuery("level", "ERROR"));  // 不参与评分

// 避免的做法:在must中使用精确匹配
query.must(QueryBuilders.termQuery("level", "ERROR"));   // 参与评分,影响性能

2. 高亮功能使用

DSL高亮优化:

{
  "highlight": {
    "fields": {
      "message": {
        "pre_tags": ["<em>"],
        "post_tags": ["</em>"],
        "fragment_size": 200,      // 适当的片段大小
        "number_of_fragments": 1,  // 减少片段数量
        "require_field_match": false
      }
    }
  }
}

Java API对应:

// 优化高亮配置
HighlightBuilder.Field highlightField = new HighlightBuilder.Field("message");
highlightField.preTags("<em>");
highlightField.postTags("</em>");
highlightField.fragmentSize(200);          // 设置合适的片段大小
highlightField.numOfFragments(1);          // 只返回第一个片段
highlightField.requireFieldMatch(false);   // 不要求字段匹配

3. 安全考虑

DSL注入防护:

/**
 * 验证用户输入,防止DSL注入
 */
private String sanitizeKeyword(String keyword) {
    if (keyword == null) return null;
  
    // 移除潜在的DSL注入字符
    keyword = keyword.replaceAll("[{}\\[\\]<>:\"']", "");
  
    // 限制关键词长度
    if (keyword.length() > 100) {
        keyword = keyword.substring(0, 100);
    }
  
    return keyword;
}

DSL安全检查:

{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "user input",  // 经过安全处理的输入
            "fields": ["message", "stack_trace"]
          }
        }
      ]
    }
  }
}

4. 监控和调试

查询调试信息:

/**
 * 输出调试信息
 */
private void logQueryDebug(SearchSourceBuilder searchSourceBuilder) {
    if (logger.isDebugEnabled()) {
        try {
            // 将SearchSourceBuilder转换为DSL格式
            XContentBuilder builder = XContentFactory.jsonBuilder();
            searchSourceBuilder.toXContent(builder, ToXContent.EMPTY_PARAMS);
            logger.debug("Generated DSL: {}", builder.string());
        } catch (IOException e) {
            logger.warn("Failed to generate DSL debug info", e);
        }
    }
}

总结

本项目实现了完整的Elasticsearch日志查询和分析系统,通过Java API和原始DSL语法的对比映射,为开发者提供了清晰的技术实现参考。文档中的代码示例和DSL语法说明可以直接用于类似的日志查询系统开发。

关键优势:

  1. 双重实现方式 - Java API和DSL语法对照,便于理解和调试
  2. 完整的错误处理 - 全面的异常捕获和用户友好的错误提示
  3. 性能优化实践 - 连接池配置、查询优化、索引管理
  4. 安全防护机制 - 输入验证、DSL注入防护、权限控制
  5. 可维护性设计 - 模块化实现、清晰注释、错误日志记录

通过这种方式,开发者可以深入理解Elasticsearch的底层机制,同时享受Java API的便利性和DSL的灵活性。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值