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  }