123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- package searcher
- import (
- "encoding/json"
- "fmt"
- "strconv"
- "strings"
- "github.com/blevesearch/bleve/v2"
- "github.com/blevesearch/bleve/v2/search/query"
- "github.com/uozi-tech/cosy/logger"
- )
- // QueryBuilderService provides high-level query building functionality
- type QueryBuilderService struct {
- defaultAnalyzer string
- }
- // NewQueryBuilderService creates a new query builder service
- func NewQueryBuilderService() *QueryBuilderService {
- return &QueryBuilderService{
- defaultAnalyzer: "standard",
- }
- }
- // BuildQuery builds a Bleve query from a SearchRequest
- func (qb *QueryBuilderService) BuildQuery(req *SearchRequest) (query.Query, error) {
- if req == nil {
- return nil, fmt.Errorf("search request cannot be nil")
- }
- // Build main query
- var mainQuery query.Query
- if req.Query == "" {
- mainQuery = bleve.NewMatchAllQuery()
- } else {
- mainQuery = bleve.NewMatchQuery(req.Query)
- }
- // Create boolean query to combine filters
- boolQuery := bleve.NewBooleanQuery()
- boolQuery.AddMust(mainQuery)
- // Add time range filters
- if req.StartTime != nil || req.EndTime != nil {
- if timeQuery := qb.buildTimeRangeQuery(req.StartTime, req.EndTime); timeQuery != nil {
- boolQuery.AddMust(timeQuery)
- }
- }
- // Add log path filters
- if len(req.LogPaths) > 0 {
- if logPathQuery := qb.buildTermsQuery("file_path", req.LogPaths); logPathQuery != nil {
- boolQuery.AddMust(logPathQuery)
- }
- }
- // Add IP address filters
- if len(req.IPAddresses) > 0 {
- if ipQuery := qb.buildTermsQuery("remote_addr", req.IPAddresses); ipQuery != nil {
- boolQuery.AddMust(ipQuery)
- }
- }
- // Add status code filters
- if len(req.StatusCodes) > 0 {
- if statusQuery := qb.buildStatusCodeQuery(req.StatusCodes); statusQuery != nil {
- boolQuery.AddMust(statusQuery)
- }
- }
- // Add method filters
- if len(req.Methods) > 0 {
- if methodQuery := qb.buildTermsQuery("request_method", req.Methods); methodQuery != nil {
- boolQuery.AddMust(methodQuery)
- }
- }
- // Add country filters
- if len(req.Countries) > 0 {
- if countryQuery := qb.buildTermsQuery("region_code", req.Countries); countryQuery != nil {
- boolQuery.AddMust(countryQuery)
- }
- }
- // Log the final query structure for debugging.
- queryBytes, err := json.Marshal(boolQuery)
- if err == nil {
- logger.Debugf("Constructed Bleve Query: %s", string(queryBytes))
- }
- return boolQuery, nil
- }
- // buildTimeRangeQuery builds a time range query
- func (qb *QueryBuilderService) buildTimeRangeQuery(start, end *int64) query.Query {
- if start == nil && end == nil {
- return nil
- }
- var min, max *float64
- if start != nil {
- startFloat := float64(*start)
- min = &startFloat
- }
- if end != nil {
- endFloat := float64(*end)
- max = &endFloat
- }
- rangeQuery := bleve.NewNumericRangeQuery(min, max)
- rangeQuery.SetField("timestamp")
- return rangeQuery
- }
- // buildTermsQuery builds a terms query for multiple values
- func (qb *QueryBuilderService) buildTermsQuery(field string, terms []string) query.Query {
- if len(terms) == 0 {
- return nil
- }
- if len(terms) == 1 {
- termQuery := bleve.NewTermQuery(terms[0])
- termQuery.SetField(field)
- return termQuery
- }
- // For multiple terms, use boolean OR
- boolQuery := bleve.NewBooleanQuery()
- for _, term := range terms {
- termQuery := bleve.NewTermQuery(term)
- termQuery.SetField(field)
- boolQuery.AddShould(termQuery)
- }
- boolQuery.SetMinShould(1) // Crucial for OR behavior
- return boolQuery
- }
- // buildStatusCodeQuery builds a status code query
- func (qb *QueryBuilderService) buildStatusCodeQuery(codes []int) query.Query {
- if len(codes) == 0 {
- return nil
- }
- // Convert status codes to strings for term query
- terms := make([]string, len(codes))
- for i, code := range codes {
- terms[i] = strconv.Itoa(code)
- }
- return qb.buildTermsQuery("status", terms)
- }
- // ValidateSearchRequest validates a search request
- func (qb *QueryBuilderService) ValidateSearchRequest(req *SearchRequest) error {
- if req == nil {
- return fmt.Errorf("search request cannot be nil")
- }
- if req.Limit < 0 {
- return fmt.Errorf("limit cannot be negative")
- }
- if req.Offset < 0 {
- return fmt.Errorf("offset cannot be negative")
- }
- // Validate sort order
- if req.SortOrder != "" && req.SortOrder != SortOrderAsc && req.SortOrder != SortOrderDesc {
- return fmt.Errorf("invalid sort order: %s", req.SortOrder)
- }
- return nil
- }
- // BuildSuggestionQuery builds a query for suggestions (simplified version)
- func (qb *QueryBuilderService) BuildSuggestionQuery(text, field string) (query.Query, error) {
- if text == "" {
- return nil, fmt.Errorf("suggestion text cannot be empty")
- }
- // Use a prefix query for suggestions
- if field == "" {
- field = "_all"
- }
- prefixQuery := bleve.NewPrefixQuery(strings.ToLower(text))
- prefixQuery.SetField(field)
- return prefixQuery, nil
- }
|