github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/segment/segexecution_test.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 segment 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "os" 23 "strconv" 24 "testing" 25 "time" 26 27 esquery "github.com/siglens/siglens/pkg/es/query" 28 "github.com/siglens/siglens/pkg/scroll" 29 toputils "github.com/siglens/siglens/pkg/utils" 30 31 "github.com/google/uuid" 32 localstorage "github.com/siglens/siglens/pkg/blob/local" 33 dtu "github.com/siglens/siglens/pkg/common/dtypeutils" 34 "github.com/siglens/siglens/pkg/config" 35 "github.com/siglens/siglens/pkg/instrumentation" 36 "github.com/siglens/siglens/pkg/segment/memory/limit" 37 "github.com/siglens/siglens/pkg/segment/query" 38 "github.com/siglens/siglens/pkg/segment/query/metadata" 39 "github.com/siglens/siglens/pkg/segment/reader/microreader" 40 "github.com/siglens/siglens/pkg/segment/reader/record" 41 "github.com/siglens/siglens/pkg/segment/reader/segread" 42 "github.com/siglens/siglens/pkg/segment/results/blockresults" 43 "github.com/siglens/siglens/pkg/segment/results/segresults" 44 "github.com/siglens/siglens/pkg/segment/structs" 45 . "github.com/siglens/siglens/pkg/segment/structs" 46 "github.com/siglens/siglens/pkg/segment/utils" 47 . "github.com/siglens/siglens/pkg/segment/utils" 48 "github.com/siglens/siglens/pkg/segment/writer" 49 serverutils "github.com/siglens/siglens/pkg/server/utils" 50 log "github.com/sirupsen/logrus" 51 "github.com/stretchr/testify/assert" 52 ) 53 54 func simpleQueryTest(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int) { 55 value1, _ := CreateDtypeEnclosure("value1", 0) 56 valueFilter := FilterCriteria{ 57 ExpressionFilter: &ExpressionFilter{ 58 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key1"}}}, 59 FilterOperator: Equals, 60 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 61 }, 62 } 63 simpleNode := &ASTNode{ 64 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&valueFilter}}, 65 TimeRange: &dtu.TimeRange{ 66 StartEpochMs: 1, 67 EndEpochMs: uint64(numEntriesForBuffer) + 1, 68 }, 69 } 70 ti := structs.InitTableInfo("evts", 0, false) 71 sizeLimit := uint64(10000) 72 qc := structs.InitQueryContextWithTableInfo(ti, sizeLimit, 0, 0, false) 73 result := ExecuteQuery(simpleNode, &QueryAggregators{}, 0, qc) 74 log.Info(result) 75 assert.NotNil(t, result, "Query ran successfully") 76 assert.Len(t, result.AllRecords, numBuffers*numEntriesForBuffer*fileCount, "all logs in all files should have matched") 77 assert.Len(t, result.ErrList, 0, "no errors should have occurred") 78 79 nine, _ := CreateDtypeEnclosure(9, 0) 80 rangeFilter := FilterCriteria{ 81 ExpressionFilter: &ExpressionFilter{ 82 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key2"}}}, 83 FilterOperator: GreaterThanOrEqualTo, 84 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: nine}}}, 85 }, 86 } 87 88 simpleNode.AndFilterCondition.FilterCriteria = append(simpleNode.AndFilterCondition.FilterCriteria, &rangeFilter) 89 result = ExecuteQuery(simpleNode, &QueryAggregators{}, 0, qc) 90 log.Info(result) 91 assert.NotNil(t, result, "Query ran successfully") 92 assert.Len(t, result.AllRecords, numBuffers*fileCount, "each buffer in each file will only have one match") 93 assert.Len(t, result.ErrList, 0, "no errors should have occurred") 94 95 filterCondition := FilterCriteria{ 96 ExpressionFilter: &ExpressionFilter{ 97 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key1"}}}, 98 FilterOperator: Equals, 99 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 100 }, 101 } 102 simpleNode.ExclusionFilterCondition = &Condition{FilterCriteria: []*FilterCriteria{&filterCondition}} 103 log.Infof("%v", simpleNode) 104 result = ExecuteQuery(simpleNode, &QueryAggregators{}, 0, qc) 105 log.Info(result) 106 assert.NotNil(t, result, "Query ran successfully") 107 assert.Len(t, result.AllRecords, 0, "exclusion filter criteria should make query return nothing") 108 assert.Len(t, result.ErrList, 0, "no errors should have occurred") 109 110 zero, _ := CreateDtypeEnclosure(0, 0) 111 orCondition := FilterCriteria{ 112 ExpressionFilter: &ExpressionFilter{ 113 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key2"}}}, 114 FilterOperator: Equals, 115 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: zero}}}, 116 }, 117 } 118 simpleNode.OrFilterCondition = &Condition{FilterCriteria: []*FilterCriteria{&orCondition}} 119 result = ExecuteQuery(simpleNode, &QueryAggregators{}, 0, qc) 120 assert.NotNil(t, result, "Query ran successfully") 121 assert.Len(t, result.AllRecords, 0, "or filter shouldhave no effect") 122 assert.Len(t, result.ErrList, 0, "no errors should have occurred") 123 124 // TODO: uncomment after isNotNull/isNull logic 125 // columnExistsCondition := FilterCriteria{ 126 // ExpressionFilter: &ExpressionFilter{ 127 // LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key2"}}}, 128 // FilterOperator: IsNotNull, 129 // }, 130 // } 131 // columnNode := &ASTNode{ 132 // AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&columnExistsCondition}}, 133 // TimeRange: &dtu.TimeRange{ 134 // StartEpochMs: 0, 135 // EndEpochMs: uint64(numEntriesForBuffer), 136 // }, 137 // } 138 // result = ExecuteQuery(columnNode, &Aggregators{}, indexName, sizeLimit) 139 // assert.NotNil(t, result, "Query ran successfully") 140 // assert.Len(t, result.AllRecords, numBuffers*numEntriesForBuffer*fileCount, "all records should have key2") 141 // assert.Len(t, result.ErrList, 0, "no errors should have occurred") 142 143 invalidColumnCondition := FilterCriteria{ 144 ExpressionFilter: &ExpressionFilter{ 145 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "abc"}}}, 146 FilterOperator: IsNotNull, 147 }, 148 } 149 columnNode := &ASTNode{ 150 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&invalidColumnCondition}}, 151 TimeRange: &dtu.TimeRange{ 152 StartEpochMs: 1, 153 EndEpochMs: uint64(numEntriesForBuffer) + 1, 154 }, 155 } 156 columnNode.AndFilterCondition = &Condition{FilterCriteria: []*FilterCriteria{&invalidColumnCondition}} 157 result = ExecuteQuery(columnNode, &QueryAggregators{}, 0, qc) 158 assert.NotNil(t, result, "Query ran successfully") 159 assert.Len(t, result.AllRecords, 0, "no column abc exists") 160 assert.NotEqual(t, 0, result.ErrList, "column not found errors MUST happened") 161 } 162 163 func wildcardQueryTest(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int) { 164 value1, _ := CreateDtypeEnclosure("value1", 0) 165 // wildcard all columns 166 ti := structs.InitTableInfo("evts", 0, false) 167 allColumns := FilterCriteria{ 168 ExpressionFilter: &ExpressionFilter{ 169 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "*"}}}, 170 FilterOperator: Equals, 171 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 172 }, 173 } 174 simpleNode := &ASTNode{ 175 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&allColumns}}, 176 TimeRange: &dtu.TimeRange{ 177 StartEpochMs: 0, 178 EndEpochMs: uint64(numEntriesForBuffer), 179 }, 180 } 181 qc := structs.InitQueryContextWithTableInfo(ti, 10000, 0, 0, false) 182 result := ExecuteQuery(simpleNode, &QueryAggregators{}, 0, qc) 183 assert.NotNil(t, result, "Query ran successfully") 184 assert.Len(t, result.AllRecords, numEntriesForBuffer*numBuffers*fileCount, "all log lines match") 185 assert.Len(t, result.ErrList, 0, "no errors should have occurred") 186 187 batch0, _ := CreateDtypeEnclosure("batch-0-*", 0) 188 allKeyColumns := FilterCriteria{ 189 ExpressionFilter: &ExpressionFilter{ 190 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key*"}}}, 191 FilterOperator: Equals, 192 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: batch0}}}, 193 }, 194 } 195 simpleNode = &ASTNode{ 196 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&allKeyColumns}}, 197 TimeRange: &dtu.TimeRange{ 198 StartEpochMs: 0, 199 EndEpochMs: uint64(numEntriesForBuffer), 200 }, 201 } 202 result = ExecuteQuery(simpleNode, &QueryAggregators{}, 0, qc) 203 for _, rrc := range result.AllRecords { 204 205 blkRecIndexes := make(map[uint16]map[uint16]uint64) 206 recIdxs := make(map[uint16]uint64) 207 recIdxs[rrc.RecordNum] = 1 208 blkRecIndexes[rrc.BlockNum] = recIdxs 209 segkey := result.SegEncToKey[rrc.SegKeyInfo.SegKeyEnc] 210 records, _, err := record.GetRecordsFromSegment(segkey, rrc.VirtualTableName, blkRecIndexes, "timestamp", false, 0, &QueryAggregators{}) 211 assert.Nil(t, err) 212 213 log.Info(records) 214 } 215 assert.NotNil(t, result, "Query ran successfully") 216 assert.Len(t, result.AllRecords, 0, "partial column wildcard is not supported") 217 assert.NotEqual(t, 0, result.ErrList, "column not found errors MUST happen") 218 } 219 220 func timeHistogramQueryTest(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int) { 221 value1, _ := CreateDtypeEnclosure("value1", 0) 222 ti := structs.InitTableInfo("evts", 0, false) 223 allColumns := FilterCriteria{ 224 ExpressionFilter: &ExpressionFilter{ 225 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key1"}}}, 226 FilterOperator: Equals, 227 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 228 }, 229 } 230 simpleNode := &ASTNode{ 231 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&allColumns}}, 232 TimeRange: &dtu.TimeRange{ 233 StartEpochMs: 1, 234 EndEpochMs: uint64(numEntriesForBuffer) + 1, 235 }, 236 } 237 238 simpleTimeHistogram := &QueryAggregators{ 239 TimeHistogram: &TimeBucket{ 240 StartTime: 1, 241 EndTime: uint64(numEntriesForBuffer) + 1, 242 IntervalMillis: 1, 243 AggName: "testTime", 244 }, 245 } 246 qc := structs.InitQueryContextWithTableInfo(ti, 10000, 0, 0, false) 247 result := ExecuteQuery(simpleNode, simpleTimeHistogram, 101, qc) 248 seenKeys := make(map[uint64]bool) 249 lenHist := len(result.Histogram["testTime"].Results) 250 for i := 0; i < lenHist; i++ { 251 assert.Equal(t, result.Histogram["testTime"].Results[i].ElemCount, uint64(10)) 252 currKey := result.Histogram["testTime"].Results[i].BucketKey.(uint64) 253 seenKeys[currKey] = true 254 } 255 assert.Len(t, seenKeys, 10) 256 assert.Condition(t, func() (success bool) { 257 for i := uint64(1); i < uint64(numEntriesForBuffer+1); i++ { 258 if _, ok := seenKeys[i]; !ok { 259 return false 260 } 261 } 262 return true 263 }) 264 } 265 266 func groupByQueryTest(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int) { 267 value1, _ := CreateDtypeEnclosure("value1", 0) 268 ti := structs.InitTableInfo("evts", 0, false) 269 allColumns := FilterCriteria{ 270 ExpressionFilter: &ExpressionFilter{ 271 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key1"}}}, 272 FilterOperator: Equals, 273 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 274 }, 275 } 276 simpleNode := &ASTNode{ 277 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&allColumns}}, 278 TimeRange: &dtu.TimeRange{ 279 StartEpochMs: 1, 280 EndEpochMs: uint64(numEntriesForBuffer) + 1, 281 }, 282 } 283 284 simpleGroupBy := &QueryAggregators{ 285 GroupByRequest: &GroupByRequest{ 286 GroupByColumns: []string{"key11"}, 287 MeasureOperations: []*MeasureAggregator{ 288 {MeasureCol: "key2", MeasureFunc: Max}, 289 {MeasureCol: "key2", MeasureFunc: Min}, 290 {MeasureCol: "key8", MeasureFunc: Sum}, 291 }, 292 AggName: "test", 293 BucketCount: 100, 294 }, 295 } 296 297 mnames := make([]string, len(simpleGroupBy.GroupByRequest.MeasureOperations)) 298 for i, mOp := range simpleGroupBy.GroupByRequest.MeasureOperations { 299 mnames[i] = mOp.String() 300 } 301 qc := structs.InitQueryContextWithTableInfo(ti, 10000, 0, 0, false) 302 result := ExecuteQuery(simpleNode, simpleGroupBy, 102, qc) 303 lenHist := len(result.Histogram["test"].Results) 304 assert.False(t, result.Histogram["test"].IsDateHistogram) 305 assert.Equal(t, lenHist, 2, "only record-batch-1 and record-batch-0 exist") 306 totalentries := numEntriesForBuffer * fileCount * numBuffers 307 for i := 0; i < lenHist; i++ { 308 assert.Equal(t, result.Histogram["test"].Results[i].ElemCount, uint64(totalentries/2)) 309 bKey := result.Histogram["test"].Results[i].BucketKey 310 311 assert.Len(t, result.Histogram["test"].Results[i].StatRes, len(simpleGroupBy.GroupByRequest.MeasureOperations)) 312 log.Infof("bkey is %+v", bKey) 313 for mFunc, m := range mnames { 314 res, ok := result.Histogram["test"].Results[i].StatRes[m] 315 316 assert.True(t, ok) 317 if mFunc == 0 { 318 if bKey == "record-batch-0" { 319 assert.Equal(t, res.CVal, int64(numEntriesForBuffer-2)) 320 } else if bKey == "record-batch-1" { 321 assert.Equal(t, res.CVal, int64(numEntriesForBuffer-1)) 322 } else { 323 assert.Fail(t, "unexpected bkey %+v", bKey) 324 } 325 } else if mFunc == 1 { 326 if bKey == "record-batch-0" { 327 assert.Equal(t, res.CVal, int64(0)) 328 } else if bKey == "record-batch-1" { 329 assert.Equal(t, res.CVal, int64(1)) 330 } else { 331 assert.Fail(t, "unexpected bkey %+v", bKey) 332 } 333 } else if mFunc == 2 { 334 assert.Greater(t, res.CVal, int64(numBuffers*fileCount)) 335 } else { 336 assert.Fail(t, "unexpected case %+v", mFunc) 337 } 338 } 339 } 340 } 341 342 func timechartGroupByQueryTest(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int) { 343 value1, _ := CreateDtypeEnclosure("value1", 0) 344 ti := structs.InitTableInfo("evts", 0, false) 345 allColumns := FilterCriteria{ 346 ExpressionFilter: &ExpressionFilter{ 347 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key1"}}}, 348 FilterOperator: Equals, 349 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 350 }, 351 } 352 simpleNode := &ASTNode{ 353 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&allColumns}}, 354 TimeRange: &dtu.TimeRange{ 355 StartEpochMs: 1, 356 EndEpochMs: uint64(numEntriesForBuffer) + 1, 357 }, 358 } 359 360 timechart := &TimechartExpr{ 361 ByField: "key11", 362 } 363 364 // time range is [1, 11], so it should have 3 time range buckets: [1, 5), [5, 9), [9, 11] 365 simpleGroupBy := &QueryAggregators{ 366 TimeHistogram: &TimeBucket{ 367 IntervalMillis: 4, 368 StartTime: 1, 369 EndTime: uint64(numEntriesForBuffer) + 1, 370 Timechart: timechart, 371 }, 372 GroupByRequest: &GroupByRequest{ 373 GroupByColumns: []string{"timestamp"}, 374 MeasureOperations: []*MeasureAggregator{ 375 {MeasureCol: "key2", MeasureFunc: Avg}, 376 {MeasureCol: "key8", MeasureFunc: Sum}, 377 }, 378 AggName: "test", 379 BucketCount: 100, 380 }, 381 } 382 383 mnames := make([]string, len(simpleGroupBy.GroupByRequest.MeasureOperations)) 384 for i, mOp := range simpleGroupBy.GroupByRequest.MeasureOperations { 385 mnames[i] = mOp.String() 386 } 387 qc := structs.InitQueryContextWithTableInfo(ti, 10000, 0, 0, false) 388 result := ExecuteQuery(simpleNode, simpleGroupBy, 102, qc) 389 lenHist := len(result.Histogram["test"].Results) 390 assert.False(t, result.Histogram["test"].IsDateHistogram) 391 assert.Equal(t, 3, lenHist, "it should have 3 time range buckets: [1, 5), [5, 9), [9, 11]") 392 timeBucketsMap := map[string]struct{}{ 393 "1": {}, 394 "5": {}, 395 "9": {}, 396 } 397 398 totalentries := uint64(numEntriesForBuffer * fileCount * numBuffers) 399 sumRecord0 := uint64(0) 400 sumRecord1 := uint64(0) 401 for i := 0; i < lenHist; i++ { 402 bKey := result.Histogram["test"].Results[i].BucketKey 403 timestamp, ok := bKey.(string) 404 assert.True(t, ok) 405 _, exists := timeBucketsMap[timestamp] 406 assert.True(t, exists) 407 delete(timeBucketsMap, timestamp) 408 409 assert.Len(t, result.Histogram["test"].Results[i].StatRes, len(simpleGroupBy.GroupByRequest.MeasureOperations)*2) 410 log.Infof("bkey is %+v", bKey) 411 412 m := mnames[1] 413 414 res0, ok := result.Histogram["test"].Results[i].StatRes[m+": record-batch-0"] 415 assert.True(t, ok) 416 num1, err := res0.GetUIntValue() 417 assert.Nil(t, err) 418 419 res1, ok := result.Histogram["test"].Results[i].StatRes[m+": record-batch-1"] 420 assert.True(t, ok) 421 num2, err := res1.GetUIntValue() 422 assert.Nil(t, err) 423 424 sumRecord0 += num1 425 sumRecord1 += num2 426 } 427 428 assert.Equal(t, totalentries, sumRecord0) 429 assert.Equal(t, totalentries, sumRecord1) 430 } 431 432 func nestedQueryTest(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int) { 433 ti := structs.InitTableInfo("evts", 0, false) 434 // key6==key2 when i == 0 435 one, _ := CreateDtypeEnclosure(1, 0) 436 zero, _ := CreateDtypeEnclosure(0, 0) 437 columnRelation := FilterCriteria{ 438 ExpressionFilter: &ExpressionFilter{ 439 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key6"}}}, 440 FilterOperator: Equals, 441 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: zero}}}, 442 }, 443 } 444 445 keyRelation := FilterCriteria{ 446 ExpressionFilter: &ExpressionFilter{ 447 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key2"}}}, 448 FilterOperator: Equals, 449 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: one}}}, 450 }, 451 } 452 453 key2Node := &ASTNode{ 454 AndFilterCondition: &Condition{ 455 FilterCriteria: []*FilterCriteria{&keyRelation}, 456 }, 457 TimeRange: &dtu.TimeRange{ 458 StartEpochMs: 1, 459 EndEpochMs: uint64(numEntriesForBuffer) + 1, 460 }, 461 } 462 463 nestedNode := &ASTNode{ 464 AndFilterCondition: &Condition{ 465 FilterCriteria: []*FilterCriteria{&columnRelation}, 466 NestedNodes: []*ASTNode{key2Node}, 467 }, 468 TimeRange: &dtu.TimeRange{ 469 StartEpochMs: 1, 470 EndEpochMs: uint64(numEntriesForBuffer) + 1, 471 }, 472 } 473 qc := structs.InitQueryContextWithTableInfo(ti, 10000, 0, 0, false) 474 result := ExecuteQuery(nestedNode, nil, 0, qc) 475 assert.Len(t, result.ErrList, 0) 476 assert.Len(t, result.AllRecords, 0, "conditions are exclusive, no responses should match") 477 478 nestedNode = &ASTNode{ 479 OrFilterCondition: &Condition{ 480 FilterCriteria: []*FilterCriteria{&columnRelation}, 481 NestedNodes: []*ASTNode{key2Node}, 482 }, 483 TimeRange: &dtu.TimeRange{ 484 StartEpochMs: 1, 485 EndEpochMs: uint64(numEntriesForBuffer) + 1, 486 }, 487 } 488 result = ExecuteQuery(nestedNode, nil, 0, qc) 489 assert.Len(t, result.ErrList, 0) 490 assert.Len(t, result.AllRecords, numBuffers*fileCount*2, "should match when key2=0 and 1") 491 492 multiNestedNode := &ASTNode{ 493 OrFilterCondition: &Condition{ 494 NestedNodes: []*ASTNode{nestedNode}, 495 }, 496 TimeRange: &dtu.TimeRange{ 497 StartEpochMs: 1, 498 EndEpochMs: uint64(numEntriesForBuffer) + 1, 499 }, 500 } 501 result = ExecuteQuery(multiNestedNode, nil, 0, qc) 502 assert.Len(t, result.ErrList, 0) 503 assert.Len(t, result.AllRecords, numBuffers*fileCount*2, "nesting node another level should have no affect") 504 } 505 506 func nestedAggregationQueryTest(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int) { 507 ti := structs.InitTableInfo("evts", 0, false) 508 value1, _ := CreateDtypeEnclosure("value1", 0) 509 510 filter := FilterCriteria{ 511 ExpressionFilter: &ExpressionFilter{ 512 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key1"}}}, 513 FilterOperator: Equals, 514 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 515 }, 516 } 517 simpleNode := &ASTNode{ 518 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&filter}}, 519 TimeRange: &dtu.TimeRange{ 520 StartEpochMs: 1, 521 EndEpochMs: uint64(numEntriesForBuffer) + 1, 522 }, 523 } 524 525 simpleMeasure := &QueryAggregators{ 526 PipeCommandType: MeasureAggsType, 527 MeasureOperations: []*MeasureAggregator{ 528 &MeasureAggregator{ 529 MeasureCol: "key2", 530 MeasureFunc: Max, 531 }, 532 }, 533 } 534 535 // Test renaming the measured column. 536 renameAggs := make(map[string]string) 537 renameAggs["max(key2)"] = "Max" 538 simpleRename := &QueryAggregators{ 539 PipeCommandType: OutputTransformType, 540 OutputTransforms: &OutputTransforms{ 541 OutputColumns: &ColumnsRequest{ 542 RenameAggregationColumns: renameAggs, 543 }, 544 }, 545 } 546 547 simpleMeasure.Next = simpleRename 548 549 qc := structs.InitQueryContextWithTableInfo(ti, 10000, 0, 0, false) 550 result := ExecuteQuery(simpleNode, simpleMeasure, 0, qc) 551 552 assert.Len(t, result.AllRecords, 0) 553 assert.Zero(t, result.TotalResults.TotalCount) 554 assert.False(t, result.TotalResults.EarlyExit) 555 556 assert.Len(t, result.MeasureFunctions, 1) 557 assert.Equal(t, result.MeasureFunctions[0], "Max") 558 559 // Test creating a new column using the renamed column. 560 simpleLetColumns := &QueryAggregators{ 561 PipeCommandType: OutputTransformType, 562 OutputTransforms: &OutputTransforms{ 563 LetColumns: &LetColumnsRequest{ 564 NewColName: "MaxSeconds", 565 ValueColRequest: &ValueExpr{ 566 ValueExprMode: VEMStringExpr, 567 StringExpr: &StringExpr{ 568 StringExprMode: SEMConcatExpr, 569 ConcatExpr: &ConcatExpr{ 570 571 Atoms: []*ConcatAtom{ 572 {IsField: true, Value: "Max"}, 573 {IsField: false, Value: " seconds"}, 574 }, 575 }, 576 }, 577 }, 578 }, 579 }, 580 } 581 582 simpleRename.Next = simpleLetColumns 583 584 result = ExecuteQuery(simpleNode, simpleMeasure, 0, qc) 585 586 assert.Len(t, result.AllRecords, 0) 587 assert.Zero(t, result.TotalResults.TotalCount) 588 assert.False(t, result.TotalResults.EarlyExit) 589 590 assert.Len(t, result.MeasureFunctions, 2) 591 assert.Equal(t, result.MeasureFunctions[0], "Max") 592 assert.Equal(t, result.MeasureFunctions[1], "MaxSeconds") 593 594 assert.Len(t, result.MeasureResults, 1) 595 maxStr := result.MeasureResults[0].MeasureVal["Max"].(string) 596 assert.Equal(t, result.MeasureResults[0].MeasureVal["Max"], maxStr) 597 } 598 599 func nestedAggregationQueryWithGroupByTest(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int) { 600 ti := structs.InitTableInfo("evts", 0, false) 601 value1, _ := CreateDtypeEnclosure("value1", 0) 602 603 filter := FilterCriteria{ 604 ExpressionFilter: &ExpressionFilter{ 605 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key1"}}}, 606 FilterOperator: Equals, 607 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 608 }, 609 } 610 simpleNode := &ASTNode{ 611 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&filter}}, 612 TimeRange: &dtu.TimeRange{ 613 StartEpochMs: 1, 614 EndEpochMs: uint64(numEntriesForBuffer) + 1, 615 }, 616 } 617 618 simpleGroupBy := &QueryAggregators{ 619 PipeCommandType: GroupByType, 620 GroupByRequest: &GroupByRequest{ 621 GroupByColumns: []string{"key11"}, 622 MeasureOperations: []*MeasureAggregator{ 623 {MeasureCol: "key2", MeasureFunc: Max}, 624 {MeasureCol: "key2", MeasureFunc: Min}, 625 {MeasureCol: "key8", MeasureFunc: Sum}, 626 }, 627 AggName: "test", 628 BucketCount: 100, 629 }, 630 } 631 632 // Create a new column using the aggregation columns. 633 firstLetColumns := &QueryAggregators{ 634 PipeCommandType: OutputTransformType, 635 OutputTransforms: &OutputTransforms{ 636 LetColumns: &LetColumnsRequest{ 637 NewColName: "Key2Range", 638 ValueColRequest: &ValueExpr{ 639 ValueExprMode: VEMStringExpr, 640 StringExpr: &StringExpr{ 641 StringExprMode: SEMConcatExpr, 642 ConcatExpr: &ConcatExpr{ 643 Atoms: []*ConcatAtom{ 644 {IsField: true, Value: "min(key2)"}, 645 {IsField: false, Value: " to "}, 646 {IsField: true, Value: "max(key2)"}, 647 }, 648 }, 649 }, 650 }, 651 }, 652 }, 653 } 654 655 mnames := make([]string, 1+len(simpleGroupBy.GroupByRequest.MeasureOperations)) 656 for i, mOp := range simpleGroupBy.GroupByRequest.MeasureOperations { 657 mnames[i] = mOp.String() 658 } 659 mnames[len(simpleGroupBy.GroupByRequest.MeasureOperations)] = "Key2Range" 660 661 // Write over a groupby column. 662 secondLetColumns := &QueryAggregators{ 663 PipeCommandType: OutputTransformType, 664 OutputTransforms: &OutputTransforms{ 665 LetColumns: &LetColumnsRequest{ 666 NewColName: "key11", 667 ValueColRequest: &ValueExpr{ 668 ValueExprMode: VEMStringExpr, 669 StringExpr: &StringExpr{ 670 StringExprMode: SEMConcatExpr, 671 ConcatExpr: &ConcatExpr{ 672 Atoms: []*ConcatAtom{ 673 {IsField: true, Value: "key11"}, 674 {IsField: false, Value: "A"}, 675 }, 676 }, 677 }, 678 }, 679 }, 680 }, 681 } 682 683 simpleGroupBy.Next = firstLetColumns 684 firstLetColumns.Next = secondLetColumns 685 686 sizeLimit := uint64(0) 687 qc := structs.InitQueryContextWithTableInfo(ti, sizeLimit, 0, 0, false) 688 result := ExecuteQuery(simpleNode, simpleGroupBy, 0, qc) 689 690 assert.False(t, result.TotalResults.EarlyExit) 691 692 assert.Len(t, result.MeasureFunctions, 4) 693 assert.True(t, toputils.SliceContainsString(result.MeasureFunctions, "max(key2)")) 694 assert.True(t, toputils.SliceContainsString(result.MeasureFunctions, "min(key2)")) 695 assert.True(t, toputils.SliceContainsString(result.MeasureFunctions, "sum(key8)")) 696 assert.True(t, toputils.SliceContainsString(result.MeasureFunctions, "Key2Range")) 697 698 // Verify MeasureResults 699 assert.Len(t, result.MeasureResults, 2) // We group by key11, which has two values (see WriteMockColSegFile()) 700 for i := 0; i < len(result.MeasureResults); i++ { 701 minStr := fmt.Sprintf("%v", result.MeasureResults[i].MeasureVal["min(key2)"].(int64)) 702 maxStr := fmt.Sprintf("%v", result.MeasureResults[i].MeasureVal["max(key2)"].(int64)) 703 704 assert.Equal(t, result.MeasureResults[i].MeasureVal["Key2Range"], minStr+" to "+maxStr) 705 } 706 707 // Verify Histogram 708 709 lenHist := len(result.Histogram["test"].Results) 710 711 assert.False(t, result.Histogram["test"].IsDateHistogram) 712 assert.Equal(t, lenHist, 2, "only record-batch-1A and record-batch-0A exist") 713 totalentries := numEntriesForBuffer * fileCount * numBuffers 714 for i := 0; i < lenHist; i++ { 715 assert.Equal(t, result.Histogram["test"].Results[i].ElemCount, uint64(totalentries/2)) 716 717 bKey := result.Histogram["test"].Results[i].BucketKey 718 719 assert.Len(t, result.Histogram["test"].Results[i].StatRes, 1+len(simpleGroupBy.GroupByRequest.MeasureOperations)) 720 for _, measureCol := range mnames { 721 res, ok := result.Histogram["test"].Results[i].StatRes[measureCol] 722 assert.True(t, ok) 723 724 if measureCol == "max(key2)" { 725 726 if bKey == "record-batch-0A" { 727 assert.Equal(t, res.CVal, int64(numEntriesForBuffer-2)) 728 } else if bKey == "record-batch-1A" { 729 assert.Equal(t, res.CVal, int64(numEntriesForBuffer-1)) 730 } else { 731 assert.Fail(t, "unexpected bkey %+v", bKey) 732 } 733 } else if measureCol == "min(key2)" { 734 if bKey == "record-batch-0A" { 735 assert.Equal(t, res.CVal, int64(0)) 736 } else if bKey == "record-batch-1A" { 737 assert.Equal(t, res.CVal, int64(1)) 738 } else { 739 assert.Fail(t, "unexpected bkey %+v", bKey) 740 } 741 } else if measureCol == "sum(key8)" { 742 assert.Greater(t, res.CVal, int64(numBuffers*fileCount)) 743 } else if measureCol == "Key2Range" { 744 if bKey == "record-batch-0A" { 745 assert.Equal(t, res.CVal, "0 to "+fmt.Sprintf("%v", int64(numEntriesForBuffer-2))) 746 } else if bKey == "record-batch-1A" { 747 assert.Equal(t, res.CVal, "1 to "+fmt.Sprintf("%v", int64(numEntriesForBuffer-1))) 748 } else { 749 assert.Fail(t, "unexpected bkey %+v", bKey) 750 } 751 } else { 752 assert.Fail(t, "unexpected case %+v", measureCol) 753 } 754 } 755 } 756 } 757 758 func nestedAggsNumericColRequestTest(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int) { 759 ti := structs.InitTableInfo("evts", 0, false) 760 value1, _ := CreateDtypeEnclosure("value1", 0) 761 762 filter := FilterCriteria{ 763 ExpressionFilter: &ExpressionFilter{ 764 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key1"}}}, 765 FilterOperator: Equals, 766 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 767 }, 768 } 769 simpleNode := &ASTNode{ 770 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&filter}}, 771 TimeRange: &dtu.TimeRange{ 772 StartEpochMs: 1, 773 EndEpochMs: uint64(numEntriesForBuffer) + 1, 774 }, 775 } 776 777 simpleMeasure := &QueryAggregators{ 778 PipeCommandType: MeasureAggsType, 779 MeasureOperations: []*MeasureAggregator{ 780 &MeasureAggregator{ 781 MeasureCol: "key2", 782 MeasureFunc: Max, 783 }, 784 }, 785 } 786 787 renameAggs := make(map[string]string) 788 renameAggs["max(key2)"] = "Max" 789 simpleRename := &QueryAggregators{ 790 PipeCommandType: OutputTransformType, 791 OutputTransforms: &OutputTransforms{ 792 OutputColumns: &ColumnsRequest{ 793 RenameAggregationColumns: renameAggs, 794 }, 795 }, 796 } 797 798 simpleMeasure.Next = simpleRename 799 800 // Test creating a new column using the renamed column and numeric calculations. 801 // We'll do 7 - 3 - (Max + 5) * 10 / 2 802 simpleLetColumns := &QueryAggregators{ 803 PipeCommandType: OutputTransformType, 804 OutputTransforms: &OutputTransforms{ 805 LetColumns: &LetColumnsRequest{ 806 NewColName: "Custom", 807 ValueColRequest: &ValueExpr{ 808 ValueExprMode: VEMNumericExpr, 809 810 NumericExpr: &NumericExpr{ 811 IsTerminal: false, 812 Op: "-", 813 Left: &NumericExpr{ 814 IsTerminal: false, 815 Op: "-", 816 Left: &NumericExpr{ 817 IsTerminal: true, 818 Value: "7", 819 ValueIsField: false, 820 }, 821 Right: &NumericExpr{ 822 IsTerminal: true, 823 Value: "3", 824 ValueIsField: false, 825 }, 826 }, 827 Right: &NumericExpr{ 828 IsTerminal: false, 829 Op: "/", 830 Left: &NumericExpr{ 831 IsTerminal: false, 832 Op: "*", 833 Left: &NumericExpr{ 834 IsTerminal: false, 835 Op: "+", 836 Left: &NumericExpr{ 837 IsTerminal: true, 838 Value: "Max", 839 ValueIsField: true, 840 NumericExprMode: NEMNumberField, 841 }, 842 Right: &NumericExpr{ 843 IsTerminal: true, 844 Value: "5", 845 ValueIsField: false, 846 }, 847 }, 848 Right: &NumericExpr{ 849 IsTerminal: true, 850 Value: "10", 851 ValueIsField: false, 852 }, 853 }, 854 Right: &NumericExpr{ 855 IsTerminal: true, 856 Value: "2", 857 ValueIsField: false, 858 }, 859 }, 860 }, 861 }, 862 }, 863 }, 864 } 865 866 simpleRename.Next = simpleLetColumns 867 868 qc := structs.InitQueryContextWithTableInfo(ti, 10000, 0, 0, false) 869 result := ExecuteQuery(simpleNode, simpleMeasure, 0, qc) 870 871 assert.Len(t, result.AllRecords, 0) 872 assert.Zero(t, result.TotalResults.TotalCount) 873 assert.False(t, result.TotalResults.EarlyExit) 874 875 assert.Len(t, result.MeasureFunctions, 2) 876 assert.Equal(t, result.MeasureFunctions[0], "Max") 877 assert.Equal(t, result.MeasureFunctions[1], "Custom") 878 879 assert.Len(t, result.MeasureResults, 1) 880 maxStr := result.MeasureResults[0].MeasureVal["Max"].(string) 881 maxFloat, err := strconv.ParseFloat(maxStr, 64) 882 assert.Nil(t, err) 883 expected := 7 - 3 - (maxFloat+5)*10/2 884 actualStr := result.MeasureResults[0].MeasureVal["Custom"].(string) 885 actualFloat, err := strconv.ParseFloat(actualStr, 64) 886 assert.Nil(t, err) 887 assert.Equal(t, actualFloat, expected) 888 } 889 890 func nestedAggsNumericColRequestWithGroupByTest(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int) { 891 ti := structs.InitTableInfo("evts", 0, false) 892 value1, _ := CreateDtypeEnclosure("value1", 0) 893 894 filter := FilterCriteria{ 895 ExpressionFilter: &ExpressionFilter{ 896 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key1"}}}, 897 FilterOperator: Equals, 898 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 899 }, 900 } 901 simpleNode := &ASTNode{ 902 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&filter}}, 903 TimeRange: &dtu.TimeRange{ 904 StartEpochMs: 1, 905 EndEpochMs: uint64(numEntriesForBuffer) + 1, 906 }, 907 } 908 909 simpleGroupBy := &QueryAggregators{ 910 PipeCommandType: GroupByType, 911 GroupByRequest: &GroupByRequest{ 912 GroupByColumns: []string{"key11"}, 913 MeasureOperations: []*MeasureAggregator{ 914 {MeasureCol: "key2", MeasureFunc: Max}, 915 {MeasureCol: "key2", MeasureFunc: Min}, 916 {MeasureCol: "key8", MeasureFunc: Sum}, 917 }, 918 AggName: "test", 919 BucketCount: 100, 920 }, 921 } 922 923 // Make a new column via numeric calculations. We'll do (Max2 - Min2) 924 numericLetCol := &QueryAggregators{ 925 PipeCommandType: OutputTransformType, 926 OutputTransforms: &OutputTransforms{ 927 LetColumns: &LetColumnsRequest{ 928 NewColName: "Range2", 929 ValueColRequest: &ValueExpr{ 930 ValueExprMode: VEMNumericExpr, 931 NumericExpr: &NumericExpr{ 932 IsTerminal: false, 933 Op: "-", 934 Left: &NumericExpr{ 935 IsTerminal: true, 936 Value: "max(key2)", 937 ValueIsField: true, 938 NumericExprMode: NEMNumberField, 939 }, 940 Right: &NumericExpr{ 941 IsTerminal: true, 942 Value: "min(key2)", 943 ValueIsField: true, 944 NumericExprMode: NEMNumberField, 945 }, 946 }, 947 }, 948 }, 949 }, 950 } 951 952 simpleGroupBy.Next = numericLetCol 953 954 mnames := make([]string, 1+len(simpleGroupBy.GroupByRequest.MeasureOperations)) 955 for i, mOp := range simpleGroupBy.GroupByRequest.MeasureOperations { 956 mnames[i] = mOp.String() 957 } 958 mnames[len(simpleGroupBy.GroupByRequest.MeasureOperations)] = "Range2" 959 960 sizeLimit := uint64(0) 961 qc := structs.InitQueryContextWithTableInfo(ti, sizeLimit, 0, 0, false) 962 result := ExecuteQuery(simpleNode, simpleGroupBy, 0, qc) 963 964 assert.False(t, result.TotalResults.EarlyExit) 965 966 assert.Len(t, result.MeasureFunctions, 4) 967 assert.True(t, toputils.SliceContainsString(result.MeasureFunctions, "max(key2)")) 968 assert.True(t, toputils.SliceContainsString(result.MeasureFunctions, "min(key2)")) 969 assert.True(t, toputils.SliceContainsString(result.MeasureFunctions, "sum(key8)")) 970 assert.True(t, toputils.SliceContainsString(result.MeasureFunctions, "Range2")) 971 972 // Verify MeasureResults 973 assert.Len(t, result.MeasureResults, 2) // We group by key11, which has two values (see WriteMockColSegFile()) 974 for i := 0; i < len(result.MeasureResults); i++ { 975 min := result.MeasureResults[i].MeasureVal["min(key2)"].(int64) 976 max := result.MeasureResults[i].MeasureVal["max(key2)"].(int64) 977 rangeStr := result.MeasureResults[i].MeasureVal["Range2"].(string) 978 rangeFloat, err := strconv.ParseFloat(rangeStr, 64) 979 assert.Nil(t, err) 980 assert.Equal(t, rangeFloat, float64(max-min)) 981 } 982 983 // Verify Histogram 984 lenHist := len(result.Histogram["test"].Results) 985 assert.False(t, result.Histogram["test"].IsDateHistogram) 986 assert.Equal(t, lenHist, 2, "only record-batch-1 and record-batch-0 exist") 987 totalentries := numEntriesForBuffer * fileCount * numBuffers 988 for i := 0; i < lenHist; i++ { 989 assert.Equal(t, result.Histogram["test"].Results[i].ElemCount, uint64(totalentries/2)) 990 bKey := result.Histogram["test"].Results[i].BucketKey 991 assert.Len(t, result.Histogram["test"].Results[i].StatRes, 1+len(simpleGroupBy.GroupByRequest.MeasureOperations)) 992 for _, measureCol := range mnames { 993 res, ok := result.Histogram["test"].Results[i].StatRes[measureCol] 994 assert.True(t, ok) 995 if measureCol == "max(key2)" { 996 if bKey == "record-batch-0" { 997 assert.Equal(t, res.CVal, int64(numEntriesForBuffer-2)) 998 } else if bKey == "record-batch-1" { 999 assert.Equal(t, res.CVal, int64(numEntriesForBuffer-1)) 1000 } else { 1001 assert.Fail(t, "unexpected bkey %+v", bKey) 1002 } 1003 } else if measureCol == "min(key2)" { 1004 if bKey == "record-batch-0" { 1005 assert.Equal(t, res.CVal, int64(0)) 1006 } else if bKey == "record-batch-1" { 1007 assert.Equal(t, res.CVal, int64(1)) 1008 } else { 1009 assert.Fail(t, "unexpected bkey %+v", bKey) 1010 } 1011 } else if measureCol == "sum(key8)" { 1012 assert.Greater(t, res.CVal, int64(numBuffers*fileCount)) 1013 } else if measureCol == "Range2" { 1014 assert.Equal(t, res.CVal, float64(numEntriesForBuffer-2)) 1015 } else { 1016 assert.Fail(t, "unexpected case %+v", measureCol) 1017 } 1018 } 1019 } 1020 } 1021 1022 func nestedAggsFilterRowsWithGroupByTest(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int) { 1023 ti := structs.InitTableInfo("evts", 0, false) 1024 value1, _ := CreateDtypeEnclosure("value1", 0) 1025 1026 filter := FilterCriteria{ 1027 ExpressionFilter: &ExpressionFilter{ 1028 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key1"}}}, 1029 FilterOperator: Equals, 1030 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 1031 }, 1032 } 1033 simpleNode := &ASTNode{ 1034 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&filter}}, 1035 TimeRange: &dtu.TimeRange{ 1036 StartEpochMs: 1, 1037 EndEpochMs: uint64(numEntriesForBuffer) + 1, 1038 }, 1039 } 1040 1041 simpleGroupBy := &QueryAggregators{ 1042 PipeCommandType: GroupByType, 1043 GroupByRequest: &GroupByRequest{ 1044 GroupByColumns: []string{"key11"}, 1045 MeasureOperations: []*MeasureAggregator{ 1046 {MeasureCol: "key2", MeasureFunc: Max}, 1047 {MeasureCol: "key2", MeasureFunc: Min}, 1048 {MeasureCol: "key8", MeasureFunc: Sum}, 1049 }, 1050 AggName: "test", 1051 BucketCount: 100, 1052 }, 1053 } 1054 1055 // Only one row should satisfy this. 1056 whereBlock := &QueryAggregators{ 1057 PipeCommandType: OutputTransformType, 1058 OutputTransforms: &OutputTransforms{ 1059 FilterRows: &BoolExpr{ 1060 IsTerminal: true, 1061 ValueOp: "=", 1062 LeftValue: &ValueExpr{ 1063 ValueExprMode: VEMNumericExpr, 1064 NumericExpr: &NumericExpr{ 1065 IsTerminal: true, 1066 ValueIsField: true, 1067 Value: "key11", 1068 NumericExprMode: NEMNumberField, 1069 }, 1070 }, 1071 RightValue: &ValueExpr{ 1072 ValueExprMode: VEMStringExpr, 1073 StringExpr: &StringExpr{ 1074 RawString: "record-batch-1", 1075 }, 1076 }, 1077 }, 1078 }, 1079 } 1080 1081 simpleGroupBy.Next = whereBlock 1082 1083 sizeLimit := uint64(0) 1084 qc := structs.InitQueryContextWithTableInfo(ti, sizeLimit, 0, 0, false) 1085 result := ExecuteQuery(simpleNode, simpleGroupBy, 0, qc) 1086 assert.Len(t, result.MeasureResults, 1) 1087 assert.True(t, len(result.Histogram) > 0) 1088 for _, aggResult := range result.Histogram { 1089 assert.Len(t, aggResult.Results, 1) 1090 } 1091 1092 whereBlock.OutputTransforms.FilterRows = &BoolExpr{ 1093 IsTerminal: true, 1094 ValueOp: "!=", 1095 LeftValue: &ValueExpr{ 1096 ValueExprMode: VEMNumericExpr, 1097 NumericExpr: &NumericExpr{ 1098 IsTerminal: false, 1099 Op: "-", 1100 Left: &NumericExpr{ 1101 IsTerminal: true, 1102 Value: "max(key2)", 1103 ValueIsField: true, 1104 NumericExprMode: NEMNumberField, 1105 }, 1106 Right: &NumericExpr{ 1107 IsTerminal: true, 1108 Value: "min(key2)", 1109 ValueIsField: true, 1110 NumericExprMode: NEMNumberField, 1111 }, 1112 }, 1113 }, 1114 RightValue: &ValueExpr{ 1115 ValueExprMode: VEMNumericExpr, 1116 NumericExpr: &NumericExpr{ 1117 IsTerminal: true, 1118 ValueIsField: false, 1119 Value: fmt.Sprintf("%v", float64(numEntriesForBuffer-2)), 1120 NumericExprMode: NEMNumber, 1121 }, 1122 }, 1123 } 1124 1125 result = ExecuteQuery(simpleNode, simpleGroupBy, 0, qc) 1126 assert.Len(t, result.MeasureResults, 0) 1127 assert.True(t, len(result.Histogram) > 0) 1128 for _, aggResult := range result.Histogram { 1129 assert.Len(t, aggResult.Results, 0) 1130 } 1131 1132 // Change it so both rows pass. 1133 whereBlock.OutputTransforms.FilterRows.ValueOp = ">=" 1134 1135 result = ExecuteQuery(simpleNode, simpleGroupBy, 0, qc) 1136 assert.Len(t, result.MeasureResults, 2) 1137 assert.True(t, len(result.Histogram) > 0) 1138 for _, aggResult := range result.Histogram { 1139 assert.Len(t, aggResult.Results, 2) 1140 } 1141 1142 // Now group by key4. See WriteMockColSegFile() for how it's mocked. 1143 // key4 will have numEntriesForBuffer values: "0", "2", "4", ..., string(2 * (numEntriesForBuffer - 1)) 1144 simpleGroupBy = &QueryAggregators{ 1145 PipeCommandType: GroupByType, 1146 GroupByRequest: &GroupByRequest{ 1147 GroupByColumns: []string{"key4"}, 1148 MeasureOperations: []*MeasureAggregator{ 1149 {MeasureCol: "key2", MeasureFunc: Max}, 1150 {MeasureCol: "key2", MeasureFunc: Min}, 1151 {MeasureCol: "key8", MeasureFunc: Sum}, 1152 }, 1153 AggName: "test", 1154 BucketCount: 100, 1155 }, 1156 Next: whereBlock, 1157 } 1158 1159 // Test that we can do numeric expressions with key4 even though its values are strings. 1160 whereBlock.OutputTransforms.FilterRows = &BoolExpr{ 1161 IsTerminal: true, 1162 ValueOp: "<", 1163 LeftValue: &ValueExpr{ 1164 ValueExprMode: VEMNumericExpr, 1165 NumericExpr: &NumericExpr{ 1166 IsTerminal: false, 1167 Op: "/", 1168 Left: &NumericExpr{ 1169 IsTerminal: true, 1170 Value: "key4", 1171 ValueIsField: true, 1172 NumericExprMode: NEMNumberField, 1173 }, 1174 Right: &NumericExpr{ 1175 IsTerminal: true, 1176 Value: "2", 1177 ValueIsField: false, 1178 }, 1179 }, 1180 }, 1181 RightValue: &ValueExpr{ 1182 ValueExprMode: VEMNumericExpr, 1183 NumericExpr: &NumericExpr{ 1184 IsTerminal: true, 1185 ValueIsField: false, 1186 Value: "3", 1187 NumericExprMode: NEMNumber, 1188 }, 1189 }, 1190 } 1191 1192 result = ExecuteQuery(simpleNode, simpleGroupBy, 0, qc) 1193 expectedLen := 3 1194 if numEntriesForBuffer < 3 { 1195 expectedLen = numEntriesForBuffer 1196 1197 // If expectedLen is 0, we might pass the below assert.Len() even when 1198 // the query had an error and returned no MeasureResults. 1199 assert.True(t, expectedLen > 0) 1200 } 1201 assert.Len(t, result.MeasureResults, expectedLen) 1202 assert.True(t, len(result.Histogram) > 0) 1203 for _, aggResult := range result.Histogram { 1204 assert.Len(t, aggResult.Results, expectedLen) 1205 } 1206 1207 // Test a non-terminal boolean expression: key4 = "2" OR min(key2) < 1 1208 // This should let two rows pass: key4 = "2" and key4 = "0" (because here min(key2) = 0) 1209 whereBlock.OutputTransforms.FilterRows = &BoolExpr{ 1210 IsTerminal: false, 1211 BoolOp: BoolOpOr, 1212 LeftBool: &BoolExpr{ 1213 IsTerminal: true, 1214 ValueOp: "=", 1215 LeftValue: &ValueExpr{ 1216 ValueExprMode: VEMNumericExpr, 1217 NumericExpr: &NumericExpr{ 1218 IsTerminal: true, 1219 ValueIsField: true, 1220 Value: "key4", 1221 NumericExprMode: NEMNumberField, 1222 }, 1223 }, 1224 RightValue: &ValueExpr{ 1225 ValueExprMode: VEMNumericExpr, 1226 NumericExpr: &NumericExpr{ 1227 IsTerminal: true, 1228 ValueIsField: false, 1229 Value: "2", 1230 NumericExprMode: NEMNumber, 1231 }, 1232 }, 1233 }, 1234 RightBool: &BoolExpr{ 1235 IsTerminal: true, 1236 ValueOp: "<", 1237 LeftValue: &ValueExpr{ 1238 ValueExprMode: VEMNumericExpr, 1239 NumericExpr: &NumericExpr{ 1240 IsTerminal: true, 1241 ValueIsField: true, 1242 Value: "min(key2)", 1243 NumericExprMode: NEMNumberField, 1244 }, 1245 }, 1246 RightValue: &ValueExpr{ 1247 ValueExprMode: VEMNumericExpr, 1248 NumericExpr: &NumericExpr{ 1249 IsTerminal: true, 1250 ValueIsField: false, 1251 Value: "1", 1252 NumericExprMode: NEMNumber, 1253 }, 1254 }, 1255 }, 1256 } 1257 1258 result = ExecuteQuery(simpleNode, simpleGroupBy, 0, qc) 1259 assert.Len(t, result.MeasureResults, 2) 1260 assert.True(t, len(result.Histogram) > 0) 1261 for _, aggResult := range result.Histogram { 1262 assert.Len(t, aggResult.Results, 2) 1263 } 1264 } 1265 1266 func asyncQueryTest(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int) { 1267 value1, _ := CreateDtypeEnclosure("*", 0) 1268 allColumns := FilterCriteria{ 1269 ExpressionFilter: &ExpressionFilter{ 1270 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "*"}}}, 1271 FilterOperator: Equals, 1272 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 1273 }, 1274 } 1275 simpleNode := &ASTNode{ 1276 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&allColumns}}, 1277 TimeRange: &dtu.TimeRange{ 1278 StartEpochMs: 1, 1279 EndEpochMs: uint64(numEntriesForBuffer) + 1, 1280 }, 1281 } 1282 1283 simpleTimeHistogram := &QueryAggregators{ 1284 TimeHistogram: &TimeBucket{ 1285 StartTime: 1, 1286 EndTime: uint64(numEntriesForBuffer) + 1, 1287 IntervalMillis: 1, 1288 AggName: "testTime", 1289 }, 1290 } 1291 ti := structs.InitTableInfo("evts", 0, false) 1292 qid := uint64(10101) 1293 scroll := 0 1294 sizeLimit := uint64(50) 1295 totalPossible := uint64(numBuffers * numEntriesForBuffer * fileCount) 1296 queryContext := structs.InitQueryContextWithTableInfo(ti, sizeLimit, scroll, 0, false) 1297 result, err := ExecuteAsyncQuery(simpleNode, simpleTimeHistogram, qid, queryContext) 1298 assert.Nil(t, err) 1299 assert.NotNil(t, result) 1300 1301 sawRunning := false 1302 sawExit := false 1303 sawQueryUpdate := false 1304 sawRRCComplete := false 1305 var rrcs []*RecordResultContainer 1306 var qc uint64 1307 var buckets map[string]*AggregationResult 1308 1309 for result != nil { 1310 updateType := <-result 1311 switch updateType.StateName { 1312 case query.RUNNING: 1313 sawRunning = true 1314 case query.QUERY_UPDATE: 1315 rrcs, qc, _, err = query.GetRawRecordInfoForQid(scroll, qid) 1316 assert.Nil(t, err) 1317 buckets, _ = query.GetBucketsForQid(qid) 1318 sawQueryUpdate = true 1319 case query.COMPLETE: 1320 sawRRCComplete = true 1321 rrcs, qc, _, err = query.GetRawRecordInfoForQid(scroll, qid) 1322 buckets, _ = query.GetBucketsForQid(qid) 1323 assert.Nil(t, err) 1324 sawExit = true 1325 result = nil 1326 } 1327 } 1328 1329 assert.True(t, sawRunning, "shouldve seen running update") 1330 assert.True(t, sawExit, "shouldve seen exit update") 1331 assert.True(t, sawQueryUpdate, "shouldve seen query update") 1332 assert.True(t, sawRRCComplete, "shouldve seen rrc complete update") 1333 assert.NotNil(t, rrcs, "rrcs should have been populated") 1334 assert.NotNil(t, qc, "query counts should have been populated") 1335 assert.NotNil(t, buckets, "buckets should have been populated") 1336 assert.Len(t, rrcs, int(sizeLimit), "only sizeLimit should be returned") 1337 assert.Equal(t, qc, totalPossible, "should still match all possible") 1338 1339 finalBuckets, finalErr := query.GetBucketsForQid(qid) 1340 assert.Nil(t, finalErr, "err should not be nil as qid as not been deleted") 1341 assert.NotNil(t, finalBuckets, "finalBuckets should not be nil as qid as not been deleted") 1342 1343 query.DeleteQuery(qid) 1344 finalBuckets, finalErr = query.GetBucketsForQid(qid) 1345 assert.Error(t, finalErr, "err should exist as qid should be deleted") 1346 assert.Nil(t, finalBuckets, "finalBuckets should be nil as qid should be deleted") 1347 1348 } 1349 1350 func testESScroll(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int) { 1351 var qid uint64 = 1 1352 value1, _ := CreateDtypeEnclosure("*", qid) 1353 queryRange := &dtu.TimeRange{ 1354 StartEpochMs: 1, 1355 EndEpochMs: uint64(numEntriesForBuffer) + 1, 1356 } 1357 valueFilter := FilterCriteria{ 1358 ExpressionFilter: &ExpressionFilter{ 1359 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "*"}}}, 1360 FilterOperator: Equals, 1361 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 1362 }, 1363 } 1364 simpleNode := &ASTNode{ 1365 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&valueFilter}}, 1366 TimeRange: queryRange, 1367 } 1368 ti := structs.InitTableInfo("evts", 0, false) 1369 sizeLimit := uint64(10000) 1370 qc := structs.InitQueryContextWithTableInfo(ti, sizeLimit, 0, 0, false) 1371 result := ExecuteQuery(simpleNode, &QueryAggregators{}, 0, qc) 1372 t.Logf("Execute Query Results :%v", result) 1373 assert.NotNil(t, result, "Query ran successfully") 1374 assert.Equal(t, len(result.AllRecords), numBuffers*numEntriesForBuffer*fileCount, "all logs in all files should have matched") 1375 assert.Len(t, result.ErrList, 0, "no errors should have occurred") 1376 timeout := time.Now().UTC().Add(time.Minute * 5).Unix() 1377 var scrollSize uint64 = 10 1378 var offset = scrollSize 1379 resulSet := []string{} 1380 scrollRecord := scroll.Scroll{ 1381 Scroll_id: "faba624a-6428-4d78-8c70-571443f0d509", 1382 Results: nil, 1383 Size: scrollSize, 1384 TimeOut: uint64(timeout), 1385 Expiry: "5m", 1386 Offset: 0, 1387 Valid: true, 1388 } 1389 rawResults := esquery.GetQueryResponseJson(result, "evts", time.Now(), sizeLimit, qid, &QueryAggregators{}) 1390 scrollRecord.Results = &rawResults 1391 scroll.SetScrollRecord("faba624a-6428-4d78-8c70-571443f0d509", &scrollRecord) 1392 httpresponse := esquery.GetQueryResponseJsonScroll("evts", time.Now().UTC(), sizeLimit, &scrollRecord, qid) 1393 t.Logf("Scroll Query results %v", httpresponse) 1394 assert.LessOrEqual(t, len(httpresponse.Hits.Hits), int(scrollSize), "scroll returned more records then the scroll size") 1395 assert.Equal(t, int(httpresponse.Hits.GetHits()), numBuffers*numEntriesForBuffer*fileCount, "all logs in all files should have matched") 1396 assert.Equal(t, int(scrollRecord.Offset), int(offset), "offset should have been increased by the scroll size") 1397 assert.Equal(t, checkScrollRecords(httpresponse.Hits.Hits, &resulSet), false, "all records in the scroll should be unique") 1398 iterations := int(httpresponse.Hits.GetHits() / scrollSize) 1399 for i := 1; i < iterations; i++ { 1400 t.Logf("Iteration No : %d for scroll", i) 1401 offset = offset + scrollSize 1402 httpresponse = esquery.GetQueryResponseJsonScroll("evts", time.Now().UTC(), sizeLimit, &scrollRecord, qid) 1403 assert.Equal(t, int(scrollRecord.Offset), int(offset), "offset should have been increased by the scroll size") 1404 assert.Equal(t, checkScrollRecords(httpresponse.Hits.Hits, &resulSet), false, "all records in the scroll should be unique") 1405 } 1406 } 1407 1408 func testPipesearchScroll(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int) { 1409 var qid uint64 = 1 1410 value1, _ := CreateDtypeEnclosure("*", qid) 1411 queryRange := &dtu.TimeRange{ 1412 StartEpochMs: 1, 1413 EndEpochMs: uint64(numEntriesForBuffer) + 1, 1414 } 1415 valueFilter := FilterCriteria{ 1416 ExpressionFilter: &ExpressionFilter{ 1417 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "*"}}}, 1418 FilterOperator: Equals, 1419 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 1420 }, 1421 } 1422 simpleNode := &ASTNode{ 1423 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&valueFilter}}, 1424 TimeRange: queryRange, 1425 } 1426 qc := structs.InitQueryContext("evts", uint64(10), 9, 0, false) 1427 result := ExecuteQuery(simpleNode, &QueryAggregators{}, 0, qc) 1428 assert.Len(t, result.AllRecords, 1) 1429 1430 qc.Scroll = 10 1431 result = ExecuteQuery(simpleNode, &QueryAggregators{}, 0, qc) 1432 assert.Len(t, result.AllRecords, 0) 1433 1434 maxPossible := uint64(numBuffers * numEntriesForBuffer * fileCount) 1435 qc.SizeLimit = maxPossible 1436 qc.Scroll = int(maxPossible) 1437 result = ExecuteQuery(simpleNode, &QueryAggregators{}, 0, qc) 1438 assert.Len(t, result.AllRecords, 0) 1439 1440 qc.Scroll = int(maxPossible - 5) 1441 result = ExecuteQuery(simpleNode, &QueryAggregators{}, 0, qc) 1442 assert.Len(t, result.AllRecords, 5) 1443 1444 } 1445 1446 func checkScrollRecords(response []toputils.Hits, resultSet *[]string) bool { 1447 log.Printf("Length of records fetched %d", len(response)) 1448 for _, hit := range response { 1449 if contains(*resultSet, fmt.Sprintf("%v", hit.Source["key5"])) { 1450 return false 1451 } 1452 *resultSet = append(*resultSet, fmt.Sprintf("%v", hit.Source["key5"])) 1453 } 1454 return false 1455 } 1456 1457 func contains(slice []string, element string) bool { 1458 for _, value := range slice { 1459 if value == element { 1460 return true 1461 } 1462 } 1463 return false 1464 } 1465 1466 func getMyIds() []uint64 { 1467 myids := make([]uint64, 1) 1468 myids[0] = 0 1469 return myids 1470 } 1471 1472 func Test_Query(t *testing.T) { 1473 config.InitializeDefaultConfig() 1474 _ = localstorage.InitLocalStorage() 1475 limit.InitMemoryLimiter() 1476 instrumentation.InitMetrics() 1477 1478 err := query.InitQueryNode(getMyIds, serverutils.ExtractKibanaRequests) 1479 if err != nil { 1480 log.Fatalf("Failed to initialize query node: %v", err) 1481 } 1482 numBuffers := 5 1483 numEntriesForBuffer := 10 1484 fileCount := 2 1485 metadata.InitMockColumnarMetadataStore("data/", fileCount, numBuffers, numEntriesForBuffer) 1486 1487 simpleQueryTest(t, numBuffers, numEntriesForBuffer, fileCount) 1488 wildcardQueryTest(t, numBuffers, numEntriesForBuffer, fileCount) 1489 timeHistogramQueryTest(t, numBuffers, numEntriesForBuffer, fileCount) 1490 groupByQueryTest(t, numBuffers, numEntriesForBuffer, fileCount) 1491 timechartGroupByQueryTest(t, numBuffers, numEntriesForBuffer, fileCount) 1492 nestedQueryTest(t, numBuffers, numEntriesForBuffer, fileCount) 1493 nestedAggregationQueryTest(t, numBuffers, numEntriesForBuffer, fileCount) 1494 nestedAggregationQueryWithGroupByTest(t, numBuffers, numEntriesForBuffer, fileCount) 1495 nestedAggsNumericColRequestTest(t, numBuffers, numEntriesForBuffer, fileCount) 1496 nestedAggsNumericColRequestWithGroupByTest(t, numBuffers, numEntriesForBuffer, fileCount) 1497 nestedAggsFilterRowsWithGroupByTest(t, numBuffers, numEntriesForBuffer, fileCount) 1498 asyncQueryTest(t, numBuffers, numEntriesForBuffer, fileCount) 1499 1500 groupByQueryTestsForAsteriskQueries(t, numBuffers, numEntriesForBuffer, fileCount) 1501 1502 os.RemoveAll("data/") 1503 } 1504 1505 func Test_Scroll(t *testing.T) { 1506 config.InitializeDefaultConfig() 1507 limit.InitMemoryLimiter() 1508 _ = localstorage.InitLocalStorage() 1509 1510 err := query.InitQueryNode(getMyIds, serverutils.ExtractKibanaRequests) 1511 if err != nil { 1512 log.Fatalf("Failed to initialize query node: %v", err) 1513 } 1514 numBuffers := 5 1515 numEntriesForBuffer := 10 1516 fileCount := 2 1517 metadata.InitMockColumnarMetadataStore("data/", fileCount, numBuffers, numEntriesForBuffer) 1518 testESScroll(t, numBuffers, numEntriesForBuffer, fileCount) 1519 testPipesearchScroll(t, numBuffers, numEntriesForBuffer, fileCount) 1520 os.RemoveAll("data/") 1521 } 1522 1523 func Test_unrotatedQuery(t *testing.T) { 1524 config.InitializeTestingConfig() 1525 config.SetDataPath("unrotatedtest/") 1526 limit.InitMemoryLimiter() 1527 err := query.InitQueryNode(getMyIds, serverutils.ExtractKibanaRequests) 1528 assert.Nil(t, err) 1529 writer.InitWriterNode() 1530 _ = localstorage.InitLocalStorage() 1531 numBatch := 10 1532 numRec := 100 1533 1534 // disable dict encoding globally 1535 writer.SetCardinalityLimit(0) 1536 1537 for batch := 0; batch < numBatch; batch++ { 1538 for rec := 0; rec < numRec; rec++ { 1539 record := make(map[string]interface{}) 1540 record["col1"] = "abc" 1541 record["col2"] = strconv.Itoa(rec) 1542 record["col3"] = "batch-" + strconv.Itoa(batch) 1543 record["col4"] = uuid.New().String() 1544 if rec >= numRec/2 { 1545 // add new column after it has reached halfway into filling a block 1546 // so that past records can we backfilled 1547 record["col5"] = "def" 1548 } 1549 record["timestamp"] = uint64(rec) 1550 rawJson, err := json.Marshal(record) 1551 assert.Nil(t, err) 1552 err = writer.AddEntryToInMemBuf("test1", rawJson, uint64(rec)+1, "test", 10, false, 1553 SIGNAL_EVENTS, 0) 1554 assert.Nil(t, err) 1555 } 1556 1557 sleep := time.Duration(1) 1558 time.Sleep(sleep) 1559 writer.FlushWipBufferToFile(&sleep) 1560 } 1561 sleep := time.Duration(1) 1562 time.Sleep(sleep) 1563 writer.FlushWipBufferToFile(&sleep) 1564 aggs := &QueryAggregators{ 1565 EarlyExit: false, 1566 } 1567 1568 // col3=batch-1 1569 value1, _ := CreateDtypeEnclosure("batch-1", 0) 1570 valueFilter := FilterCriteria{ 1571 ExpressionFilter: &ExpressionFilter{ 1572 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "col3"}}}, 1573 FilterOperator: Equals, 1574 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 1575 }, 1576 } 1577 simpleNode := &ASTNode{ 1578 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&valueFilter}}, 1579 TimeRange: &dtu.TimeRange{ 1580 StartEpochMs: 0, 1581 EndEpochMs: uint64(numRec) + 1, 1582 }, 1583 } 1584 sizeLimit := uint64(10000) 1585 scroll := 0 1586 qc := structs.InitQueryContext("test", sizeLimit, scroll, 0, false) 1587 result := ExecuteQuery(simpleNode, aggs, uint64(numBatch*numRec*2), qc) 1588 assert.Equal(t, uint64(numRec), result.TotalResults.TotalCount) 1589 assert.Equal(t, Equals, result.TotalResults.Op) 1590 1591 // *=batch-1 1592 valueFilter = FilterCriteria{ 1593 ExpressionFilter: &ExpressionFilter{ 1594 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "*"}}}, 1595 FilterOperator: Equals, 1596 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 1597 }, 1598 } 1599 simpleNode = &ASTNode{ 1600 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&valueFilter}}, 1601 TimeRange: &dtu.TimeRange{ 1602 StartEpochMs: 0, 1603 EndEpochMs: uint64(numRec) + 1, 1604 }, 1605 } 1606 result = ExecuteQuery(simpleNode, aggs, uint64(numBatch*numRec*2), qc) 1607 query.DeleteQuery(uint64(numBatch * numRec * 2)) 1608 assert.Equal(t, uint64(numRec), result.TotalResults.TotalCount) 1609 assert.Equal(t, Equals, result.TotalResults.Op) 1610 1611 // *=def 1612 def, _ := CreateDtypeEnclosure("def", 0) 1613 valueFilter = FilterCriteria{ 1614 ExpressionFilter: &ExpressionFilter{ 1615 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "*"}}}, 1616 FilterOperator: Equals, 1617 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: def}}}, 1618 }, 1619 } 1620 simpleNode = &ASTNode{ 1621 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&valueFilter}}, 1622 TimeRange: &dtu.TimeRange{ 1623 StartEpochMs: 0, 1624 EndEpochMs: uint64(numRec) + 1, 1625 }, 1626 } 1627 result = ExecuteQuery(simpleNode, aggs, uint64(numBatch*numRec*2), qc) 1628 backfillExpectecd := uint64(numRec*numBatch) / 2 // since we added new column halfway through the block 1629 assert.Equal(t, backfillExpectecd, result.TotalResults.TotalCount, 1630 "backfillExpectecd: %v, actual: %v", backfillExpectecd, result.TotalResults.TotalCount) 1631 assert.Equal(t, Equals, result.TotalResults.Op) 1632 1633 // col5=def 1634 valueFilter = FilterCriteria{ 1635 ExpressionFilter: &ExpressionFilter{ 1636 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "col5"}}}, 1637 FilterOperator: Equals, 1638 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: def}}}, 1639 }, 1640 } 1641 simpleNode = &ASTNode{ 1642 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&valueFilter}}, 1643 TimeRange: &dtu.TimeRange{ 1644 StartEpochMs: 0, 1645 EndEpochMs: uint64(numRec) + 1, 1646 }, 1647 } 1648 result = ExecuteQuery(simpleNode, aggs, uint64(numBatch*numRec*2), qc) 1649 assert.Equal(t, backfillExpectecd, result.TotalResults.TotalCount) 1650 assert.Equal(t, Equals, result.TotalResults.Op) 1651 os.RemoveAll(config.GetDataPath()) 1652 } 1653 1654 func Test_EncodeDecodeBlockSummary(t *testing.T) { 1655 1656 batchSize := 10 1657 entryCount := 10 1658 dir := "data/" 1659 err := os.MkdirAll(dir, os.FileMode(0755)) 1660 _ = localstorage.InitLocalStorage() 1661 1662 if err != nil { 1663 log.Fatal(err) 1664 } 1665 currFile := dir + "query_test.seg" 1666 _, blockSummaries, _, _, allBmhInMem, _ := writer.WriteMockColSegFile(currFile, batchSize, entryCount) 1667 blockSumFile := dir + "query_test.bsu" 1668 1669 writer.WriteMockBlockSummary(blockSumFile, blockSummaries, allBmhInMem) 1670 blockSums, readAllBmh, _, err := microreader.ReadBlockSummaries(blockSumFile, []byte{}) 1671 if err != nil { 1672 os.RemoveAll(dir) 1673 log.Fatal(err) 1674 } 1675 1676 for i := 0; i < len(blockSums); i++ { 1677 assert.Equal(t, blockSums[i].HighTs, blockSummaries[i].HighTs) 1678 assert.Equal(t, blockSums[i].LowTs, blockSummaries[i].LowTs) 1679 assert.Equal(t, blockSums[i].RecCount, blockSummaries[i].RecCount) 1680 1681 // cnames are create in WriteMockColSegFile, we will only verify one of cnames 1682 // cnames start from key0..key11 1683 // key1 stores "value1", and the blockLen was calculated by running thw writemock.. func with print statement 1684 assert.Equal(t, uint32(30), readAllBmh[uint16(i)].ColumnBlockLen["key1"]) 1685 assert.Equal(t, int64(i*30), readAllBmh[uint16(i)].ColumnBlockOffset["key1"]) 1686 } 1687 os.RemoveAll(dir) 1688 } 1689 1690 func Benchmark_agileTreeQueryReader(t *testing.B) { 1691 // go test -run=Bench -bench=Benchmark_agileTreeQueryReader -benchmem -memprofile memprofile.out -o rawsearch_mem 1692 // go test -run=Bench -bench=Benchmark_agileTreeQueryReader -cpuprofile cpuprofile.out -o rawsearch_cpu 1693 1694 segKeyPref := "/Users/kunalnawale/work/perf/siglens/data/Kunals-MacBook-Pro.local/final/ind-0/0-3544697602014606120/" 1695 1696 grpByCols := []string{"passenger_count", "pickup_date", "trip_distance"} 1697 measureOps := []*structs.MeasureAggregator{ 1698 {MeasureCol: "total_amount", MeasureFunc: utils.Count}, 1699 } 1700 grpByRequest := &GroupByRequest{MeasureOperations: measureOps, GroupByColumns: grpByCols} 1701 1702 aggs := &QueryAggregators{ 1703 GroupByRequest: grpByRequest, 1704 } 1705 1706 agileTreeBuf := make([]byte, 300_000_000) 1707 qid := uint64(67) 1708 qType := structs.QueryType(structs.RRCCmd) 1709 1710 allSearchResults, err1 := segresults.InitSearchResults(0, aggs, qType, qid) 1711 assert.NoError(t, err1) 1712 1713 numSegs := 114 1714 for skNum := 0; skNum < numSegs; skNum++ { 1715 sTime := time.Now() 1716 1717 segKey := fmt.Sprintf("%v/%v/%v", segKeyPref, skNum, skNum) 1718 blkResults, err := blockresults.InitBlockResults(0, aggs, qid) 1719 assert.NoError(t, err) 1720 1721 str, err := segread.InitNewAgileTreeReader(segKey, qid) 1722 assert.NoError(t, err) 1723 1724 err1 := str.ApplyGroupByJit(grpByRequest.GroupByColumns, measureOps, blkResults, qid, agileTreeBuf) 1725 assert.NoError(t, err1) 1726 1727 //log.Infof("Aggs seg: %v query, time: %+v", skNum, time.Since(sTime)) 1728 1729 res := blkResults.GetGroupByBuckets() 1730 assert.NotNil(t, res) 1731 assert.NotEqual(t, 0, res.Results) 1732 1733 allSearchResults.AddBlockResults(blkResults) 1734 1735 srRes := allSearchResults.BlockResults.GetGroupByBuckets() 1736 assert.NotNil(t, srRes) 1737 assert.NotEqual(t, 0, srRes.Results) 1738 1739 log.Infof("Aggs query, segNum: %v, Num of bkt key: %v, time: %v", skNum, 1740 len(srRes.Results), time.Since(sTime)) 1741 1742 _ = allSearchResults.GetBucketResults() 1743 } 1744 1745 res := allSearchResults.BlockResults.GetGroupByBuckets() 1746 log.Infof("Aggs query, Num of bkt key: %v", len(res.Results)) 1747 1748 } 1749 1750 func measureColsTest(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int, measureCol string, measureFunc AggregateFunctions) { 1751 value1, _ := CreateDtypeEnclosure("value1", 0) 1752 // wildcard all columns 1753 ti := structs.InitTableInfo("evts", 0, false) 1754 allColumns := FilterCriteria{ 1755 ExpressionFilter: &ExpressionFilter{ 1756 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "*"}}}, 1757 FilterOperator: Equals, 1758 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 1759 }, 1760 } 1761 simpleNode := &ASTNode{ 1762 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&allColumns}}, 1763 TimeRange: &dtu.TimeRange{ 1764 StartEpochMs: 0, 1765 EndEpochMs: uint64(numEntriesForBuffer), 1766 }, 1767 } 1768 qc := structs.InitQueryContextWithTableInfo(ti, 10000, 0, 0, false) 1769 result := ExecuteQuery(simpleNode, &QueryAggregators{ 1770 MeasureOperations: []*MeasureAggregator{ 1771 {MeasureCol: measureCol, MeasureFunc: measureFunc}, 1772 }, 1773 }, 0, qc) 1774 1775 if measureCol == "*" && measureFunc != Count { 1776 assert.Len(t, result.AllRecords, 0) 1777 assert.Zero(t, result.TotalResults.TotalCount) 1778 assert.False(t, result.TotalResults.EarlyExit) 1779 } 1780 } 1781 1782 func groupByAggQueryTest(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int, measureCol string, measureFunc AggregateFunctions) *NodeResult { 1783 value1, _ := CreateDtypeEnclosure("value1", 0) 1784 ti := structs.InitTableInfo("evts", 0, false) 1785 allColumns := FilterCriteria{ 1786 ExpressionFilter: &ExpressionFilter{ 1787 LeftInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnName: "key1"}}}, 1788 FilterOperator: Equals, 1789 RightInput: &FilterInput{Expression: &Expression{LeftInput: &ExpressionInput{ColumnValue: value1}}}, 1790 }, 1791 } 1792 simpleNode := &ASTNode{ 1793 AndFilterCondition: &Condition{FilterCriteria: []*FilterCriteria{&allColumns}}, 1794 TimeRange: &dtu.TimeRange{ 1795 StartEpochMs: 1, 1796 EndEpochMs: uint64(numEntriesForBuffer) + 1, 1797 }, 1798 } 1799 1800 simpleGroupBy := &QueryAggregators{ 1801 GroupByRequest: &GroupByRequest{ 1802 GroupByColumns: []string{"key11"}, 1803 MeasureOperations: []*MeasureAggregator{ 1804 {MeasureCol: measureCol, MeasureFunc: measureFunc}, 1805 }, 1806 AggName: "test", 1807 BucketCount: 100, 1808 }, 1809 } 1810 1811 qc := structs.InitQueryContextWithTableInfo(ti, 10000, 0, 0, false) 1812 result := ExecuteQuery(simpleNode, simpleGroupBy, 102, qc) 1813 lenHist := len(result.Histogram["test"].Results) 1814 assert.False(t, result.Histogram["test"].IsDateHistogram) 1815 if measureFunc == Count { 1816 assert.Equal(t, lenHist, 2, "only record-batch-1 and record-batch-0 exist") 1817 totalentries := numEntriesForBuffer * fileCount * numBuffers 1818 for i := 0; i < lenHist; i++ { 1819 assert.Equal(t, result.Histogram["test"].Results[i].ElemCount, uint64(totalentries/2)) 1820 bKey := result.Histogram["test"].Results[i].BucketKey 1821 assert.Len(t, result.Histogram["test"].Results[i].StatRes, len(simpleGroupBy.GroupByRequest.MeasureOperations)) 1822 log.Infof("bkey is %+v", bKey) 1823 res, ok := result.Histogram["test"].Results[i].StatRes[fmt.Sprintf("count(%v)", measureCol)] 1824 assert.True(t, ok) 1825 assert.Equal(t, res.CVal, uint64(50)) 1826 } 1827 } else if measureCol == "*" && measureFunc != Count { 1828 assert.NotZero(t, len(result.ErrList)) 1829 assert.Len(t, result.AllRecords, 0) 1830 assert.Zero(t, result.TotalResults.TotalCount) 1831 assert.False(t, result.TotalResults.EarlyExit) 1832 } 1833 1834 return result 1835 } 1836 1837 func groupByQueryTestsForAsteriskQueries(t *testing.T, numBuffers int, numEntriesForBuffer int, fileCount int) { 1838 asteriskResult := groupByAggQueryTest(t, numBuffers, numEntriesForBuffer, fileCount, "*", Count) 1839 columnarResult := groupByAggQueryTest(t, numBuffers, numEntriesForBuffer, fileCount, "key11", Count) 1840 1841 assert.Equal(t, asteriskResult.TotalRRCCount, columnarResult.TotalRRCCount) 1842 assert.Equal(t, asteriskResult.TotalResults.TotalCount, columnarResult.TotalResults.TotalCount) 1843 1844 for recIdx, rec := range asteriskResult.AllRecords { 1845 assert.Equal(t, rec.RecordNum, columnarResult.AllRecords[recIdx].RecordNum) 1846 assert.Equal(t, rec.SortColumnValue, columnarResult.AllRecords[recIdx].SortColumnValue) 1847 assert.Equal(t, rec.VirtualTableName, columnarResult.AllRecords[recIdx].VirtualTableName) 1848 } 1849 1850 groupByAggQueryTest(t, numBuffers, numEntriesForBuffer, fileCount, "*", Avg) 1851 measureColsTest(t, numBuffers, numEntriesForBuffer, fileCount, "*", Avg) 1852 }