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 }