github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/ast/common.go (about)

     1  /*
     2  Copyright 2023.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package ast
    18  
    19  import (
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"regexp"
    24  	"strings"
    25  
    26  	dtu "github.com/siglens/siglens/pkg/common/dtypeutils"
    27  	"github.com/siglens/siglens/pkg/es/query"
    28  	rutils "github.com/siglens/siglens/pkg/readerUtils"
    29  	"github.com/siglens/siglens/pkg/segment"
    30  	segquery "github.com/siglens/siglens/pkg/segment/query"
    31  	"github.com/siglens/siglens/pkg/segment/reader/record"
    32  	"github.com/siglens/siglens/pkg/segment/structs"
    33  	. "github.com/siglens/siglens/pkg/segment/structs"
    34  	"github.com/siglens/siglens/pkg/segment/utils"
    35  	. "github.com/siglens/siglens/pkg/segment/utils"
    36  	log "github.com/sirupsen/logrus"
    37  )
    38  
    39  // When valueIsRegex is true, colValue should be a string containing the regex
    40  // to match and should not have quotation marks as the first and last character
    41  // unless those are intended to be matched.
    42  func ProcessSingleFilter(colName string, colValue interface{}, compOpr string, valueIsRegex bool, qid uint64) ([]*FilterCriteria, error) {
    43  	andFilterCondition := make([]*FilterCriteria, 0)
    44  	var opr FilterOperator = Equals
    45  	switch compOpr {
    46  	case ">":
    47  		opr = GreaterThan
    48  	case ">=":
    49  		opr = GreaterThanOrEqualTo
    50  	case "<":
    51  		opr = LessThan
    52  	case "<=":
    53  		opr = LessThanOrEqualTo
    54  	case "=":
    55  		opr = Equals
    56  	case "!=":
    57  		opr = NotEquals
    58  	default:
    59  		log.Errorf("qid=%d, ProcessSingleFilter: invalid comparison operator %v", qid, opr)
    60  		return nil, errors.New("ProcessSingleFilter: invalid comparison operator")
    61  	}
    62  	switch t := colValue.(type) {
    63  	case string:
    64  		if t != "" {
    65  			if colName == "" || colName == "*" {
    66  				colName = "*"
    67  
    68  				if valueIsRegex {
    69  					compiledRegex, err := regexp.Compile(t)
    70  					if err != nil {
    71  						log.Errorf("qid=%d, ProcessSingleFilter: Failed to compile regex for %s. This may cause search failures. Err: %v", qid, t, err)
    72  						return nil, fmt.Errorf("Invalid regex: %s", t)
    73  					}
    74  					criteria := CreateTermFilterCriteria(colName, compiledRegex, opr, qid)
    75  					andFilterCondition = append(andFilterCondition, criteria)
    76  				} else {
    77  					negateMatch := (opr == NotEquals)
    78  					if opr != Equals && opr != NotEquals {
    79  						log.Errorf("qid=%d, ProcessSingleFilter: invalid string comparison operator %v", qid, opr)
    80  					}
    81  
    82  					cleanedColVal := strings.ReplaceAll(strings.TrimSpace(t), "\"", "")
    83  					if strings.Contains(t, "\"") {
    84  						criteria := createMatchPhraseFilterCriteria(colName, cleanedColVal, And, negateMatch, qid)
    85  						andFilterCondition = append(andFilterCondition, criteria)
    86  					} else {
    87  						if strings.Contains(t, "*") {
    88  							criteria := CreateTermFilterCriteria(colName, colValue, opr, qid)
    89  							andFilterCondition = append(andFilterCondition, criteria)
    90  						} else {
    91  							criteria := createMatchFilterCriteria(colName, colValue, And, negateMatch, qid)
    92  							andFilterCondition = append(andFilterCondition, criteria)
    93  						}
    94  					}
    95  				}
    96  			} else {
    97  				if valueIsRegex {
    98  					compiledRegex, err := regexp.Compile(t)
    99  					if err != nil {
   100  						log.Errorf("ProcessSingleFilter: Failed to compile regex for %s. This may cause search failures. Err: %v", t, err)
   101  						return nil, fmt.Errorf("Invalid regex: %s", t)
   102  					}
   103  					criteria := CreateTermFilterCriteria(colName, compiledRegex, opr, qid)
   104  					andFilterCondition = append(andFilterCondition, criteria)
   105  				} else {
   106  					cleanedColVal := strings.ReplaceAll(strings.TrimSpace(t), "\"", "")
   107  					criteria := CreateTermFilterCriteria(colName, cleanedColVal, opr, qid)
   108  					andFilterCondition = append(andFilterCondition, criteria)
   109  				}
   110  			}
   111  		} else {
   112  			return nil, errors.New("ProcessSingleFilter: colValue/ search Text can not be empty ")
   113  		}
   114  	case bool:
   115  		criteria := CreateTermFilterCriteria(colName, colValue, opr, qid)
   116  		andFilterCondition = append(andFilterCondition, criteria)
   117  	case json.Number:
   118  		if colValue.(json.Number) != "" {
   119  			if colName == "" {
   120  				colName = "*"
   121  			}
   122  			criteria := CreateTermFilterCriteria(colName, colValue, opr, qid)
   123  			andFilterCondition = append(andFilterCondition, criteria)
   124  
   125  		} else {
   126  			return nil, errors.New("ProcessSingleFilter: colValue/ search Text can not be empty ")
   127  		}
   128  	case GrepValue:
   129  		cleanedColVal := strings.ReplaceAll(strings.TrimSpace(t.Field), "\"", "")
   130  		criteria := CreateTermFilterCriteria("*", cleanedColVal, opr, qid)
   131  		andFilterCondition = append(andFilterCondition, criteria)
   132  	default:
   133  		log.Errorf("ProcessSingleFilter: Invalid colValue type %v", t)
   134  		return nil, errors.New("ProcessSingleFilter: Invalid colValue type")
   135  	}
   136  	return andFilterCondition, nil
   137  }
   138  
   139  func createMatchPhraseFilterCriteria(k, v interface{}, opr LogicalOperator, negateMatch bool, qid uint64) *FilterCriteria {
   140  	//match_phrase value will always be string
   141  	var rtInput = strings.TrimSpace(v.(string))
   142  	var matchWords = make([][]byte, 0)
   143  	for _, word := range strings.Split(rtInput, " ") {
   144  		matchWords = append(matchWords, [][]byte{[]byte(word)}...)
   145  	}
   146  	criteria := FilterCriteria{MatchFilter: &MatchFilter{
   147  		MatchColumn:   k.(string),
   148  		MatchWords:    matchWords,
   149  		MatchOperator: opr,
   150  		MatchPhrase:   []byte(rtInput),
   151  		MatchType:     MATCH_PHRASE,
   152  		NegateMatch:   negateMatch}}
   153  	return &criteria
   154  }
   155  
   156  func createMatchFilterCriteria(k, v interface{}, opr LogicalOperator, negateMatch bool, qid uint64) *FilterCriteria {
   157  	var rtInput string
   158  	switch vtype := v.(type) {
   159  	case json.Number:
   160  		rtInput = string(vtype)
   161  	case string:
   162  		rtInput = vtype
   163  	default:
   164  		log.Errorf("qid=%d, createMatchFilterCriteria: invalid value ", qid)
   165  	}
   166  	words := strings.Split(rtInput, " ")
   167  	var matchWords = make([][]byte, 0)
   168  	for _, word := range words {
   169  		word = strings.TrimSpace(word)
   170  		if word != "" {
   171  			matchWords = append(matchWords, []byte(word))
   172  		}
   173  	}
   174  
   175  	_, ok := k.(string)
   176  	if !ok {
   177  		log.Errorf("qid=%d, createMatchFilterCriteria: invalid type for key %+v", qid, k)
   178  		return nil
   179  	}
   180  
   181  	criteria := FilterCriteria{MatchFilter: &MatchFilter{
   182  		MatchColumn:   k.(string),
   183  		MatchWords:    matchWords,
   184  		MatchOperator: opr,
   185  		NegateMatch:   negateMatch}}
   186  
   187  	return &criteria
   188  }
   189  
   190  func CreateTermFilterCriteria(k string, v interface{}, opr FilterOperator, qid uint64) *FilterCriteria {
   191  	cVal, err := CreateDtypeEnclosure(v, qid)
   192  	if err != nil {
   193  		log.Errorf("qid=%d, createTermFilterCriteria: error creating DtypeEnclosure: %+v", qid, err)
   194  	}
   195  	criteria := FilterCriteria{ExpressionFilter: &ExpressionFilter{
   196  		LeftInput: &FilterInput{Expression: &Expression{
   197  			LeftInput: &ExpressionInput{ColumnName: k}}},
   198  		FilterOperator: opr,
   199  		RightInput: &FilterInput{Expression: &Expression{
   200  			LeftInput: &ExpressionInput{ColumnValue: cVal}}}}}
   201  	return &criteria
   202  }
   203  
   204  // Executes simple query to return a single column values in a given table
   205  func GetColValues(cname string, table string, qid uint64, orgid uint64) ([]interface{}, error) {
   206  	aggNode := structs.InitDefaultQueryAggregations()
   207  	astNode, err := query.GetMatchAllASTNode(qid)
   208  	if err != nil {
   209  		log.Errorf("qid=%v, GetColValues: match all ast node failed! %+v", qid, err)
   210  		return nil, err
   211  	}
   212  	aggNode.OutputTransforms = &structs.OutputTransforms{OutputColumns: &structs.ColumnsRequest{}}
   213  	aggNode.OutputTransforms.OutputColumns.IncludeColumns = append(make([]string, 0), cname)
   214  
   215  	ti := structs.InitTableInfo(table, orgid, false)
   216  	qc := structs.InitQueryContextWithTableInfo(ti, segquery.MAX_GRP_BUCKS, 0, orgid, false)
   217  	queryResult := segment.ExecuteQuery(astNode, aggNode, qid, qc)
   218  	allJsons, _, err := record.GetJsonFromAllRrc(queryResult.AllRecords, false, qid, queryResult.SegEncToKey, aggNode)
   219  	if err != nil {
   220  		log.Errorf("qid=%v, GetColValues: get json from all records failed! %+v", qid, err)
   221  		return nil, err
   222  	}
   223  
   224  	colVals := make([]interface{}, 0)
   225  	for _, row := range allJsons {
   226  		colVals = append(colVals, row[cname])
   227  	}
   228  
   229  	return colVals, nil
   230  }
   231  
   232  func ParseTimeRange(startEpoch, endEpoch uint64, aggs *QueryAggregators, qid uint64) (*dtu.TimeRange, error) {
   233  	tRange := new(dtu.TimeRange)
   234  	if aggs != nil && aggs.TimeHistogram != nil && aggs.TimeHistogram.Timechart == nil {
   235  		tRange.StartEpochMs = aggs.TimeHistogram.StartTime
   236  		tRange.EndEpochMs = aggs.TimeHistogram.EndTime
   237  		return tRange, nil
   238  	}
   239  	if startEpoch == 0 && endEpoch == 0 {
   240  		//set default time range to last 90 days
   241  		return rutils.GetESDefaultQueryTimeRange(), nil
   242  	} else if startEpoch == 0 || endEpoch == 0 {
   243  		err := fmt.Errorf("parseTimeRange: , startEpoch/ endEpoch not set : %v %v", startEpoch, endEpoch)
   244  		return nil, err
   245  	}
   246  	tRange.StartEpochMs = startEpoch
   247  	tRange.EndEpochMs = endEpoch
   248  	return tRange, nil
   249  }
   250  
   251  func GetDefaultTimechartSpanOptions(startEpoch, endEpoch uint64, qid uint64) (*structs.SpanOptions, error) {
   252  	if startEpoch == 0 || endEpoch == 0 {
   253  		err := fmt.Errorf("GetDefaultTimechartSpanOptions: , time range not set")
   254  		return nil, err
   255  	}
   256  
   257  	duration := endEpoch - startEpoch
   258  
   259  	// 15 minutes
   260  	if duration <= 15*60*1000 {
   261  		return &structs.SpanOptions{SpanLength: &structs.SpanLength{Num: 10, TimeScalr: utils.TMSecond}}, nil
   262  	} else if duration <= 60*60*1000 {
   263  		return &structs.SpanOptions{SpanLength: &structs.SpanLength{Num: 1, TimeScalr: utils.TMMinute}}, nil
   264  	} else if duration <= 4*60*60*1000 {
   265  		return &structs.SpanOptions{SpanLength: &structs.SpanLength{Num: 5, TimeScalr: utils.TMMinute}}, nil
   266  	} else if duration <= 24*60*60*1000 {
   267  		return &structs.SpanOptions{SpanLength: &structs.SpanLength{Num: 30, TimeScalr: utils.TMMinute}}, nil
   268  	} else if duration <= 7*24*60*60*1000 {
   269  		return &structs.SpanOptions{SpanLength: &structs.SpanLength{Num: 1, TimeScalr: utils.TMHour}}, nil
   270  	} else if duration <= 180*24*60*60*1000 {
   271  		return &structs.SpanOptions{SpanLength: &structs.SpanLength{Num: 1, TimeScalr: utils.TMDay}}, nil
   272  	} else {
   273  		return &structs.SpanOptions{SpanLength: &structs.SpanLength{Num: 1, TimeScalr: utils.TMMonth}}, nil
   274  	}
   275  
   276  }