github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/ast/spl/tests/splParser_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 tests
    18  
    19  import (
    20  	"encoding/json"
    21  	"io"
    22  	"os"
    23  	"regexp"
    24  	"testing"
    25  
    26  	"github.com/siglens/siglens/pkg/ast"
    27  	"github.com/siglens/siglens/pkg/ast/pipesearch"
    28  	"github.com/siglens/siglens/pkg/ast/spl"
    29  	segquery "github.com/siglens/siglens/pkg/segment/query"
    30  	"github.com/siglens/siglens/pkg/segment/structs"
    31  	"github.com/siglens/siglens/pkg/segment/utils"
    32  	log "github.com/sirupsen/logrus"
    33  	"github.com/stretchr/testify/assert"
    34  )
    35  
    36  // Helper functions
    37  
    38  func extractMatchFilter(t *testing.T, node *ast.Node) *structs.MatchFilter {
    39  	astNode := &structs.ASTNode{}
    40  	err := pipesearch.SearchQueryToASTnode(node, astNode, 0)
    41  	assert.Nil(t, err)
    42  	assert.NotNil(t, astNode.AndFilterCondition)
    43  
    44  	criteria := astNode.AndFilterCondition.FilterCriteria
    45  	assert.NotNil(t, criteria)
    46  	assert.Equal(t, 1, len(criteria))
    47  	assert.Nil(t, criteria[0].ExpressionFilter)
    48  
    49  	matchFilter := criteria[0].MatchFilter
    50  	assert.NotNil(t, matchFilter)
    51  
    52  	return matchFilter
    53  }
    54  
    55  func extractExpressionFilter(t *testing.T, node *ast.Node) *structs.ExpressionFilter {
    56  	astNode := &structs.ASTNode{}
    57  	err := pipesearch.SearchQueryToASTnode(node, astNode, 0)
    58  	assert.Nil(t, err)
    59  	assert.NotNil(t, astNode.AndFilterCondition)
    60  
    61  	criteria := astNode.AndFilterCondition.FilterCriteria
    62  	assert.NotNil(t, criteria)
    63  	assert.Equal(t, 1, len(criteria))
    64  	assert.Nil(t, criteria[0].MatchFilter)
    65  
    66  	expressionFilter := criteria[0].ExpressionFilter
    67  	assert.NotNil(t, expressionFilter)
    68  
    69  	return expressionFilter
    70  }
    71  
    72  // Initial setup.
    73  func TestMain(m *testing.M) {
    74  	// Suppress log output.
    75  	log.SetOutput(io.Discard)
    76  
    77  	// Run the tests.
    78  	os.Exit(m.Run())
    79  }
    80  
    81  // Tests
    82  
    83  func Test_searchQuotedStringNoBreakers(t *testing.T) {
    84  	query := []byte(`search "abc"`)
    85  	res, err := spl.Parse("", query)
    86  	assert.Nil(t, err)
    87  	filterNode := res.(ast.QueryStruct).SearchFilter
    88  
    89  	assert.NotNil(t, filterNode)
    90  	assert.Equal(t, filterNode.Comparison.Op, "=")
    91  	assert.Equal(t, filterNode.Comparison.Field, "*")
    92  	assert.Equal(t, filterNode.Comparison.Values, `"abc"`)
    93  
    94  	matchFilter := extractMatchFilter(t, filterNode)
    95  	assert.Equal(t, structs.MATCH_PHRASE, matchFilter.MatchType)
    96  
    97  	// Note: the double quotes got stripped off in ast.ProcessSingleFilter(), so
    98  	// it's just abc and not "abc"
    99  	assert.Equal(t, []byte(`abc`), matchFilter.MatchPhrase)
   100  }
   101  
   102  func Test_searchQuotedStringMinorBreakers(t *testing.T) {
   103  	query := []byte(`search "abc./\\:=@#$%-_DEF"`)
   104  	res, err := spl.Parse("", query)
   105  	assert.Nil(t, err)
   106  	filterNode := res.(ast.QueryStruct).SearchFilter
   107  
   108  	assert.NotNil(t, filterNode)
   109  	assert.Equal(t, filterNode.Comparison.Op, "=")
   110  	assert.Equal(t, filterNode.Comparison.Field, "*")
   111  	assert.Equal(t, filterNode.Comparison.Values, `"abc./\\:=@#$%-_DEF"`)
   112  
   113  	matchFilter := extractMatchFilter(t, filterNode)
   114  	assert.Equal(t, structs.MATCH_PHRASE, matchFilter.MatchType)
   115  	assert.Equal(t, []byte(`abc./\\:=@#$%-_DEF`), matchFilter.MatchPhrase)
   116  }
   117  
   118  func Test_searchQuotedStringMajorBreakers(t *testing.T) {
   119  	query := []byte(`search "abc DEF < > [ ] ( ) { } ! ? ; , ' &"`)
   120  	res, err := spl.Parse("", query)
   121  	assert.Nil(t, err)
   122  	filterNode := res.(ast.QueryStruct).SearchFilter
   123  
   124  	assert.NotNil(t, filterNode)
   125  	assert.Equal(t, filterNode.Comparison.Op, "=")
   126  	assert.Equal(t, filterNode.Comparison.Field, "*")
   127  	assert.Equal(t, filterNode.Comparison.Values, `"abc DEF < > [ ] ( ) { } ! ? ; , ' &"`)
   128  
   129  	matchFilter := extractMatchFilter(t, filterNode)
   130  	assert.Equal(t, structs.MATCH_PHRASE, matchFilter.MatchType)
   131  	assert.Equal(t, []byte(`abc DEF < > [ ] ( ) { } ! ? ; , ' &`), matchFilter.MatchPhrase)
   132  }
   133  
   134  func Test_impliedSearchCommand(t *testing.T) {
   135  	query := []byte(`"apple"`)
   136  	res, err := spl.Parse("", query)
   137  	assert.Nil(t, err)
   138  	filterNode := res.(ast.QueryStruct).SearchFilter
   139  
   140  	assert.NotNil(t, filterNode)
   141  	assert.Equal(t, filterNode.Comparison.Op, "=")
   142  	assert.Equal(t, filterNode.Comparison.Field, "*")
   143  	assert.Equal(t, filterNode.Comparison.Values, `"apple"`)
   144  
   145  	matchFilter := extractMatchFilter(t, filterNode)
   146  	assert.Equal(t, structs.MATCH_PHRASE, matchFilter.MatchType)
   147  	assert.Equal(t, []byte(`apple`), matchFilter.MatchPhrase)
   148  }
   149  
   150  func Test_searchUnquotedStringNoBreakers(t *testing.T) {
   151  	query := []byte(`search abc`)
   152  	res, err := spl.Parse("", query)
   153  	assert.Nil(t, err)
   154  	filterNode := res.(ast.QueryStruct).SearchFilter
   155  
   156  	assert.NotNil(t, filterNode)
   157  	assert.Equal(t, filterNode.Comparison.Op, "=")
   158  	assert.Equal(t, filterNode.Comparison.Field, "*")
   159  	assert.Equal(t, filterNode.Comparison.Values, `"abc"`)
   160  
   161  	matchFilter := extractMatchFilter(t, filterNode)
   162  	assert.Equal(t, structs.MATCH_PHRASE, matchFilter.MatchType)
   163  	assert.Equal(t, []byte(`abc`), matchFilter.MatchPhrase)
   164  }
   165  
   166  func Test_searchUnquotedStringMinorBreakers(t *testing.T) {
   167  	query := []byte(`search "abc./\\:=@#$%-_DEF"`)
   168  	res, err := spl.Parse("", query)
   169  	assert.Nil(t, err)
   170  	filterNode := res.(ast.QueryStruct).SearchFilter
   171  
   172  	assert.NotNil(t, filterNode)
   173  	assert.Equal(t, filterNode.Comparison.Op, "=")
   174  	assert.Equal(t, filterNode.Comparison.Field, "*")
   175  	assert.Equal(t, filterNode.Comparison.Values, `"abc./\\:=@#$%-_DEF"`)
   176  
   177  	matchFilter := extractMatchFilter(t, filterNode)
   178  	assert.Equal(t, structs.MATCH_PHRASE, matchFilter.MatchType)
   179  	assert.Equal(t, []byte(`abc./\\:=@#$%-_DEF`), matchFilter.MatchPhrase)
   180  }
   181  
   182  func Test_searchUnquotedStringMajorBreakerAtStart(t *testing.T) {
   183  	query := []byte(`search &abcDEF`)
   184  	res, err := spl.Parse("", query)
   185  	assert.Nil(t, res)
   186  	assert.NotNil(t, err)
   187  }
   188  
   189  func Test_searchUnquotedStringMajorBreakerInMiddle(t *testing.T) {
   190  	query := []byte(`search abc(DEF`)
   191  	res, err := spl.Parse("", query)
   192  	assert.Nil(t, res)
   193  	assert.NotNil(t, err)
   194  }
   195  
   196  func Test_searchUnquotedStringMajorBreakerAtEnd(t *testing.T) {
   197  	query := []byte(`search &abcDEF%26`)
   198  	res, err := spl.Parse("", query)
   199  	assert.Nil(t, res)
   200  	assert.NotNil(t, err)
   201  }
   202  
   203  func Test_searchFieldEqualToQuotedString(t *testing.T) {
   204  	query := []byte(`search status="ok"`)
   205  	res, err := spl.Parse("", query)
   206  	assert.Nil(t, err)
   207  	filterNode := res.(ast.QueryStruct).SearchFilter
   208  
   209  	assert.NotNil(t, filterNode)
   210  	assert.Equal(t, "=", filterNode.Comparison.Op)
   211  	assert.Equal(t, "status", filterNode.Comparison.Field)
   212  	assert.Equal(t, `"ok"`, filterNode.Comparison.Values)
   213  
   214  	expressionFilter := extractExpressionFilter(t, filterNode)
   215  	assert.Equal(t, "status", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   216  	assert.Equal(t, utils.Equals, expressionFilter.FilterOperator)
   217  	assert.Equal(t, "ok", expressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal)
   218  }
   219  
   220  func Test_searchFieldNotEqualToQuotedString(t *testing.T) {
   221  	query := []byte(`search status!="ok"`)
   222  	res, err := spl.Parse("", query)
   223  	assert.Nil(t, err)
   224  	filterNode := res.(ast.QueryStruct).SearchFilter
   225  
   226  	assert.NotNil(t, filterNode)
   227  	assert.Equal(t, "!=", filterNode.Comparison.Op)
   228  	assert.Equal(t, "status", filterNode.Comparison.Field)
   229  	assert.Equal(t, `"ok"`, filterNode.Comparison.Values)
   230  
   231  	expressionFilter := extractExpressionFilter(t, filterNode)
   232  	assert.Equal(t, "status", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   233  	assert.Equal(t, utils.NotEquals, expressionFilter.FilterOperator)
   234  	assert.Equal(t, "ok", expressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal)
   235  }
   236  
   237  func Test_searchFieldLessThanQuotedString(t *testing.T) {
   238  	query := []byte(`search status<"ok"`)
   239  	res, err := spl.Parse("", query)
   240  	assert.Nil(t, res)
   241  	assert.NotNil(t, err)
   242  }
   243  
   244  func Test_searchFieldLessThanOrEqualToQuotedString(t *testing.T) {
   245  	query := []byte(`search status<="ok"`)
   246  	res, err := spl.Parse("", query)
   247  	assert.Nil(t, res)
   248  	assert.NotNil(t, err)
   249  }
   250  
   251  func Test_searchFieldGreaterThanThanQuotedString(t *testing.T) {
   252  	query := []byte(`search status>"ok"`)
   253  	res, err := spl.Parse("", query)
   254  	assert.Nil(t, res)
   255  	assert.NotNil(t, err)
   256  }
   257  
   258  func Test_searchFieldGreaterThanOrEqualToQuotedString(t *testing.T) {
   259  	query := []byte(`search status>="ok"`)
   260  	res, err := spl.Parse("", query)
   261  	assert.Nil(t, res)
   262  	assert.NotNil(t, err)
   263  }
   264  
   265  func Test_searchFieldEqualToBooleanTrue(t *testing.T) {
   266  	query := []byte(`search status=true`)
   267  	res, err := spl.Parse("", query)
   268  	assert.Nil(t, err)
   269  	filterNode := res.(ast.QueryStruct).SearchFilter
   270  
   271  	assert.NotNil(t, filterNode)
   272  	assert.Equal(t, "=", filterNode.Comparison.Op)
   273  	assert.Equal(t, "status", filterNode.Comparison.Field)
   274  	assert.Equal(t, true, filterNode.Comparison.Values)
   275  
   276  	expressionFilter := extractExpressionFilter(t, filterNode)
   277  	assert.Equal(t, "status", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   278  	assert.Equal(t, utils.Equals, expressionFilter.FilterOperator)
   279  	assert.Equal(t, uint8(1), expressionFilter.RightInput.Expression.LeftInput.ColumnValue.BoolVal)
   280  }
   281  
   282  func Test_searchFieldEqualToBooleanFalse(t *testing.T) {
   283  	query := []byte(`search status=false`)
   284  	res, err := spl.Parse("", query)
   285  	assert.Nil(t, err)
   286  	filterNode := res.(ast.QueryStruct).SearchFilter
   287  
   288  	assert.NotNil(t, filterNode)
   289  	assert.Equal(t, "=", filterNode.Comparison.Op)
   290  	assert.Equal(t, "status", filterNode.Comparison.Field)
   291  	assert.Equal(t, false, filterNode.Comparison.Values)
   292  
   293  	expressionFilter := extractExpressionFilter(t, filterNode)
   294  	assert.Equal(t, "status", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   295  	assert.Equal(t, utils.Equals, expressionFilter.FilterOperator)
   296  	assert.Equal(t, uint8(0), expressionFilter.RightInput.Expression.LeftInput.ColumnValue.BoolVal)
   297  }
   298  
   299  func Test_searchFieldEqualToUnquotedString(t *testing.T) {
   300  	query := []byte(`search status=ok`)
   301  	res, err := spl.Parse("", query)
   302  	assert.Nil(t, err)
   303  	filterNode := res.(ast.QueryStruct).SearchFilter
   304  
   305  	assert.NotNil(t, filterNode)
   306  	assert.Equal(t, "=", filterNode.Comparison.Op)
   307  	assert.Equal(t, "status", filterNode.Comparison.Field)
   308  	assert.Equal(t, `"ok"`, filterNode.Comparison.Values)
   309  
   310  	expressionFilter := extractExpressionFilter(t, filterNode)
   311  	assert.Equal(t, "status", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   312  	assert.Equal(t, utils.Equals, expressionFilter.FilterOperator)
   313  	assert.Equal(t, "ok", expressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal)
   314  }
   315  
   316  func Test_searchFieldNotEqualToUnquotedString(t *testing.T) {
   317  	query := []byte(`search status!=ok`)
   318  	res, err := spl.Parse("", query)
   319  	assert.Nil(t, err)
   320  	filterNode := res.(ast.QueryStruct).SearchFilter
   321  
   322  	assert.NotNil(t, filterNode)
   323  	assert.Equal(t, "!=", filterNode.Comparison.Op)
   324  	assert.Equal(t, "status", filterNode.Comparison.Field)
   325  	assert.Equal(t, `"ok"`, filterNode.Comparison.Values)
   326  
   327  	expressionFilter := extractExpressionFilter(t, filterNode)
   328  	assert.Equal(t, "status", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   329  	assert.Equal(t, utils.NotEquals, expressionFilter.FilterOperator)
   330  	assert.Equal(t, "ok", expressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal)
   331  }
   332  
   333  func Test_searchFieldLessThanUnquotedString(t *testing.T) {
   334  	query := []byte(`search status<ok`)
   335  	res, err := spl.Parse("", query)
   336  	assert.Nil(t, res)
   337  	assert.NotNil(t, err)
   338  }
   339  
   340  func Test_searchFieldLessThanOrEqualToUnquotedString(t *testing.T) {
   341  	query := []byte(`search status<=ok`)
   342  	res, err := spl.Parse("", query)
   343  	assert.Nil(t, res)
   344  	assert.NotNil(t, err)
   345  }
   346  
   347  func Test_searchFieldGreaterThanThanUnquotedString(t *testing.T) {
   348  	query := []byte(`search status>ok`)
   349  	res, err := spl.Parse("", query)
   350  	assert.Nil(t, res)
   351  	assert.NotNil(t, err)
   352  }
   353  
   354  func Test_searchFieldGreaterThanOrEqualToUnquotedString(t *testing.T) {
   355  	query := []byte(`search status>=ok`)
   356  	res, err := spl.Parse("", query)
   357  	assert.Nil(t, res)
   358  	assert.NotNil(t, err)
   359  }
   360  
   361  func Test_searchInteger(t *testing.T) {
   362  	query := []byte(`search 123`)
   363  	res, err := spl.Parse("", query)
   364  	assert.Nil(t, err)
   365  	filterNode := res.(ast.QueryStruct).SearchFilter
   366  
   367  	assert.NotNil(t, filterNode)
   368  	assert.Equal(t, "=", filterNode.Comparison.Op)
   369  	assert.Equal(t, "*", filterNode.Comparison.Field)
   370  	assert.Equal(t, json.Number("123"), filterNode.Comparison.Values)
   371  
   372  	expressionFilter := extractExpressionFilter(t, filterNode)
   373  	assert.Equal(t, "*", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   374  	assert.Equal(t, utils.Equals, expressionFilter.FilterOperator)
   375  	dtype, err := utils.CreateDtypeEnclosure(json.Number("123"), 0 /* qid */)
   376  	assert.Nil(t, err)
   377  	assert.Equal(t, dtype, expressionFilter.RightInput.Expression.LeftInput.ColumnValue)
   378  }
   379  
   380  func Test_searchIntegerLeadingZeros(t *testing.T) {
   381  	query := []byte(`search 00123`)
   382  	res, err := spl.Parse("", query)
   383  	assert.Nil(t, err)
   384  	filterNode := res.(ast.QueryStruct).SearchFilter
   385  
   386  	assert.NotNil(t, filterNode)
   387  	assert.Equal(t, "=", filterNode.Comparison.Op)
   388  	assert.Equal(t, "*", filterNode.Comparison.Field)
   389  	assert.Equal(t, json.Number("00123"), filterNode.Comparison.Values)
   390  
   391  	expressionFilter := extractExpressionFilter(t, filterNode)
   392  	assert.Equal(t, "*", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   393  	assert.Equal(t, utils.Equals, expressionFilter.FilterOperator)
   394  	dtype, err := utils.CreateDtypeEnclosure(json.Number("00123"), 0 /* qid */)
   395  	assert.Nil(t, err)
   396  	assert.Equal(t, dtype, expressionFilter.RightInput.Expression.LeftInput.ColumnValue)
   397  }
   398  
   399  func Test_searchIntegerLeadingPlusSign(t *testing.T) {
   400  	query := []byte(`search +123`)
   401  	res, err := spl.Parse("", query)
   402  	assert.Nil(t, err)
   403  	filterNode := res.(ast.QueryStruct).SearchFilter
   404  
   405  	assert.NotNil(t, filterNode)
   406  	assert.Equal(t, "=", filterNode.Comparison.Op)
   407  	assert.Equal(t, "*", filterNode.Comparison.Field)
   408  	assert.Equal(t, json.Number("+123"), filterNode.Comparison.Values)
   409  
   410  	expressionFilter := extractExpressionFilter(t, filterNode)
   411  	assert.Equal(t, "*", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   412  	assert.Equal(t, utils.Equals, expressionFilter.FilterOperator)
   413  	dtype, err := utils.CreateDtypeEnclosure(json.Number("+123"), 0 /* qid */)
   414  	assert.Nil(t, err)
   415  	assert.Equal(t, dtype, expressionFilter.RightInput.Expression.LeftInput.ColumnValue)
   416  }
   417  
   418  func Test_searchNegativeInteger(t *testing.T) {
   419  	query := []byte(`search -123`)
   420  	res, err := spl.Parse("", query)
   421  	assert.Nil(t, err)
   422  	filterNode := res.(ast.QueryStruct).SearchFilter
   423  
   424  	assert.NotNil(t, filterNode)
   425  	assert.Equal(t, "=", filterNode.Comparison.Op)
   426  	assert.Equal(t, "*", filterNode.Comparison.Field)
   427  	assert.Equal(t, json.Number("-123"), filterNode.Comparison.Values)
   428  
   429  	expressionFilter := extractExpressionFilter(t, filterNode)
   430  	assert.Equal(t, "*", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   431  	assert.Equal(t, utils.Equals, expressionFilter.FilterOperator)
   432  	dtype, err := utils.CreateDtypeEnclosure(json.Number("-123"), 0 /* qid */)
   433  	assert.Nil(t, err)
   434  	assert.Equal(t, dtype, expressionFilter.RightInput.Expression.LeftInput.ColumnValue)
   435  }
   436  
   437  func Test_searchFloat(t *testing.T) {
   438  	query := []byte(`search 123.5`)
   439  	res, err := spl.Parse("", query)
   440  	assert.Nil(t, err)
   441  	filterNode := res.(ast.QueryStruct).SearchFilter
   442  
   443  	assert.NotNil(t, filterNode)
   444  	assert.Equal(t, "=", filterNode.Comparison.Op)
   445  	assert.Equal(t, "*", filterNode.Comparison.Field)
   446  	assert.Equal(t, json.Number("123.5"), filterNode.Comparison.Values)
   447  
   448  	expressionFilter := extractExpressionFilter(t, filterNode)
   449  	assert.Equal(t, "*", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   450  	assert.Equal(t, utils.Equals, expressionFilter.FilterOperator)
   451  	dtype, err := utils.CreateDtypeEnclosure(json.Number("123.5"), 0 /* qid */)
   452  	assert.Nil(t, err)
   453  	assert.Equal(t, dtype, expressionFilter.RightInput.Expression.LeftInput.ColumnValue)
   454  }
   455  
   456  func Test_searchFloatLeadingDecimal(t *testing.T) {
   457  	query := []byte(`search .375`)
   458  	res, err := spl.Parse("", query)
   459  	assert.Nil(t, err)
   460  	filterNode := res.(ast.QueryStruct).SearchFilter
   461  
   462  	assert.NotNil(t, filterNode)
   463  	assert.Equal(t, "=", filterNode.Comparison.Op)
   464  	assert.Equal(t, "*", filterNode.Comparison.Field)
   465  	assert.Equal(t, json.Number(".375"), filterNode.Comparison.Values)
   466  
   467  	expressionFilter := extractExpressionFilter(t, filterNode)
   468  	assert.Equal(t, "*", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   469  	assert.Equal(t, utils.Equals, expressionFilter.FilterOperator)
   470  	dtype, err := utils.CreateDtypeEnclosure(json.Number(".375"), 0 /* qid */)
   471  	assert.Nil(t, err)
   472  	assert.Equal(t, dtype, expressionFilter.RightInput.Expression.LeftInput.ColumnValue)
   473  }
   474  
   475  func Test_searchFloatLeadingPlusSign(t *testing.T) {
   476  	query := []byte(`search +0.375`)
   477  	res, err := spl.Parse("", query)
   478  	assert.Nil(t, err)
   479  	filterNode := res.(ast.QueryStruct).SearchFilter
   480  
   481  	assert.NotNil(t, filterNode)
   482  	assert.Equal(t, "=", filterNode.Comparison.Op)
   483  	assert.Equal(t, "*", filterNode.Comparison.Field)
   484  	assert.Equal(t, json.Number("+0.375"), filterNode.Comparison.Values)
   485  
   486  	expressionFilter := extractExpressionFilter(t, filterNode)
   487  	assert.Equal(t, "*", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   488  	assert.Equal(t, utils.Equals, expressionFilter.FilterOperator)
   489  	dtype, err := utils.CreateDtypeEnclosure(json.Number("+0.375"), 0 /* qid */)
   490  	assert.Nil(t, err)
   491  	assert.Equal(t, dtype, expressionFilter.RightInput.Expression.LeftInput.ColumnValue)
   492  }
   493  
   494  func Test_searchFieldEqualToNumber(t *testing.T) {
   495  	query := []byte(`search status=400`)
   496  	res, err := spl.Parse("", query)
   497  	assert.Nil(t, err)
   498  	filterNode := res.(ast.QueryStruct).SearchFilter
   499  
   500  	assert.NotNil(t, filterNode)
   501  	assert.Equal(t, "=", filterNode.Comparison.Op)
   502  	assert.Equal(t, "status", filterNode.Comparison.Field)
   503  	assert.Equal(t, json.Number("400"), filterNode.Comparison.Values)
   504  
   505  	expressionFilter := extractExpressionFilter(t, filterNode)
   506  	assert.Equal(t, "status", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   507  	assert.Equal(t, utils.Equals, expressionFilter.FilterOperator)
   508  	dtype, err := utils.CreateDtypeEnclosure(json.Number("400"), 0 /* qid */)
   509  	assert.Nil(t, err)
   510  	assert.Equal(t, dtype, expressionFilter.RightInput.Expression.LeftInput.ColumnValue)
   511  }
   512  
   513  func Test_searchFieldNotEqualToNumber(t *testing.T) {
   514  	query := []byte(`search status!=400`)
   515  	res, err := spl.Parse("", query)
   516  	assert.Nil(t, err)
   517  	filterNode := res.(ast.QueryStruct).SearchFilter
   518  
   519  	assert.NotNil(t, filterNode)
   520  	assert.Equal(t, "!=", filterNode.Comparison.Op)
   521  	assert.Equal(t, "status", filterNode.Comparison.Field)
   522  	assert.Equal(t, json.Number("400"), filterNode.Comparison.Values)
   523  
   524  	expressionFilter := extractExpressionFilter(t, filterNode)
   525  	assert.Equal(t, "status", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   526  	assert.Equal(t, utils.NotEquals, expressionFilter.FilterOperator)
   527  	dtype, err := utils.CreateDtypeEnclosure(json.Number("400"), 0 /* qid */)
   528  	assert.Nil(t, err)
   529  	assert.Equal(t, dtype, expressionFilter.RightInput.Expression.LeftInput.ColumnValue)
   530  }
   531  
   532  func Test_searchFieldLessThanNumber(t *testing.T) {
   533  	query := []byte(`search latency<1000`)
   534  	res, err := spl.Parse("", query)
   535  	assert.Nil(t, err)
   536  	filterNode := res.(ast.QueryStruct).SearchFilter
   537  
   538  	assert.NotNil(t, filterNode)
   539  	assert.Equal(t, "<", filterNode.Comparison.Op)
   540  	assert.Equal(t, "latency", filterNode.Comparison.Field)
   541  	assert.Equal(t, json.Number("1000"), filterNode.Comparison.Values)
   542  
   543  	expressionFilter := extractExpressionFilter(t, filterNode)
   544  	assert.Equal(t, "latency", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   545  	assert.Equal(t, utils.LessThan, expressionFilter.FilterOperator)
   546  	dtype, err := utils.CreateDtypeEnclosure(json.Number("1000"), 0 /* qid */)
   547  	assert.Nil(t, err)
   548  	assert.Equal(t, dtype, expressionFilter.RightInput.Expression.LeftInput.ColumnValue)
   549  }
   550  
   551  func Test_searchFieldLessThanOrEqualToNumber(t *testing.T) {
   552  	query := []byte(`search latency<=0.5`)
   553  	res, err := spl.Parse("", query)
   554  	assert.Nil(t, err)
   555  	filterNode := res.(ast.QueryStruct).SearchFilter
   556  
   557  	assert.NotNil(t, filterNode)
   558  	assert.Equal(t, "<=", filterNode.Comparison.Op)
   559  	assert.Equal(t, "latency", filterNode.Comparison.Field)
   560  	assert.Equal(t, json.Number("0.5"), filterNode.Comparison.Values)
   561  
   562  	expressionFilter := extractExpressionFilter(t, filterNode)
   563  	assert.Equal(t, "latency", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   564  	assert.Equal(t, utils.LessThanOrEqualTo, expressionFilter.FilterOperator)
   565  	dtype, err := utils.CreateDtypeEnclosure(json.Number("0.5"), 0 /* qid */)
   566  	assert.Nil(t, err)
   567  	assert.Equal(t, dtype, expressionFilter.RightInput.Expression.LeftInput.ColumnValue)
   568  }
   569  
   570  func Test_searchFieldGreaterThanNumber(t *testing.T) {
   571  	query := []byte(`search latency>3.175`)
   572  	res, err := spl.Parse("", query)
   573  	assert.Nil(t, err)
   574  	filterNode := res.(ast.QueryStruct).SearchFilter
   575  
   576  	assert.NotNil(t, filterNode)
   577  	assert.Equal(t, ">", filterNode.Comparison.Op)
   578  	assert.Equal(t, "latency", filterNode.Comparison.Field)
   579  	assert.Equal(t, json.Number("3.175"), filterNode.Comparison.Values)
   580  
   581  	expressionFilter := extractExpressionFilter(t, filterNode)
   582  	assert.Equal(t, "latency", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   583  	assert.Equal(t, utils.GreaterThan, expressionFilter.FilterOperator)
   584  	dtype, err := utils.CreateDtypeEnclosure(json.Number("3.175"), 0 /* qid */)
   585  	assert.Nil(t, err)
   586  	assert.Equal(t, dtype, expressionFilter.RightInput.Expression.LeftInput.ColumnValue)
   587  }
   588  
   589  func Test_searchFieldGreaterThanOrEqualToNumber(t *testing.T) {
   590  	query := []byte(`search latency>=1200`)
   591  	res, err := spl.Parse("", query)
   592  	assert.Nil(t, err)
   593  	filterNode := res.(ast.QueryStruct).SearchFilter
   594  
   595  	assert.NotNil(t, filterNode)
   596  	assert.Equal(t, ">=", filterNode.Comparison.Op)
   597  	assert.Equal(t, "latency", filterNode.Comparison.Field)
   598  	assert.Equal(t, json.Number("1200"), filterNode.Comparison.Values)
   599  
   600  	expressionFilter := extractExpressionFilter(t, filterNode)
   601  	assert.Equal(t, "latency", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   602  	assert.Equal(t, utils.GreaterThanOrEqualTo, expressionFilter.FilterOperator)
   603  	dtype, err := utils.CreateDtypeEnclosure(json.Number("1200"), 0 /* qid */)
   604  	assert.Nil(t, err)
   605  	assert.Equal(t, dtype, expressionFilter.RightInput.Expression.LeftInput.ColumnValue)
   606  }
   607  
   608  func Test_searchSimpleAND(t *testing.T) {
   609  	query := []byte(`search status=ok AND latency<1000`)
   610  	res, err := spl.Parse("", query)
   611  	assert.Nil(t, err)
   612  	filterNode := res.(ast.QueryStruct).SearchFilter
   613  
   614  	assert.NotNil(t, filterNode)
   615  	assert.Equal(t, filterNode.NodeType, ast.NodeAnd)
   616  
   617  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
   618  	assert.Equal(t, filterNode.Left.Comparison.Field, "status")
   619  	assert.Equal(t, filterNode.Left.Comparison.Op, "=")
   620  	assert.Equal(t, filterNode.Left.Comparison.Values, `"ok"`)
   621  
   622  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeTerminal)
   623  	assert.Equal(t, filterNode.Right.Comparison.Field, "latency")
   624  	assert.Equal(t, filterNode.Right.Comparison.Op, "<")
   625  	assert.Equal(t, filterNode.Right.Comparison.Values, json.Number("1000"))
   626  
   627  	astNode := &structs.ASTNode{}
   628  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
   629  	assert.Nil(t, err)
   630  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
   631  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 2)
   632  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "status")
   633  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
   634  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal, "ok")
   635  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "latency")
   636  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.LessThan)
   637  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1000))
   638  }
   639  
   640  func Test_searchChainedAND(t *testing.T) {
   641  	query := []byte(`search A=1 AND B=2 AND C=3`)
   642  	res, err := spl.Parse("", query)
   643  	assert.Nil(t, err)
   644  	filterNode := res.(ast.QueryStruct).SearchFilter
   645  
   646  	assert.NotNil(t, filterNode)
   647  	assert.Equal(t, ast.NodeAnd, filterNode.NodeType)
   648  	assert.Equal(t, ast.NodeAnd, filterNode.Left.NodeType)
   649  
   650  	assert.Equal(t, filterNode.Left.Left.NodeType, ast.NodeTerminal)
   651  	assert.Equal(t, filterNode.Left.Left.Comparison.Field, "A")
   652  	assert.Equal(t, filterNode.Left.Left.Comparison.Op, "=")
   653  	assert.Equal(t, filterNode.Left.Left.Comparison.Values, json.Number("1"))
   654  
   655  	assert.Equal(t, filterNode.Left.Right.NodeType, ast.NodeTerminal)
   656  	assert.Equal(t, filterNode.Left.Right.Comparison.Field, "B")
   657  	assert.Equal(t, filterNode.Left.Right.Comparison.Op, "=")
   658  	assert.Equal(t, filterNode.Left.Right.Comparison.Values, json.Number("2"))
   659  
   660  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeTerminal)
   661  	assert.Equal(t, filterNode.Right.Comparison.Field, "C")
   662  	assert.Equal(t, filterNode.Right.Comparison.Op, "=")
   663  	assert.Equal(t, filterNode.Right.Comparison.Values, json.Number("3"))
   664  
   665  	astNode := &structs.ASTNode{}
   666  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
   667  	assert.Nil(t, err)
   668  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
   669  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
   670  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "C")
   671  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
   672  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(3))
   673  	assert.Len(t, astNode.AndFilterCondition.NestedNodes, 1)
   674  	assert.Len(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria, 2)
   675  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
   676  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
   677  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
   678  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "B")
   679  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
   680  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(2))
   681  }
   682  
   683  func Test_searchSimpleOR(t *testing.T) {
   684  	query := []byte(`search status=ok OR latency<1000`)
   685  	res, err := spl.Parse("", query)
   686  	assert.Nil(t, err)
   687  	filterNode := res.(ast.QueryStruct).SearchFilter
   688  
   689  	assert.NotNil(t, filterNode)
   690  	assert.Equal(t, filterNode.NodeType, ast.NodeOr)
   691  
   692  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
   693  	assert.Equal(t, filterNode.Left.Comparison.Field, "status")
   694  	assert.Equal(t, filterNode.Left.Comparison.Op, "=")
   695  	assert.Equal(t, filterNode.Left.Comparison.Values, `"ok"`)
   696  
   697  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeTerminal)
   698  	assert.Equal(t, filterNode.Right.Comparison.Field, "latency")
   699  	assert.Equal(t, filterNode.Right.Comparison.Op, "<")
   700  	assert.Equal(t, filterNode.Right.Comparison.Values, json.Number("1000"))
   701  
   702  	astNode := &structs.ASTNode{}
   703  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
   704  	assert.Nil(t, err)
   705  	assert.NotNil(t, astNode.OrFilterCondition.FilterCriteria)
   706  	assert.Len(t, astNode.OrFilterCondition.FilterCriteria, 2)
   707  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "status")
   708  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
   709  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal, "ok")
   710  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "latency")
   711  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.LessThan)
   712  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1000))
   713  }
   714  
   715  func Test_searchChainedOR(t *testing.T) {
   716  	query := []byte(`search A=1 OR B=2 OR C=3`)
   717  	res, err := spl.Parse("", query)
   718  	assert.Nil(t, err)
   719  	filterNode := res.(ast.QueryStruct).SearchFilter
   720  
   721  	assert.NotNil(t, filterNode)
   722  	assert.Equal(t, ast.NodeOr, filterNode.NodeType)
   723  	assert.Equal(t, ast.NodeOr, filterNode.Left.NodeType)
   724  
   725  	assert.Equal(t, filterNode.Left.Left.NodeType, ast.NodeTerminal)
   726  	assert.Equal(t, filterNode.Left.Left.Comparison.Field, "A")
   727  	assert.Equal(t, filterNode.Left.Left.Comparison.Op, "=")
   728  	assert.Equal(t, filterNode.Left.Left.Comparison.Values, json.Number("1"))
   729  
   730  	assert.Equal(t, filterNode.Left.Right.NodeType, ast.NodeTerminal)
   731  	assert.Equal(t, filterNode.Left.Right.Comparison.Field, "B")
   732  	assert.Equal(t, filterNode.Left.Right.Comparison.Op, "=")
   733  	assert.Equal(t, filterNode.Left.Right.Comparison.Values, json.Number("2"))
   734  
   735  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeTerminal)
   736  	assert.Equal(t, filterNode.Right.Comparison.Field, "C")
   737  	assert.Equal(t, filterNode.Right.Comparison.Op, "=")
   738  	assert.Equal(t, filterNode.Right.Comparison.Values, json.Number("3"))
   739  
   740  	astNode := &structs.ASTNode{}
   741  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
   742  	assert.Nil(t, err)
   743  	assert.NotNil(t, astNode.OrFilterCondition.FilterCriteria)
   744  	assert.Len(t, astNode.OrFilterCondition.FilterCriteria, 1)
   745  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "C")
   746  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
   747  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(3))
   748  	assert.Len(t, astNode.OrFilterCondition.NestedNodes, 1)
   749  	assert.Len(t, astNode.OrFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria, 2)
   750  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
   751  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
   752  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
   753  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "B")
   754  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
   755  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(2))
   756  }
   757  
   758  func Test_searchANDThenOR(t *testing.T) {
   759  	// This should be parsed as `A=1 AND (B=2 OR C=3)`.
   760  	query := []byte(`search A=1 AND B=2 OR C=3`)
   761  	res, err := spl.Parse("", query)
   762  	assert.Nil(t, err)
   763  	filterNode := res.(ast.QueryStruct).SearchFilter
   764  
   765  	assert.NotNil(t, filterNode)
   766  	assert.Equal(t, filterNode.NodeType, ast.NodeAnd)
   767  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeOr)
   768  
   769  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
   770  	assert.Equal(t, filterNode.Left.Comparison.Field, "A")
   771  	assert.Equal(t, filterNode.Left.Comparison.Op, "=")
   772  	assert.Equal(t, filterNode.Left.Comparison.Values, json.Number("1"))
   773  
   774  	assert.Equal(t, filterNode.Right.Left.NodeType, ast.NodeTerminal)
   775  	assert.Equal(t, filterNode.Right.Left.Comparison.Field, "B")
   776  	assert.Equal(t, filterNode.Right.Left.Comparison.Op, "=")
   777  	assert.Equal(t, filterNode.Right.Left.Comparison.Values, json.Number("2"))
   778  
   779  	assert.Equal(t, filterNode.Right.Right.NodeType, ast.NodeTerminal)
   780  	assert.Equal(t, filterNode.Right.Right.Comparison.Field, "C")
   781  	assert.Equal(t, filterNode.Right.Right.Comparison.Op, "=")
   782  	assert.Equal(t, filterNode.Right.Right.Comparison.Values, json.Number("3"))
   783  
   784  	astNode := &structs.ASTNode{}
   785  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
   786  	assert.Nil(t, err)
   787  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
   788  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
   789  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
   790  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
   791  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
   792  	assert.Len(t, astNode.AndFilterCondition.NestedNodes, 1)
   793  	assert.Len(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria, 2)
   794  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "B")
   795  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
   796  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(2))
   797  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "C")
   798  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
   799  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(3))
   800  }
   801  
   802  func Test_searchORThenAND(t *testing.T) {
   803  	// This should be parsed as `(A=1 OR B=2) AND C=3`.
   804  	query := []byte(`search A=1 OR B=2 AND C=3`)
   805  	res, err := spl.Parse("", query)
   806  	assert.Nil(t, err)
   807  	filterNode := res.(ast.QueryStruct).SearchFilter
   808  
   809  	assert.NotNil(t, filterNode)
   810  	assert.Equal(t, filterNode.NodeType, ast.NodeAnd)
   811  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeOr)
   812  
   813  	assert.Equal(t, filterNode.Left.Left.NodeType, ast.NodeTerminal)
   814  	assert.Equal(t, filterNode.Left.Left.Comparison.Field, "A")
   815  	assert.Equal(t, filterNode.Left.Left.Comparison.Op, "=")
   816  	assert.Equal(t, filterNode.Left.Left.Comparison.Values, json.Number("1"))
   817  
   818  	assert.Equal(t, filterNode.Left.Right.NodeType, ast.NodeTerminal)
   819  	assert.Equal(t, filterNode.Left.Right.Comparison.Field, "B")
   820  	assert.Equal(t, filterNode.Left.Right.Comparison.Op, "=")
   821  	assert.Equal(t, filterNode.Left.Right.Comparison.Values, json.Number("2"))
   822  
   823  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeTerminal)
   824  	assert.Equal(t, filterNode.Right.Comparison.Field, "C")
   825  	assert.Equal(t, filterNode.Right.Comparison.Op, "=")
   826  	assert.Equal(t, filterNode.Right.Comparison.Values, json.Number("3"))
   827  
   828  	astNode := &structs.ASTNode{}
   829  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
   830  	assert.Nil(t, err)
   831  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
   832  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
   833  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "C")
   834  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
   835  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(3))
   836  	assert.Len(t, astNode.AndFilterCondition.NestedNodes, 1)
   837  	assert.Len(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria, 2)
   838  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
   839  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
   840  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
   841  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "B")
   842  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
   843  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(2))
   844  }
   845  
   846  func Test_SimpleParentheses(t *testing.T) {
   847  	query := []byte(`search (status="ok")`)
   848  	res, err := spl.Parse("", query)
   849  	assert.Nil(t, err)
   850  	filterNode := res.(ast.QueryStruct).SearchFilter
   851  
   852  	assert.NotNil(t, filterNode)
   853  	assert.Equal(t, "=", filterNode.Comparison.Op)
   854  	assert.Equal(t, "status", filterNode.Comparison.Field)
   855  	assert.Equal(t, `"ok"`, filterNode.Comparison.Values)
   856  
   857  	expressionFilter := extractExpressionFilter(t, filterNode)
   858  	assert.Equal(t, "status", expressionFilter.LeftInput.Expression.LeftInput.ColumnName)
   859  	assert.Equal(t, utils.Equals, expressionFilter.FilterOperator)
   860  	assert.Equal(t, "ok", expressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal)
   861  }
   862  
   863  func Test_searchParenthesesToChangePrecedence(t *testing.T) {
   864  	query := []byte(`search A=1 OR (B=2 AND C=3)`)
   865  	res, err := spl.Parse("", query)
   866  	assert.Nil(t, err)
   867  	filterNode := res.(ast.QueryStruct).SearchFilter
   868  
   869  	assert.NotNil(t, filterNode)
   870  	assert.Equal(t, filterNode.NodeType, ast.NodeOr)
   871  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeAnd)
   872  
   873  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
   874  	assert.Equal(t, filterNode.Left.Comparison.Field, "A")
   875  	assert.Equal(t, filterNode.Left.Comparison.Op, "=")
   876  	assert.Equal(t, filterNode.Left.Comparison.Values, json.Number("1"))
   877  
   878  	assert.Equal(t, filterNode.Right.Left.NodeType, ast.NodeTerminal)
   879  	assert.Equal(t, filterNode.Right.Left.Comparison.Field, "B")
   880  	assert.Equal(t, filterNode.Right.Left.Comparison.Op, "=")
   881  	assert.Equal(t, filterNode.Right.Left.Comparison.Values, json.Number("2"))
   882  
   883  	assert.Equal(t, filterNode.Right.Right.NodeType, ast.NodeTerminal)
   884  	assert.Equal(t, filterNode.Right.Right.Comparison.Field, "C")
   885  	assert.Equal(t, filterNode.Right.Right.Comparison.Op, "=")
   886  	assert.Equal(t, filterNode.Right.Right.Comparison.Values, json.Number("3"))
   887  
   888  	astNode := &structs.ASTNode{}
   889  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
   890  	assert.Nil(t, err)
   891  	assert.NotNil(t, astNode.OrFilterCondition.FilterCriteria)
   892  	assert.Len(t, astNode.OrFilterCondition.FilterCriteria, 1)
   893  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
   894  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
   895  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
   896  	assert.Len(t, astNode.OrFilterCondition.NestedNodes, 1)
   897  	assert.Len(t, astNode.OrFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria, 2)
   898  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "B")
   899  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
   900  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(2))
   901  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "C")
   902  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
   903  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(3))
   904  }
   905  
   906  func Test_searchImplicitAND(t *testing.T) {
   907  	query := []byte(`search status=ok latency<1000`)
   908  	res, err := spl.Parse("", query)
   909  	assert.Nil(t, err)
   910  	filterNode := res.(ast.QueryStruct).SearchFilter
   911  
   912  	assert.NotNil(t, filterNode)
   913  	assert.Equal(t, filterNode.NodeType, ast.NodeAnd)
   914  
   915  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
   916  	assert.Equal(t, filterNode.Left.Comparison.Field, "status")
   917  	assert.Equal(t, filterNode.Left.Comparison.Op, "=")
   918  	assert.Equal(t, filterNode.Left.Comparison.Values, `"ok"`)
   919  
   920  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeTerminal)
   921  	assert.Equal(t, filterNode.Right.Comparison.Field, "latency")
   922  	assert.Equal(t, filterNode.Right.Comparison.Op, "<")
   923  	assert.Equal(t, filterNode.Right.Comparison.Values, json.Number("1000"))
   924  
   925  	astNode := &structs.ASTNode{}
   926  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
   927  	assert.Nil(t, err)
   928  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
   929  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 2)
   930  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "status")
   931  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
   932  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal, "ok")
   933  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "latency")
   934  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.LessThan)
   935  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1000))
   936  }
   937  
   938  func Test_searchChainedImplicitAND(t *testing.T) {
   939  	query := []byte(`search A=1 B=2 C=3`)
   940  	res, err := spl.Parse("", query)
   941  	assert.Nil(t, err)
   942  	filterNode := res.(ast.QueryStruct).SearchFilter
   943  
   944  	assert.NotNil(t, filterNode)
   945  	assert.Equal(t, ast.NodeAnd, filterNode.NodeType)
   946  	assert.Equal(t, ast.NodeAnd, filterNode.Left.NodeType)
   947  
   948  	assert.Equal(t, filterNode.Left.Left.NodeType, ast.NodeTerminal)
   949  	assert.Equal(t, filterNode.Left.Left.Comparison.Field, "A")
   950  	assert.Equal(t, filterNode.Left.Left.Comparison.Op, "=")
   951  	assert.Equal(t, filterNode.Left.Left.Comparison.Values, json.Number("1"))
   952  
   953  	assert.Equal(t, filterNode.Left.Right.NodeType, ast.NodeTerminal)
   954  	assert.Equal(t, filterNode.Left.Right.Comparison.Field, "B")
   955  	assert.Equal(t, filterNode.Left.Right.Comparison.Op, "=")
   956  	assert.Equal(t, filterNode.Left.Right.Comparison.Values, json.Number("2"))
   957  
   958  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeTerminal)
   959  	assert.Equal(t, filterNode.Right.Comparison.Field, "C")
   960  	assert.Equal(t, filterNode.Right.Comparison.Op, "=")
   961  	assert.Equal(t, filterNode.Right.Comparison.Values, json.Number("3"))
   962  
   963  	astNode := &structs.ASTNode{}
   964  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
   965  	assert.Nil(t, err)
   966  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
   967  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
   968  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "C")
   969  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
   970  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(3))
   971  	assert.Len(t, astNode.AndFilterCondition.NestedNodes, 1)
   972  	assert.Len(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria, 2)
   973  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
   974  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
   975  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
   976  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "B")
   977  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
   978  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(2))
   979  }
   980  
   981  func Test_searchMixedImplicitAndExplicitAND(t *testing.T) {
   982  	query := []byte(`search A=1 AND B=2 C=3`)
   983  	res, err := spl.Parse("", query)
   984  	assert.Nil(t, err)
   985  	filterNode := res.(ast.QueryStruct).SearchFilter
   986  
   987  	assert.NotNil(t, filterNode)
   988  	assert.Equal(t, ast.NodeAnd, filterNode.NodeType)
   989  	assert.Equal(t, ast.NodeAnd, filterNode.Left.NodeType)
   990  
   991  	assert.Equal(t, filterNode.Left.Left.NodeType, ast.NodeTerminal)
   992  	assert.Equal(t, filterNode.Left.Left.Comparison.Field, "A")
   993  	assert.Equal(t, filterNode.Left.Left.Comparison.Op, "=")
   994  	assert.Equal(t, filterNode.Left.Left.Comparison.Values, json.Number("1"))
   995  
   996  	assert.Equal(t, filterNode.Left.Right.NodeType, ast.NodeTerminal)
   997  	assert.Equal(t, filterNode.Left.Right.Comparison.Field, "B")
   998  	assert.Equal(t, filterNode.Left.Right.Comparison.Op, "=")
   999  	assert.Equal(t, filterNode.Left.Right.Comparison.Values, json.Number("2"))
  1000  
  1001  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeTerminal)
  1002  	assert.Equal(t, filterNode.Right.Comparison.Field, "C")
  1003  	assert.Equal(t, filterNode.Right.Comparison.Op, "=")
  1004  	assert.Equal(t, filterNode.Right.Comparison.Values, json.Number("3"))
  1005  
  1006  	astNode := &structs.ASTNode{}
  1007  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1008  	assert.Nil(t, err)
  1009  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1010  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1011  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "C")
  1012  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1013  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(3))
  1014  	assert.Len(t, astNode.AndFilterCondition.NestedNodes, 1)
  1015  	assert.Len(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria, 2)
  1016  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1017  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1018  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1019  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "B")
  1020  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
  1021  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(2))
  1022  }
  1023  
  1024  func Test_searchSimpleNOTEquals(t *testing.T) {
  1025  	query := []byte(`search NOT status=200`)
  1026  	res, err := spl.Parse("", query)
  1027  	assert.Nil(t, err)
  1028  	filterNode := res.(ast.QueryStruct).SearchFilter
  1029  
  1030  	assert.NotNil(t, filterNode)
  1031  
  1032  	// NOT is handled internally with De Morgan's law.
  1033  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1034  	assert.Equal(t, filterNode.Comparison.Field, "status")
  1035  	assert.Equal(t, filterNode.Comparison.Op, "!=")
  1036  	assert.Equal(t, filterNode.Comparison.Values, json.Number("200"))
  1037  
  1038  	astNode := &structs.ASTNode{}
  1039  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1040  	assert.Nil(t, err)
  1041  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1042  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1043  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "status")
  1044  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.NotEquals)
  1045  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(200))
  1046  }
  1047  
  1048  func Test_searchSimpleNOTNotEquals(t *testing.T) {
  1049  	query := []byte(`search NOT status!=200`)
  1050  	res, err := spl.Parse("", query)
  1051  	assert.Nil(t, err)
  1052  	filterNode := res.(ast.QueryStruct).SearchFilter
  1053  
  1054  	assert.NotNil(t, filterNode)
  1055  
  1056  	// NOT is handled internally with De Morgan's law.
  1057  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1058  	assert.Equal(t, filterNode.Comparison.Field, "status")
  1059  	assert.Equal(t, filterNode.Comparison.Op, "=")
  1060  	assert.Equal(t, filterNode.Comparison.Values, json.Number("200"))
  1061  
  1062  	astNode := &structs.ASTNode{}
  1063  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1064  	assert.Nil(t, err)
  1065  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1066  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1067  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "status")
  1068  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1069  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(200))
  1070  }
  1071  
  1072  func Test_searchSimpleNOTLessThan(t *testing.T) {
  1073  	query := []byte(`search NOT status<200`)
  1074  	res, err := spl.Parse("", query)
  1075  	assert.Nil(t, err)
  1076  	filterNode := res.(ast.QueryStruct).SearchFilter
  1077  
  1078  	assert.NotNil(t, filterNode)
  1079  
  1080  	// NOT is handled internally with De Morgan's law.
  1081  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1082  	assert.Equal(t, filterNode.Comparison.Field, "status")
  1083  	assert.Equal(t, filterNode.Comparison.Op, ">=")
  1084  	assert.Equal(t, filterNode.Comparison.Values, json.Number("200"))
  1085  
  1086  	astNode := &structs.ASTNode{}
  1087  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1088  	assert.Nil(t, err)
  1089  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1090  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1091  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "status")
  1092  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.GreaterThanOrEqualTo)
  1093  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(200))
  1094  }
  1095  
  1096  func Test_searchSimpleNOTGreaterThan(t *testing.T) {
  1097  	query := []byte(`search NOT status>200`)
  1098  	res, err := spl.Parse("", query)
  1099  	assert.Nil(t, err)
  1100  	filterNode := res.(ast.QueryStruct).SearchFilter
  1101  
  1102  	assert.NotNil(t, filterNode)
  1103  
  1104  	// NOT is handled internally with De Morgan's law.
  1105  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1106  	assert.Equal(t, filterNode.Comparison.Field, "status")
  1107  	assert.Equal(t, filterNode.Comparison.Op, "<=")
  1108  	assert.Equal(t, filterNode.Comparison.Values, json.Number("200"))
  1109  
  1110  	astNode := &structs.ASTNode{}
  1111  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1112  	assert.Nil(t, err)
  1113  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1114  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1115  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "status")
  1116  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.LessThanOrEqualTo)
  1117  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(200))
  1118  }
  1119  
  1120  func Test_searchSimpleNOTLessThanOrEqual(t *testing.T) {
  1121  	query := []byte(`search NOT status<=200`)
  1122  	res, err := spl.Parse("", query)
  1123  	assert.Nil(t, err)
  1124  	filterNode := res.(ast.QueryStruct).SearchFilter
  1125  
  1126  	assert.NotNil(t, filterNode)
  1127  
  1128  	// NOT is handled internally with De Morgan's law.
  1129  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1130  	assert.Equal(t, filterNode.Comparison.Field, "status")
  1131  	assert.Equal(t, filterNode.Comparison.Op, ">")
  1132  	assert.Equal(t, filterNode.Comparison.Values, json.Number("200"))
  1133  
  1134  	astNode := &structs.ASTNode{}
  1135  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1136  	assert.Nil(t, err)
  1137  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1138  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1139  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "status")
  1140  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.GreaterThan)
  1141  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(200))
  1142  }
  1143  
  1144  func Test_searchSimpleNOTGreaterThanOrEqual(t *testing.T) {
  1145  	query := []byte(`search NOT status>=200`)
  1146  	res, err := spl.Parse("", query)
  1147  	assert.Nil(t, err)
  1148  	filterNode := res.(ast.QueryStruct).SearchFilter
  1149  
  1150  	assert.NotNil(t, filterNode)
  1151  
  1152  	// NOT is handled internally with De Morgan's law.
  1153  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1154  	assert.Equal(t, filterNode.Comparison.Field, "status")
  1155  	assert.Equal(t, filterNode.Comparison.Op, "<")
  1156  	assert.Equal(t, filterNode.Comparison.Values, json.Number("200"))
  1157  
  1158  	astNode := &structs.ASTNode{}
  1159  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1160  	assert.Nil(t, err)
  1161  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1162  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1163  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "status")
  1164  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.LessThan)
  1165  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(200))
  1166  }
  1167  
  1168  func Test_searchCancelingNots(t *testing.T) {
  1169  	query := []byte(`search NOT NOT (NOT (NOT status=200))`)
  1170  	res, err := spl.Parse("", query)
  1171  	assert.Nil(t, err)
  1172  	filterNode := res.(ast.QueryStruct).SearchFilter
  1173  
  1174  	assert.NotNil(t, filterNode)
  1175  
  1176  	// NOT is handled internally with De Morgan's law.
  1177  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1178  	assert.Equal(t, filterNode.Comparison.Field, "status")
  1179  	assert.Equal(t, filterNode.Comparison.Op, "=")
  1180  	assert.Equal(t, filterNode.Comparison.Values, json.Number("200"))
  1181  
  1182  	astNode := &structs.ASTNode{}
  1183  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1184  	assert.Nil(t, err)
  1185  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1186  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1187  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "status")
  1188  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1189  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(200))
  1190  }
  1191  
  1192  func Test_searchCompoundNOT(t *testing.T) {
  1193  	query := []byte(`search NOT (status=ok OR (A>1 AND NOT A>=10))`)
  1194  	res, err := spl.Parse("", query)
  1195  	assert.Nil(t, err)
  1196  	filterNode := res.(ast.QueryStruct).SearchFilter
  1197  
  1198  	assert.NotNil(t, filterNode)
  1199  	// NOT is handled internally with De Morgan's law.
  1200  	assert.Equal(t, filterNode.NodeType, ast.NodeAnd)
  1201  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeOr)
  1202  
  1203  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
  1204  	assert.Equal(t, filterNode.Left.Comparison.Field, "status")
  1205  	assert.Equal(t, filterNode.Left.Comparison.Op, "!=")
  1206  	assert.Equal(t, filterNode.Left.Comparison.Values, `"ok"`)
  1207  
  1208  	assert.Equal(t, filterNode.Right.Left.NodeType, ast.NodeTerminal)
  1209  	assert.Equal(t, filterNode.Right.Left.Comparison.Field, "A")
  1210  	assert.Equal(t, filterNode.Right.Left.Comparison.Op, "<=")
  1211  	assert.Equal(t, filterNode.Right.Left.Comparison.Values, json.Number("1"))
  1212  
  1213  	assert.Equal(t, filterNode.Right.Right.NodeType, ast.NodeTerminal)
  1214  	assert.Equal(t, filterNode.Right.Right.Comparison.Field, "A")
  1215  	assert.Equal(t, filterNode.Right.Right.Comparison.Op, ">=")
  1216  	assert.Equal(t, filterNode.Right.Right.Comparison.Values, json.Number("10"))
  1217  
  1218  	astNode := &structs.ASTNode{}
  1219  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1220  	assert.Nil(t, err)
  1221  	assert.Len(t, astNode.AndFilterCondition.NestedNodes, 1)
  1222  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1223  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "status")
  1224  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.NotEquals)
  1225  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal, "ok")
  1226  	assert.Len(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria, 2)
  1227  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1228  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.LessThanOrEqualTo)
  1229  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1230  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1231  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.GreaterThanOrEqualTo)
  1232  	assert.Equal(t, astNode.AndFilterCondition.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(10))
  1233  }
  1234  
  1235  func Test_searchQuotedWildcard(t *testing.T) {
  1236  	query := []byte(`search day="T*day"`)
  1237  	res, err := spl.Parse("", query)
  1238  	assert.Nil(t, err)
  1239  	filterNode := res.(ast.QueryStruct).SearchFilter
  1240  
  1241  	assert.NotNil(t, filterNode)
  1242  
  1243  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1244  	assert.Equal(t, filterNode.Comparison.Field, "day")
  1245  	assert.Equal(t, filterNode.Comparison.Op, "=")
  1246  	assert.Equal(t, filterNode.Comparison.Values, `"T*day"`)
  1247  
  1248  	astNode := &structs.ASTNode{}
  1249  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1250  	assert.Nil(t, err)
  1251  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1252  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1253  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "day")
  1254  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1255  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal, "T*day")
  1256  }
  1257  
  1258  func Test_searchUnquotedWildcard(t *testing.T) {
  1259  	query := []byte(`search day=T*day`)
  1260  	res, err := spl.Parse("", query)
  1261  	assert.Nil(t, err)
  1262  	filterNode := res.(ast.QueryStruct).SearchFilter
  1263  
  1264  	assert.NotNil(t, filterNode)
  1265  
  1266  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1267  	assert.Equal(t, filterNode.Comparison.Field, "day")
  1268  	assert.Equal(t, filterNode.Comparison.Op, "=")
  1269  	assert.Equal(t, filterNode.Comparison.Values, `"T*day"`)
  1270  
  1271  	astNode := &structs.ASTNode{}
  1272  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1273  	assert.Nil(t, err)
  1274  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1275  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1276  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "day")
  1277  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1278  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal, "T*day")
  1279  }
  1280  
  1281  func Test_searchNumberedWildcardBecomesString(t *testing.T) {
  1282  	query := []byte(`search status_code=50*`)
  1283  	res, err := spl.Parse("", query)
  1284  	assert.Nil(t, err)
  1285  	filterNode := res.(ast.QueryStruct).SearchFilter
  1286  
  1287  	assert.NotNil(t, filterNode)
  1288  
  1289  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1290  	assert.Equal(t, filterNode.Comparison.Field, "status_code")
  1291  	assert.Equal(t, filterNode.Comparison.Op, "=")
  1292  	assert.Equal(t, filterNode.Comparison.Values, `"50*"`)
  1293  
  1294  	astNode := &structs.ASTNode{}
  1295  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1296  	assert.Nil(t, err)
  1297  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1298  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1299  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "status_code")
  1300  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1301  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal, "50*")
  1302  }
  1303  
  1304  func Test_chainedSearch(t *testing.T) {
  1305  	// This should be equivalent to `search A=1 AND B=2`
  1306  	query := []byte(`A=1 | search B=2`)
  1307  	res, err := spl.Parse("", query)
  1308  	assert.Nil(t, err)
  1309  	filterNode := res.(ast.QueryStruct).SearchFilter
  1310  
  1311  	assert.NotNil(t, filterNode)
  1312  	assert.Equal(t, ast.NodeAnd, filterNode.NodeType)
  1313  
  1314  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
  1315  	assert.Equal(t, filterNode.Left.Comparison.Field, "A")
  1316  	assert.Equal(t, filterNode.Left.Comparison.Op, "=")
  1317  	assert.Equal(t, filterNode.Left.Comparison.Values, json.Number("1"))
  1318  
  1319  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeTerminal)
  1320  	assert.Equal(t, filterNode.Right.Comparison.Field, "B")
  1321  	assert.Equal(t, filterNode.Right.Comparison.Op, "=")
  1322  	assert.Equal(t, filterNode.Right.Comparison.Values, json.Number("2"))
  1323  
  1324  	astNode := &structs.ASTNode{}
  1325  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1326  	assert.Nil(t, err)
  1327  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1328  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 2)
  1329  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1330  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1331  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1332  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "B")
  1333  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
  1334  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(2))
  1335  }
  1336  
  1337  func Test_manyChainedSearch(t *testing.T) {
  1338  	// This should be equivalent to `search A=1 AND (B=2 AND (C=3 AND D=4))`
  1339  	query := []byte(`search A=1 | search B=2 | search C=3 | search D=4`)
  1340  	res, err := spl.Parse("", query)
  1341  	assert.Nil(t, err)
  1342  	filterNode := res.(ast.QueryStruct).SearchFilter
  1343  
  1344  	assert.NotNil(t, filterNode)
  1345  	assert.Equal(t, ast.NodeAnd, filterNode.NodeType)
  1346  	assert.Equal(t, ast.NodeAnd, filterNode.Right.NodeType)
  1347  	assert.Equal(t, ast.NodeAnd, filterNode.Right.Right.NodeType)
  1348  
  1349  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
  1350  	assert.Equal(t, filterNode.Left.Comparison.Field, "A")
  1351  	assert.Equal(t, filterNode.Left.Comparison.Op, "=")
  1352  	assert.Equal(t, filterNode.Left.Comparison.Values, json.Number("1"))
  1353  
  1354  	assert.Equal(t, filterNode.Right.Left.NodeType, ast.NodeTerminal)
  1355  	assert.Equal(t, filterNode.Right.Left.Comparison.Field, "B")
  1356  	assert.Equal(t, filterNode.Right.Left.Comparison.Op, "=")
  1357  	assert.Equal(t, filterNode.Right.Left.Comparison.Values, json.Number("2"))
  1358  
  1359  	assert.Equal(t, filterNode.Right.Right.Left.NodeType, ast.NodeTerminal)
  1360  	assert.Equal(t, filterNode.Right.Right.Left.Comparison.Field, "C")
  1361  	assert.Equal(t, filterNode.Right.Right.Left.Comparison.Op, "=")
  1362  	assert.Equal(t, filterNode.Right.Right.Left.Comparison.Values, json.Number("3"))
  1363  
  1364  	assert.Equal(t, filterNode.Right.Right.Right.NodeType, ast.NodeTerminal)
  1365  	assert.Equal(t, filterNode.Right.Right.Right.Comparison.Field, "D")
  1366  	assert.Equal(t, filterNode.Right.Right.Right.Comparison.Op, "=")
  1367  	assert.Equal(t, filterNode.Right.Right.Right.Comparison.Values, json.Number("4"))
  1368  
  1369  	astNode := &structs.ASTNode{}
  1370  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1371  	assert.Nil(t, err)
  1372  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1373  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1374  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1375  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1376  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1377  
  1378  	assert.Len(t, astNode.AndFilterCondition.NestedNodes, 1)
  1379  	andFilter := astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition
  1380  	assert.Len(t, andFilter.FilterCriteria, 1)
  1381  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "B")
  1382  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1383  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(2))
  1384  
  1385  	assert.Len(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.NestedNodes, 1)
  1386  	andFilter = astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.NestedNodes[0].AndFilterCondition
  1387  	assert.Len(t, andFilter.FilterCriteria, 2)
  1388  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "C")
  1389  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1390  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(3))
  1391  	assert.Equal(t, andFilter.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "D")
  1392  	assert.Equal(t, andFilter.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
  1393  	assert.Equal(t, andFilter.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(4))
  1394  }
  1395  
  1396  func Test_manyChainedSearchOptionalPipeSpacing(t *testing.T) {
  1397  	// This should be equivalent to `search A=1 AND (B=2 AND (C=3 AND D=4))`
  1398  	query := []byte(`search A=1| search B=apple|search C=3 |search D=4`)
  1399  	res, err := spl.Parse("", query)
  1400  	assert.Nil(t, err)
  1401  	filterNode := res.(ast.QueryStruct).SearchFilter
  1402  
  1403  	assert.NotNil(t, filterNode)
  1404  	assert.Equal(t, ast.NodeAnd, filterNode.NodeType)
  1405  	assert.Equal(t, ast.NodeAnd, filterNode.Right.NodeType)
  1406  	assert.Equal(t, ast.NodeAnd, filterNode.Right.Right.NodeType)
  1407  
  1408  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
  1409  	assert.Equal(t, filterNode.Left.Comparison.Field, "A")
  1410  	assert.Equal(t, filterNode.Left.Comparison.Op, "=")
  1411  	assert.Equal(t, filterNode.Left.Comparison.Values, json.Number("1"))
  1412  
  1413  	assert.Equal(t, filterNode.Right.Left.NodeType, ast.NodeTerminal)
  1414  	assert.Equal(t, filterNode.Right.Left.Comparison.Field, "B")
  1415  	assert.Equal(t, filterNode.Right.Left.Comparison.Op, "=")
  1416  	assert.Equal(t, filterNode.Right.Left.Comparison.Values, `"apple"`)
  1417  
  1418  	assert.Equal(t, filterNode.Right.Right.Left.NodeType, ast.NodeTerminal)
  1419  	assert.Equal(t, filterNode.Right.Right.Left.Comparison.Field, "C")
  1420  	assert.Equal(t, filterNode.Right.Right.Left.Comparison.Op, "=")
  1421  	assert.Equal(t, filterNode.Right.Right.Left.Comparison.Values, json.Number("3"))
  1422  
  1423  	assert.Equal(t, filterNode.Right.Right.Right.NodeType, ast.NodeTerminal)
  1424  	assert.Equal(t, filterNode.Right.Right.Right.Comparison.Field, "D")
  1425  	assert.Equal(t, filterNode.Right.Right.Right.Comparison.Op, "=")
  1426  	assert.Equal(t, filterNode.Right.Right.Right.Comparison.Values, json.Number("4"))
  1427  
  1428  	astNode := &structs.ASTNode{}
  1429  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1430  	assert.Nil(t, err)
  1431  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1432  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1433  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1434  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1435  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1436  
  1437  	assert.Len(t, astNode.AndFilterCondition.NestedNodes, 1)
  1438  	andFilter := astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition
  1439  	assert.Len(t, andFilter.FilterCriteria, 1)
  1440  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "B")
  1441  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1442  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal, "apple")
  1443  
  1444  	assert.Len(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.NestedNodes, 1)
  1445  	andFilter = astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.NestedNodes[0].AndFilterCondition
  1446  	assert.Len(t, andFilter.FilterCriteria, 2)
  1447  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "C")
  1448  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1449  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(3))
  1450  	assert.Equal(t, andFilter.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "D")
  1451  	assert.Equal(t, andFilter.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
  1452  	assert.Equal(t, andFilter.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(4))
  1453  }
  1454  
  1455  func Test_manyChainedCompoundSearch(t *testing.T) {
  1456  	// This should be equivalent to `search A=1 AND ((B=2 AND C=3) AND ((D=4 OR E=5) AND F=6))`
  1457  	query := []byte(`search A=1 | search B=2 C=3 | search D=4 OR E=5 | search F=6`)
  1458  	res, err := spl.Parse("", query)
  1459  	assert.Nil(t, err)
  1460  	filterNode := res.(ast.QueryStruct).SearchFilter
  1461  
  1462  	assert.NotNil(t, filterNode)
  1463  	assert.Equal(t, ast.NodeAnd, filterNode.NodeType)
  1464  	assert.Equal(t, ast.NodeAnd, filterNode.Right.NodeType)
  1465  	assert.Equal(t, ast.NodeAnd, filterNode.Right.Left.NodeType)
  1466  	assert.Equal(t, ast.NodeAnd, filterNode.Right.Right.NodeType)
  1467  	assert.Equal(t, ast.NodeOr, filterNode.Right.Right.Left.NodeType)
  1468  
  1469  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
  1470  	assert.Equal(t, filterNode.Left.Comparison.Field, "A")
  1471  	assert.Equal(t, filterNode.Left.Comparison.Op, "=")
  1472  	assert.Equal(t, filterNode.Left.Comparison.Values, json.Number("1"))
  1473  
  1474  	assert.Equal(t, filterNode.Right.Left.Left.NodeType, ast.NodeTerminal)
  1475  	assert.Equal(t, filterNode.Right.Left.Left.Comparison.Field, "B")
  1476  	assert.Equal(t, filterNode.Right.Left.Left.Comparison.Op, "=")
  1477  	assert.Equal(t, filterNode.Right.Left.Left.Comparison.Values, json.Number("2"))
  1478  
  1479  	assert.Equal(t, filterNode.Right.Left.Right.NodeType, ast.NodeTerminal)
  1480  	assert.Equal(t, filterNode.Right.Left.Right.Comparison.Field, "C")
  1481  	assert.Equal(t, filterNode.Right.Left.Right.Comparison.Op, "=")
  1482  	assert.Equal(t, filterNode.Right.Left.Right.Comparison.Values, json.Number("3"))
  1483  
  1484  	assert.Equal(t, filterNode.Right.Right.Left.Left.NodeType, ast.NodeTerminal)
  1485  	assert.Equal(t, filterNode.Right.Right.Left.Left.Comparison.Field, "D")
  1486  	assert.Equal(t, filterNode.Right.Right.Left.Left.Comparison.Op, "=")
  1487  	assert.Equal(t, filterNode.Right.Right.Left.Left.Comparison.Values, json.Number("4"))
  1488  
  1489  	assert.Equal(t, filterNode.Right.Right.Left.Right.NodeType, ast.NodeTerminal)
  1490  	assert.Equal(t, filterNode.Right.Right.Left.Right.Comparison.Field, "E")
  1491  	assert.Equal(t, filterNode.Right.Right.Left.Right.Comparison.Op, "=")
  1492  	assert.Equal(t, filterNode.Right.Right.Left.Right.Comparison.Values, json.Number("5"))
  1493  
  1494  	assert.Equal(t, filterNode.Right.Right.Right.NodeType, ast.NodeTerminal)
  1495  	assert.Equal(t, filterNode.Right.Right.Right.Comparison.Field, "F")
  1496  	assert.Equal(t, filterNode.Right.Right.Right.Comparison.Op, "=")
  1497  	assert.Equal(t, filterNode.Right.Right.Right.Comparison.Values, json.Number("6"))
  1498  
  1499  	astNode := &structs.ASTNode{}
  1500  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1501  	assert.Nil(t, err)
  1502  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1503  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1504  	andFilter := astNode.AndFilterCondition
  1505  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1506  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1507  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1508  
  1509  	assert.Len(t, astNode.AndFilterCondition.NestedNodes, 1)
  1510  	assert.Len(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.NestedNodes, 2)
  1511  	andFilter = astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.NestedNodes[0].AndFilterCondition
  1512  	assert.Len(t, andFilter.FilterCriteria, 2)
  1513  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "B")
  1514  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1515  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(2))
  1516  	assert.Equal(t, andFilter.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "C")
  1517  	assert.Equal(t, andFilter.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
  1518  	assert.Equal(t, andFilter.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(3))
  1519  
  1520  	andFilter = astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.NestedNodes[1].AndFilterCondition
  1521  	assert.Len(t, andFilter.NestedNodes, 1)
  1522  	assert.Equal(t, andFilter.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "D")
  1523  	assert.Equal(t, andFilter.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1524  	assert.Equal(t, andFilter.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(4))
  1525  	assert.Equal(t, andFilter.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "E")
  1526  	assert.Equal(t, andFilter.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
  1527  	assert.Equal(t, andFilter.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(5))
  1528  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "F")
  1529  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1530  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(6))
  1531  }
  1532  
  1533  func Test_searchBlockWithoutUsingSearchKeyword(t *testing.T) {
  1534  	// This should be equivalent to `search A=1 AND ((B=2 AND C=3) AND ((D=4 OR E=5) AND F=6))`
  1535  	query := []byte(`search A=1 | B=2 C=3 | search D=4 OR E=5 | F=6`)
  1536  	res, err := spl.Parse("", query)
  1537  	assert.Nil(t, err)
  1538  	filterNode := res.(ast.QueryStruct).SearchFilter
  1539  
  1540  	assert.NotNil(t, filterNode)
  1541  	assert.Equal(t, ast.NodeAnd, filterNode.NodeType)
  1542  	assert.Equal(t, ast.NodeAnd, filterNode.Right.NodeType)
  1543  	assert.Equal(t, ast.NodeAnd, filterNode.Right.Left.NodeType)
  1544  	assert.Equal(t, ast.NodeAnd, filterNode.Right.Right.NodeType)
  1545  	assert.Equal(t, ast.NodeOr, filterNode.Right.Right.Left.NodeType)
  1546  
  1547  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
  1548  	assert.Equal(t, filterNode.Left.Comparison.Field, "A")
  1549  	assert.Equal(t, filterNode.Left.Comparison.Op, "=")
  1550  	assert.Equal(t, filterNode.Left.Comparison.Values, json.Number("1"))
  1551  
  1552  	assert.Equal(t, filterNode.Right.Left.Left.NodeType, ast.NodeTerminal)
  1553  	assert.Equal(t, filterNode.Right.Left.Left.Comparison.Field, "B")
  1554  	assert.Equal(t, filterNode.Right.Left.Left.Comparison.Op, "=")
  1555  	assert.Equal(t, filterNode.Right.Left.Left.Comparison.Values, json.Number("2"))
  1556  
  1557  	assert.Equal(t, filterNode.Right.Left.Right.NodeType, ast.NodeTerminal)
  1558  	assert.Equal(t, filterNode.Right.Left.Right.Comparison.Field, "C")
  1559  	assert.Equal(t, filterNode.Right.Left.Right.Comparison.Op, "=")
  1560  	assert.Equal(t, filterNode.Right.Left.Right.Comparison.Values, json.Number("3"))
  1561  
  1562  	assert.Equal(t, filterNode.Right.Right.Left.Left.NodeType, ast.NodeTerminal)
  1563  	assert.Equal(t, filterNode.Right.Right.Left.Left.Comparison.Field, "D")
  1564  	assert.Equal(t, filterNode.Right.Right.Left.Left.Comparison.Op, "=")
  1565  	assert.Equal(t, filterNode.Right.Right.Left.Left.Comparison.Values, json.Number("4"))
  1566  
  1567  	assert.Equal(t, filterNode.Right.Right.Left.Right.NodeType, ast.NodeTerminal)
  1568  	assert.Equal(t, filterNode.Right.Right.Left.Right.Comparison.Field, "E")
  1569  	assert.Equal(t, filterNode.Right.Right.Left.Right.Comparison.Op, "=")
  1570  	assert.Equal(t, filterNode.Right.Right.Left.Right.Comparison.Values, json.Number("5"))
  1571  
  1572  	assert.Equal(t, filterNode.Right.Right.Right.NodeType, ast.NodeTerminal)
  1573  	assert.Equal(t, filterNode.Right.Right.Right.Comparison.Field, "F")
  1574  	assert.Equal(t, filterNode.Right.Right.Right.Comparison.Op, "=")
  1575  	assert.Equal(t, filterNode.Right.Right.Right.Comparison.Values, json.Number("6"))
  1576  
  1577  	astNode := &structs.ASTNode{}
  1578  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1579  	assert.Nil(t, err)
  1580  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1581  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1582  	andFilter := astNode.AndFilterCondition
  1583  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1584  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1585  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1586  
  1587  	assert.Len(t, astNode.AndFilterCondition.NestedNodes, 1)
  1588  	assert.Len(t, astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.NestedNodes, 2)
  1589  	andFilter = astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.NestedNodes[0].AndFilterCondition
  1590  	assert.Len(t, andFilter.FilterCriteria, 2)
  1591  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "B")
  1592  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1593  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(2))
  1594  	assert.Equal(t, andFilter.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "C")
  1595  	assert.Equal(t, andFilter.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
  1596  	assert.Equal(t, andFilter.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(3))
  1597  
  1598  	andFilter = astNode.AndFilterCondition.NestedNodes[0].AndFilterCondition.NestedNodes[1].AndFilterCondition
  1599  	assert.Len(t, andFilter.NestedNodes, 1)
  1600  	assert.Equal(t, andFilter.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "D")
  1601  	assert.Equal(t, andFilter.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1602  	assert.Equal(t, andFilter.NestedNodes[0].OrFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(4))
  1603  	assert.Equal(t, andFilter.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "E")
  1604  	assert.Equal(t, andFilter.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
  1605  	assert.Equal(t, andFilter.NestedNodes[0].OrFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(5))
  1606  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "F")
  1607  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1608  	assert.Equal(t, andFilter.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(6))
  1609  }
  1610  
  1611  func Test_regexSingleColumnEquals(t *testing.T) {
  1612  	query := []byte(`A=1 | regex B="^\d$"`)
  1613  	res, err := spl.Parse("", query)
  1614  	assert.Nil(t, err)
  1615  	filterNode := res.(ast.QueryStruct).SearchFilter
  1616  
  1617  	assert.NotNil(t, filterNode)
  1618  	assert.Equal(t, ast.NodeAnd, filterNode.NodeType)
  1619  
  1620  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
  1621  	assert.Equal(t, filterNode.Left.Comparison.Field, "A")
  1622  	assert.Equal(t, filterNode.Left.Comparison.Op, "=")
  1623  	assert.Equal(t, filterNode.Left.Comparison.Values, json.Number("1"))
  1624  
  1625  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeTerminal)
  1626  	assert.Equal(t, filterNode.Right.Comparison.Field, "B")
  1627  	assert.Equal(t, filterNode.Right.Comparison.Op, "=")
  1628  	assert.Equal(t, filterNode.Right.Comparison.Values, `^\d$`)
  1629  	assert.Equal(t, filterNode.Right.Comparison.ValueIsRegex, true)
  1630  
  1631  	astNode := &structs.ASTNode{}
  1632  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1633  	assert.Nil(t, err)
  1634  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1635  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 2)
  1636  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1637  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1638  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1639  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "B")
  1640  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
  1641  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal, `^\d$`)
  1642  
  1643  	compiledRegex, err := regexp.Compile(`^\d$`)
  1644  	assert.Nil(t, err)
  1645  	assert.NotNil(t, compiledRegex)
  1646  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.GetRegexp(), compiledRegex)
  1647  }
  1648  
  1649  func Test_regexSingleColumnNotEquals(t *testing.T) {
  1650  	query := []byte(`A=1 | regex B!="^\d$"`)
  1651  	res, err := spl.Parse("", query)
  1652  	assert.Nil(t, err)
  1653  	filterNode := res.(ast.QueryStruct).SearchFilter
  1654  
  1655  	assert.NotNil(t, filterNode)
  1656  	assert.Equal(t, ast.NodeAnd, filterNode.NodeType)
  1657  
  1658  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
  1659  	assert.Equal(t, filterNode.Left.Comparison.Field, "A")
  1660  	assert.Equal(t, filterNode.Left.Comparison.Op, "=")
  1661  	assert.Equal(t, filterNode.Left.Comparison.Values, json.Number("1"))
  1662  
  1663  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeTerminal)
  1664  	assert.Equal(t, filterNode.Right.Comparison.Field, "B")
  1665  	assert.Equal(t, filterNode.Right.Comparison.Op, "!=")
  1666  	assert.Equal(t, filterNode.Right.Comparison.Values, `^\d$`)
  1667  	assert.Equal(t, filterNode.Right.Comparison.ValueIsRegex, true)
  1668  
  1669  	astNode := &structs.ASTNode{}
  1670  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1671  	assert.Nil(t, err)
  1672  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1673  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 2)
  1674  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1675  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1676  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1677  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "B")
  1678  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.NotEquals)
  1679  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal, `^\d$`)
  1680  
  1681  	compiledRegex, err := regexp.Compile(`^\d$`)
  1682  	assert.Nil(t, err)
  1683  	assert.NotNil(t, compiledRegex)
  1684  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.GetRegexp(), compiledRegex)
  1685  }
  1686  
  1687  func Test_regexRequiresQuotes(t *testing.T) {
  1688  	query := []byte(`A=1 | regex B=^\d$`)
  1689  	res, err := spl.Parse("", query)
  1690  	assert.NotNil(t, err)
  1691  	assert.Nil(t, res)
  1692  }
  1693  
  1694  func Test_regexAnyColumn(t *testing.T) {
  1695  	query := []byte(`A=1 | regex "^\d$"`)
  1696  	res, err := spl.Parse("", query)
  1697  	assert.Nil(t, err)
  1698  	filterNode := res.(ast.QueryStruct).SearchFilter
  1699  
  1700  	assert.NotNil(t, filterNode)
  1701  	assert.Equal(t, ast.NodeAnd, filterNode.NodeType)
  1702  
  1703  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
  1704  	assert.Equal(t, filterNode.Left.Comparison.Field, "A")
  1705  	assert.Equal(t, filterNode.Left.Comparison.Op, "=")
  1706  	assert.Equal(t, filterNode.Left.Comparison.Values, json.Number("1"))
  1707  
  1708  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeTerminal)
  1709  	assert.Equal(t, filterNode.Right.Comparison.Field, "*")
  1710  	assert.Equal(t, filterNode.Right.Comparison.Op, "=")
  1711  	assert.Equal(t, filterNode.Right.Comparison.Values, `^\d$`)
  1712  	assert.Equal(t, filterNode.Right.Comparison.ValueIsRegex, true)
  1713  
  1714  	astNode := &structs.ASTNode{}
  1715  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  1716  	assert.Nil(t, err)
  1717  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  1718  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 2)
  1719  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1720  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1721  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1722  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "*")
  1723  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
  1724  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal, `^\d$`)
  1725  
  1726  	compiledRegex, err := regexp.Compile(`^\d$`)
  1727  	assert.Nil(t, err)
  1728  	assert.NotNil(t, compiledRegex)
  1729  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.GetRegexp(), compiledRegex)
  1730  }
  1731  
  1732  func Test_aggCountWithField(t *testing.T) {
  1733  	query := []byte(`search A=1 | stats count(city)`)
  1734  	res, err := spl.Parse("", query)
  1735  	assert.Nil(t, err)
  1736  	filterNode := res.(ast.QueryStruct).SearchFilter
  1737  	assert.NotNil(t, filterNode)
  1738  
  1739  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1740  	assert.Equal(t, filterNode.Comparison.Field, "A")
  1741  	assert.Equal(t, filterNode.Comparison.Op, "=")
  1742  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  1743  
  1744  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  1745  	assert.NotNil(t, pipeCommands)
  1746  	assert.Equal(t, pipeCommands.PipeCommandType, structs.MeasureAggsType)
  1747  	assert.Len(t, pipeCommands.MeasureOperations, 1)
  1748  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureCol, "city")
  1749  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureFunc, utils.Count)
  1750  
  1751  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  1752  	assert.Nil(t, err)
  1753  	assert.NotNil(t, astNode)
  1754  	assert.NotNil(t, aggregator)
  1755  
  1756  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1757  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1758  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1759  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1760  
  1761  	assert.Equal(t, aggregator.PipeCommandType, structs.MeasureAggsType)
  1762  	assert.Len(t, aggregator.MeasureOperations, 1)
  1763  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureCol, "city")
  1764  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureFunc, utils.Count)
  1765  }
  1766  
  1767  func Test_aggCountWithoutField(t *testing.T) {
  1768  	query := []byte(`search A=1 | stats count`)
  1769  	res, err := spl.Parse("", query)
  1770  	assert.Nil(t, err)
  1771  	filterNode := res.(ast.QueryStruct).SearchFilter
  1772  	assert.NotNil(t, filterNode)
  1773  
  1774  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1775  	assert.Equal(t, filterNode.Comparison.Field, "A")
  1776  	assert.Equal(t, filterNode.Comparison.Op, "=")
  1777  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  1778  
  1779  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  1780  	assert.NotNil(t, pipeCommands)
  1781  	assert.Equal(t, pipeCommands.PipeCommandType, structs.MeasureAggsType)
  1782  	assert.Len(t, pipeCommands.MeasureOperations, 1)
  1783  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureCol, "*")
  1784  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureFunc, utils.Count)
  1785  
  1786  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  1787  	assert.Nil(t, err)
  1788  	assert.NotNil(t, astNode)
  1789  	assert.NotNil(t, aggregator)
  1790  
  1791  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1792  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1793  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1794  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1795  
  1796  	assert.Equal(t, aggregator.PipeCommandType, structs.MeasureAggsType)
  1797  	assert.Len(t, aggregator.MeasureOperations, 1)
  1798  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureCol, "*")
  1799  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureFunc, utils.Count)
  1800  }
  1801  
  1802  func Test_aggCountAlias(t *testing.T) {
  1803  	query := []byte(`search A=1 | stats c(city)`)
  1804  	res, err := spl.Parse("", query)
  1805  	assert.Nil(t, err)
  1806  	filterNode := res.(ast.QueryStruct).SearchFilter
  1807  	assert.NotNil(t, filterNode)
  1808  
  1809  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1810  	assert.Equal(t, filterNode.Comparison.Field, "A")
  1811  	assert.Equal(t, filterNode.Comparison.Op, "=")
  1812  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  1813  
  1814  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  1815  	assert.NotNil(t, pipeCommands)
  1816  	assert.Equal(t, pipeCommands.PipeCommandType, structs.MeasureAggsType)
  1817  	assert.Len(t, pipeCommands.MeasureOperations, 1)
  1818  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureCol, "city")
  1819  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureFunc, utils.Count)
  1820  
  1821  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  1822  	assert.Nil(t, err)
  1823  	assert.NotNil(t, astNode)
  1824  	assert.NotNil(t, aggregator)
  1825  
  1826  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1827  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1828  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1829  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1830  
  1831  	assert.Equal(t, aggregator.PipeCommandType, structs.MeasureAggsType)
  1832  	assert.Len(t, aggregator.MeasureOperations, 1)
  1833  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureCol, "city")
  1834  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureFunc, utils.Count)
  1835  }
  1836  
  1837  func Test_aggDistinctCount(t *testing.T) {
  1838  	query := []byte(`search A=1 | stats distinct_count(city)`)
  1839  	res, err := spl.Parse("", query)
  1840  	assert.Nil(t, err)
  1841  	filterNode := res.(ast.QueryStruct).SearchFilter
  1842  	assert.NotNil(t, filterNode)
  1843  
  1844  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1845  	assert.Equal(t, filterNode.Comparison.Field, "A")
  1846  	assert.Equal(t, filterNode.Comparison.Op, "=")
  1847  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  1848  
  1849  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  1850  	assert.NotNil(t, pipeCommands)
  1851  	assert.Equal(t, pipeCommands.PipeCommandType, structs.MeasureAggsType)
  1852  	assert.Len(t, pipeCommands.MeasureOperations, 1)
  1853  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureCol, "city")
  1854  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureFunc, utils.Cardinality)
  1855  
  1856  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  1857  	assert.Nil(t, err)
  1858  	assert.NotNil(t, astNode)
  1859  	assert.NotNil(t, aggregator)
  1860  
  1861  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1862  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1863  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1864  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1865  
  1866  	assert.Equal(t, aggregator.PipeCommandType, structs.MeasureAggsType)
  1867  	assert.Len(t, aggregator.MeasureOperations, 1)
  1868  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureCol, "city")
  1869  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureFunc, utils.Cardinality)
  1870  }
  1871  
  1872  func Test_aggDistinctCountAlias(t *testing.T) {
  1873  	query := []byte(`search A=1 | stats dc(city)`)
  1874  	res, err := spl.Parse("", query)
  1875  	assert.Nil(t, err)
  1876  	filterNode := res.(ast.QueryStruct).SearchFilter
  1877  	assert.NotNil(t, filterNode)
  1878  
  1879  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1880  	assert.Equal(t, filterNode.Comparison.Field, "A")
  1881  	assert.Equal(t, filterNode.Comparison.Op, "=")
  1882  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  1883  
  1884  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  1885  	assert.NotNil(t, pipeCommands)
  1886  	assert.Equal(t, pipeCommands.PipeCommandType, structs.MeasureAggsType)
  1887  	assert.Len(t, pipeCommands.MeasureOperations, 1)
  1888  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureCol, "city")
  1889  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureFunc, utils.Cardinality)
  1890  
  1891  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  1892  	assert.Nil(t, err)
  1893  	assert.NotNil(t, astNode)
  1894  	assert.NotNil(t, aggregator)
  1895  
  1896  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1897  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1898  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1899  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1900  
  1901  	assert.Equal(t, aggregator.PipeCommandType, structs.MeasureAggsType)
  1902  	assert.Len(t, aggregator.MeasureOperations, 1)
  1903  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureCol, "city")
  1904  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureFunc, utils.Cardinality)
  1905  }
  1906  
  1907  func Test_aggAvg(t *testing.T) {
  1908  	query := []byte(`search A=1 | stats avg(latency)`)
  1909  	res, err := spl.Parse("", query)
  1910  	assert.Nil(t, err)
  1911  	filterNode := res.(ast.QueryStruct).SearchFilter
  1912  	assert.NotNil(t, filterNode)
  1913  
  1914  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1915  	assert.Equal(t, filterNode.Comparison.Field, "A")
  1916  	assert.Equal(t, filterNode.Comparison.Op, "=")
  1917  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  1918  
  1919  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  1920  	assert.NotNil(t, pipeCommands)
  1921  	assert.Equal(t, pipeCommands.PipeCommandType, structs.MeasureAggsType)
  1922  	assert.Len(t, pipeCommands.MeasureOperations, 1)
  1923  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureCol, "latency")
  1924  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureFunc, utils.Avg)
  1925  
  1926  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  1927  	assert.Nil(t, err)
  1928  	assert.NotNil(t, astNode)
  1929  	assert.NotNil(t, aggregator)
  1930  
  1931  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1932  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1933  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1934  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1935  
  1936  	assert.Equal(t, aggregator.PipeCommandType, structs.MeasureAggsType)
  1937  	assert.Len(t, aggregator.MeasureOperations, 1)
  1938  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureCol, "latency")
  1939  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureFunc, utils.Avg)
  1940  }
  1941  
  1942  func Test_aggMin(t *testing.T) {
  1943  	query := []byte(`search A=1 | stats min(latency)`)
  1944  	res, err := spl.Parse("", query)
  1945  	assert.Nil(t, err)
  1946  	filterNode := res.(ast.QueryStruct).SearchFilter
  1947  	assert.NotNil(t, filterNode)
  1948  
  1949  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1950  	assert.Equal(t, filterNode.Comparison.Field, "A")
  1951  	assert.Equal(t, filterNode.Comparison.Op, "=")
  1952  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  1953  
  1954  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  1955  	assert.NotNil(t, pipeCommands)
  1956  	assert.Equal(t, pipeCommands.PipeCommandType, structs.MeasureAggsType)
  1957  	assert.Len(t, pipeCommands.MeasureOperations, 1)
  1958  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureCol, "latency")
  1959  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureFunc, utils.Min)
  1960  
  1961  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  1962  	assert.Nil(t, err)
  1963  	assert.NotNil(t, astNode)
  1964  	assert.NotNil(t, aggregator)
  1965  
  1966  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  1967  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  1968  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  1969  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  1970  
  1971  	assert.Equal(t, aggregator.PipeCommandType, structs.MeasureAggsType)
  1972  	assert.Len(t, aggregator.MeasureOperations, 1)
  1973  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureCol, "latency")
  1974  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureFunc, utils.Min)
  1975  }
  1976  
  1977  func Test_aggMax(t *testing.T) {
  1978  	query := []byte(`search A=1 | stats max(latency)`)
  1979  	res, err := spl.Parse("", query)
  1980  	assert.Nil(t, err)
  1981  	filterNode := res.(ast.QueryStruct).SearchFilter
  1982  	assert.NotNil(t, filterNode)
  1983  
  1984  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  1985  	assert.Equal(t, filterNode.Comparison.Field, "A")
  1986  	assert.Equal(t, filterNode.Comparison.Op, "=")
  1987  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  1988  
  1989  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  1990  	assert.NotNil(t, pipeCommands)
  1991  	assert.Equal(t, pipeCommands.PipeCommandType, structs.MeasureAggsType)
  1992  	assert.Len(t, pipeCommands.MeasureOperations, 1)
  1993  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureCol, "latency")
  1994  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureFunc, utils.Max)
  1995  
  1996  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  1997  	assert.Nil(t, err)
  1998  	assert.NotNil(t, astNode)
  1999  	assert.NotNil(t, aggregator)
  2000  
  2001  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2002  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2003  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2004  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2005  
  2006  	assert.Equal(t, aggregator.PipeCommandType, structs.MeasureAggsType)
  2007  	assert.Len(t, aggregator.MeasureOperations, 1)
  2008  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureCol, "latency")
  2009  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureFunc, utils.Max)
  2010  }
  2011  
  2012  func Test_aggRange(t *testing.T) {
  2013  	query := []byte(`search A=1 | stats range(latency)`)
  2014  	res, err := spl.Parse("", query)
  2015  	assert.Nil(t, err)
  2016  	filterNode := res.(ast.QueryStruct).SearchFilter
  2017  	assert.NotNil(t, filterNode)
  2018  
  2019  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2020  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2021  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2022  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2023  
  2024  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  2025  	assert.NotNil(t, pipeCommands)
  2026  	assert.Equal(t, pipeCommands.PipeCommandType, structs.MeasureAggsType)
  2027  	assert.Len(t, pipeCommands.MeasureOperations, 1)
  2028  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureCol, "latency")
  2029  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureFunc, utils.Range)
  2030  
  2031  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2032  	assert.Nil(t, err)
  2033  	assert.NotNil(t, astNode)
  2034  	assert.NotNil(t, aggregator)
  2035  
  2036  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2037  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2038  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2039  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2040  
  2041  	assert.Equal(t, aggregator.PipeCommandType, structs.MeasureAggsType)
  2042  	assert.Len(t, aggregator.MeasureOperations, 1)
  2043  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureCol, "latency")
  2044  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureFunc, utils.Range)
  2045  }
  2046  
  2047  func Test_aggValues(t *testing.T) {
  2048  	query := []byte(`search A=1 | stats values(latency)`)
  2049  	res, err := spl.Parse("", query)
  2050  	assert.Nil(t, err)
  2051  	filterNode := res.(ast.QueryStruct).SearchFilter
  2052  	assert.NotNil(t, filterNode)
  2053  
  2054  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2055  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2056  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2057  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2058  
  2059  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  2060  	assert.NotNil(t, pipeCommands)
  2061  	assert.Equal(t, pipeCommands.PipeCommandType, structs.MeasureAggsType)
  2062  	assert.Len(t, pipeCommands.MeasureOperations, 1)
  2063  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureCol, "latency")
  2064  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureFunc, utils.Values)
  2065  
  2066  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2067  	assert.Nil(t, err)
  2068  	assert.NotNil(t, astNode)
  2069  	assert.NotNil(t, aggregator)
  2070  
  2071  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2072  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2073  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2074  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2075  
  2076  	assert.Equal(t, aggregator.PipeCommandType, structs.MeasureAggsType)
  2077  	assert.Len(t, aggregator.MeasureOperations, 1)
  2078  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureCol, "latency")
  2079  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureFunc, utils.Values)
  2080  }
  2081  
  2082  func Test_aggSum(t *testing.T) {
  2083  	query := []byte(`search A=1 | stats sum(latency)`)
  2084  	res, err := spl.Parse("", query)
  2085  	assert.Nil(t, err)
  2086  	filterNode := res.(ast.QueryStruct).SearchFilter
  2087  	assert.NotNil(t, filterNode)
  2088  
  2089  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2090  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2091  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2092  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2093  
  2094  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  2095  	assert.NotNil(t, pipeCommands)
  2096  	assert.Equal(t, pipeCommands.PipeCommandType, structs.MeasureAggsType)
  2097  	assert.Len(t, pipeCommands.MeasureOperations, 1)
  2098  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureCol, "latency")
  2099  	assert.Equal(t, pipeCommands.MeasureOperations[0].MeasureFunc, utils.Sum)
  2100  
  2101  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2102  	assert.Nil(t, err)
  2103  	assert.NotNil(t, astNode)
  2104  	assert.NotNil(t, aggregator)
  2105  
  2106  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2107  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2108  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2109  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2110  
  2111  	assert.Equal(t, aggregator.PipeCommandType, structs.MeasureAggsType)
  2112  	assert.Len(t, aggregator.MeasureOperations, 1)
  2113  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureCol, "latency")
  2114  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureFunc, utils.Sum)
  2115  }
  2116  
  2117  func Test_groupbyOneField(t *testing.T) {
  2118  	query := []byte(`search A=1 | stats avg(latency) BY http_status`)
  2119  	res, err := spl.Parse("", query)
  2120  	assert.Nil(t, err)
  2121  	filterNode := res.(ast.QueryStruct).SearchFilter
  2122  	assert.NotNil(t, filterNode)
  2123  
  2124  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2125  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2126  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2127  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2128  
  2129  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  2130  	assert.NotNil(t, pipeCommands)
  2131  	assert.Equal(t, pipeCommands.PipeCommandType, structs.GroupByType)
  2132  	assert.Len(t, pipeCommands.GroupByRequest.MeasureOperations, 1)
  2133  	assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[0].MeasureCol, "latency")
  2134  	assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[0].MeasureFunc, utils.Avg)
  2135  	assert.Len(t, pipeCommands.GroupByRequest.GroupByColumns, 1)
  2136  	assert.Equal(t, pipeCommands.GroupByRequest.GroupByColumns[0], "http_status")
  2137  	assert.Equal(t, pipeCommands.BucketLimit, segquery.MAX_GRP_BUCKS)
  2138  
  2139  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2140  	assert.Nil(t, err)
  2141  	assert.NotNil(t, astNode)
  2142  	assert.NotNil(t, aggregator)
  2143  
  2144  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2145  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2146  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2147  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2148  
  2149  	assert.Equal(t, aggregator.PipeCommandType, structs.GroupByType)
  2150  	assert.Len(t, aggregator.GroupByRequest.MeasureOperations, 1)
  2151  	assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[0].MeasureCol, "latency")
  2152  	assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[0].MeasureFunc, utils.Avg)
  2153  	assert.Len(t, aggregator.GroupByRequest.GroupByColumns, 1)
  2154  	assert.Equal(t, aggregator.GroupByRequest.GroupByColumns[0], "http_status")
  2155  	assert.Equal(t, aggregator.BucketLimit, segquery.MAX_GRP_BUCKS)
  2156  }
  2157  
  2158  func Test_groupbyManyFields(t *testing.T) {
  2159  	query := []byte(`search A=1 | stats avg(latency) BY http_status, weekday, city`)
  2160  	res, err := spl.Parse("", query)
  2161  	assert.Nil(t, err)
  2162  	filterNode := res.(ast.QueryStruct).SearchFilter
  2163  	assert.NotNil(t, filterNode)
  2164  
  2165  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2166  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2167  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2168  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2169  
  2170  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  2171  	assert.NotNil(t, pipeCommands)
  2172  	assert.Equal(t, pipeCommands.PipeCommandType, structs.GroupByType)
  2173  	assert.Len(t, pipeCommands.GroupByRequest.MeasureOperations, 1)
  2174  	assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[0].MeasureCol, "latency")
  2175  	assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[0].MeasureFunc, utils.Avg)
  2176  	assert.Len(t, pipeCommands.GroupByRequest.GroupByColumns, 3)
  2177  	assert.Equal(t, pipeCommands.GroupByRequest.GroupByColumns[0], "http_status")
  2178  	assert.Equal(t, pipeCommands.GroupByRequest.GroupByColumns[1], "weekday")
  2179  	assert.Equal(t, pipeCommands.GroupByRequest.GroupByColumns[2], "city")
  2180  	assert.Equal(t, pipeCommands.BucketLimit, segquery.MAX_GRP_BUCKS)
  2181  
  2182  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2183  	assert.Nil(t, err)
  2184  	assert.NotNil(t, astNode)
  2185  	assert.NotNil(t, aggregator)
  2186  
  2187  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2188  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2189  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2190  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2191  
  2192  	assert.Equal(t, aggregator.PipeCommandType, structs.GroupByType)
  2193  	assert.Len(t, aggregator.GroupByRequest.MeasureOperations, 1)
  2194  	assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[0].MeasureCol, "latency")
  2195  	assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[0].MeasureFunc, utils.Avg)
  2196  	assert.Len(t, aggregator.GroupByRequest.GroupByColumns, 3)
  2197  	assert.Equal(t, aggregator.GroupByRequest.GroupByColumns[0], "http_status")
  2198  	assert.Equal(t, aggregator.GroupByRequest.GroupByColumns[1], "weekday")
  2199  	assert.Equal(t, aggregator.GroupByRequest.GroupByColumns[2], "city")
  2200  	assert.Equal(t, aggregator.BucketLimit, segquery.MAX_GRP_BUCKS)
  2201  }
  2202  
  2203  func Test_timechartHasGroupby(t *testing.T) {
  2204  	queries := []string{
  2205  		`search A=1 | timechart span=1d avg(latency), sum(latitude) BY http_status limit=bottom2`,
  2206  		`search A=1 | timechart avg(latency), sum(latitude) BY http_status span=1d limit=bottom2`,
  2207  	}
  2208  
  2209  	for _, queryStr := range queries {
  2210  		query := []byte(queryStr)
  2211  		res, err := spl.Parse("", query)
  2212  		assert.Nil(t, err)
  2213  		filterNode := res.(ast.QueryStruct).SearchFilter
  2214  		assert.NotNil(t, filterNode)
  2215  
  2216  		assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2217  		assert.Equal(t, filterNode.Comparison.Field, "A")
  2218  		assert.Equal(t, filterNode.Comparison.Op, "=")
  2219  		assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2220  
  2221  		pipeCommands := res.(ast.QueryStruct).PipeCommands
  2222  		assert.NotNil(t, pipeCommands)
  2223  		assert.Equal(t, pipeCommands.PipeCommandType, structs.GroupByType)
  2224  		assert.Len(t, pipeCommands.GroupByRequest.MeasureOperations, 2)
  2225  		assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[0].MeasureCol, "latency")
  2226  		assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[0].MeasureFunc, utils.Avg)
  2227  		assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[1].MeasureCol, "latitude")
  2228  		assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[1].MeasureFunc, utils.Sum)
  2229  		assert.Len(t, pipeCommands.GroupByRequest.GroupByColumns, 1)
  2230  		assert.Equal(t, pipeCommands.GroupByRequest.GroupByColumns[0], "timestamp")
  2231  		assert.Equal(t, pipeCommands.BucketLimit, segquery.MAX_GRP_BUCKS)
  2232  		// Timechart
  2233  		assert.NotNil(t, pipeCommands.TimeHistogram)
  2234  		assert.NotNil(t, pipeCommands.TimeHistogram.Timechart)
  2235  		assert.Equal(t, uint64(86_400_000), pipeCommands.TimeHistogram.IntervalMillis)
  2236  		assert.Equal(t, "http_status", pipeCommands.TimeHistogram.Timechart.ByField)
  2237  		assert.NotNil(t, pipeCommands.TimeHistogram.Timechart.LimitExpr)
  2238  		assert.False(t, pipeCommands.TimeHistogram.Timechart.LimitExpr.IsTop)
  2239  		assert.Equal(t, 2, pipeCommands.TimeHistogram.Timechart.LimitExpr.Num)
  2240  		assert.Equal(t, structs.LSMByFreq, int(pipeCommands.TimeHistogram.Timechart.LimitExpr.LimitScoreMode))
  2241  
  2242  		astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2243  		assert.Nil(t, err)
  2244  		assert.NotNil(t, astNode)
  2245  		assert.NotNil(t, aggregator)
  2246  
  2247  		assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2248  		assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2249  		assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2250  		assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2251  
  2252  		assert.Equal(t, aggregator.PipeCommandType, structs.GroupByType)
  2253  		assert.Len(t, aggregator.GroupByRequest.MeasureOperations, 2)
  2254  		assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[0].MeasureCol, "latency")
  2255  		assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[0].MeasureFunc, utils.Avg)
  2256  		assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[1].MeasureCol, "latitude")
  2257  		assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[1].MeasureFunc, utils.Sum)
  2258  		assert.Len(t, aggregator.GroupByRequest.GroupByColumns, 1)
  2259  		assert.Equal(t, aggregator.GroupByRequest.GroupByColumns[0], "timestamp")
  2260  		assert.Equal(t, aggregator.BucketLimit, segquery.MAX_GRP_BUCKS)
  2261  		// Timechart
  2262  		assert.NotNil(t, aggregator.TimeHistogram)
  2263  		assert.NotNil(t, aggregator.TimeHistogram.Timechart)
  2264  		assert.Equal(t, uint64(86_400_000), aggregator.TimeHistogram.IntervalMillis)
  2265  		assert.Equal(t, "http_status", aggregator.TimeHistogram.Timechart.ByField)
  2266  		assert.NotNil(t, aggregator.TimeHistogram.Timechart.LimitExpr)
  2267  		assert.False(t, aggregator.TimeHistogram.Timechart.LimitExpr.IsTop)
  2268  		assert.Equal(t, 2, aggregator.TimeHistogram.Timechart.LimitExpr.Num)
  2269  		assert.Equal(t, structs.LSMByFreq, int(aggregator.TimeHistogram.Timechart.LimitExpr.LimitScoreMode))
  2270  	}
  2271  }
  2272  
  2273  func Test_timechartWithoutGroupby(t *testing.T) {
  2274  	queries := []string{
  2275  		`search A=1 | timechart span=1hr min(latency), range(longitude)`,
  2276  		`search A=1 | timechart min(latency), range(longitude) span=1hr`,
  2277  	}
  2278  
  2279  	for _, queryStr := range queries {
  2280  		query := []byte(queryStr)
  2281  		res, err := spl.Parse("", query)
  2282  		assert.Nil(t, err)
  2283  		filterNode := res.(ast.QueryStruct).SearchFilter
  2284  		assert.NotNil(t, filterNode)
  2285  
  2286  		assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2287  		assert.Equal(t, filterNode.Comparison.Field, "A")
  2288  		assert.Equal(t, filterNode.Comparison.Op, "=")
  2289  		assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2290  
  2291  		pipeCommands := res.(ast.QueryStruct).PipeCommands
  2292  		assert.NotNil(t, pipeCommands)
  2293  		assert.Equal(t, pipeCommands.PipeCommandType, structs.GroupByType)
  2294  		assert.Len(t, pipeCommands.GroupByRequest.MeasureOperations, 2)
  2295  		assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[0].MeasureCol, "latency")
  2296  		assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[0].MeasureFunc, utils.Min)
  2297  		assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[1].MeasureCol, "longitude")
  2298  		assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[1].MeasureFunc, utils.Range)
  2299  		assert.Len(t, pipeCommands.GroupByRequest.GroupByColumns, 1)
  2300  		assert.Equal(t, pipeCommands.GroupByRequest.GroupByColumns[0], "timestamp")
  2301  		assert.Equal(t, pipeCommands.BucketLimit, segquery.MAX_GRP_BUCKS)
  2302  		// Timechart
  2303  		assert.NotNil(t, pipeCommands.TimeHistogram)
  2304  		assert.NotNil(t, pipeCommands.TimeHistogram.Timechart)
  2305  		assert.Equal(t, uint64(3_600_000), pipeCommands.TimeHistogram.IntervalMillis)
  2306  		assert.Equal(t, "", pipeCommands.TimeHistogram.Timechart.ByField)
  2307  		assert.Nil(t, pipeCommands.TimeHistogram.Timechart.LimitExpr)
  2308  
  2309  		astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2310  		assert.Nil(t, err)
  2311  		assert.NotNil(t, astNode)
  2312  		assert.NotNil(t, aggregator)
  2313  
  2314  		assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2315  		assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2316  		assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2317  		assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2318  
  2319  		assert.Equal(t, aggregator.PipeCommandType, structs.GroupByType)
  2320  		assert.Len(t, aggregator.GroupByRequest.MeasureOperations, 2)
  2321  		assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[0].MeasureCol, "latency")
  2322  		assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[0].MeasureFunc, utils.Min)
  2323  		assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[1].MeasureCol, "longitude")
  2324  		assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[1].MeasureFunc, utils.Range)
  2325  		assert.Len(t, aggregator.GroupByRequest.GroupByColumns, 1)
  2326  		assert.Equal(t, aggregator.GroupByRequest.GroupByColumns[0], "timestamp")
  2327  		assert.Equal(t, aggregator.BucketLimit, segquery.MAX_GRP_BUCKS)
  2328  		// Timechart
  2329  		assert.NotNil(t, aggregator.TimeHistogram)
  2330  		assert.NotNil(t, aggregator.TimeHistogram.Timechart)
  2331  		assert.Equal(t, uint64(3_600_000), aggregator.TimeHistogram.IntervalMillis)
  2332  		assert.Equal(t, "", aggregator.TimeHistogram.Timechart.ByField)
  2333  		assert.Nil(t, aggregator.TimeHistogram.Timechart.LimitExpr)
  2334  	}
  2335  }
  2336  
  2337  func Test_timechartWithoutGroupBy(t *testing.T) {
  2338  	queries := []string{
  2339  		`search A=1 | timechart span=1d avg(latency), sum(latitude) BY http_status`,
  2340  		`search A=1 | timechart avg(latency), sum(latitude) BY http_status span=1d`,
  2341  	}
  2342  	for _, queryStr := range queries {
  2343  		query := []byte(queryStr)
  2344  		res, err := spl.Parse("", query)
  2345  		assert.Nil(t, err)
  2346  		filterNode := res.(ast.QueryStruct).SearchFilter
  2347  		assert.NotNil(t, filterNode)
  2348  
  2349  		assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2350  		assert.Equal(t, filterNode.Comparison.Field, "A")
  2351  		assert.Equal(t, filterNode.Comparison.Op, "=")
  2352  		assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2353  
  2354  		pipeCommands := res.(ast.QueryStruct).PipeCommands
  2355  		assert.NotNil(t, pipeCommands)
  2356  		assert.Equal(t, pipeCommands.PipeCommandType, structs.GroupByType)
  2357  		assert.Len(t, pipeCommands.GroupByRequest.MeasureOperations, 2)
  2358  		assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[0].MeasureCol, "latency")
  2359  		assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[0].MeasureFunc, utils.Avg)
  2360  		assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[1].MeasureCol, "latitude")
  2361  		assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[1].MeasureFunc, utils.Sum)
  2362  		assert.Len(t, pipeCommands.GroupByRequest.GroupByColumns, 1)
  2363  		assert.Equal(t, pipeCommands.GroupByRequest.GroupByColumns[0], "timestamp")
  2364  		assert.Equal(t, pipeCommands.BucketLimit, segquery.MAX_GRP_BUCKS)
  2365  		// Timechart
  2366  		assert.NotNil(t, pipeCommands.TimeHistogram)
  2367  		assert.NotNil(t, pipeCommands.TimeHistogram.Timechart)
  2368  		assert.Equal(t, "http_status", pipeCommands.TimeHistogram.Timechart.ByField)
  2369  
  2370  		astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2371  		assert.Nil(t, err)
  2372  		assert.NotNil(t, astNode)
  2373  		assert.NotNil(t, aggregator)
  2374  
  2375  		assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2376  		assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2377  		assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2378  		assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2379  
  2380  		assert.Equal(t, aggregator.PipeCommandType, structs.GroupByType)
  2381  		assert.Len(t, aggregator.GroupByRequest.MeasureOperations, 2)
  2382  		assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[0].MeasureCol, "latency")
  2383  		assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[0].MeasureFunc, utils.Avg)
  2384  		assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[1].MeasureCol, "latitude")
  2385  		assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[1].MeasureFunc, utils.Sum)
  2386  		assert.Len(t, aggregator.GroupByRequest.GroupByColumns, 1)
  2387  		assert.Equal(t, aggregator.GroupByRequest.GroupByColumns[0], "timestamp")
  2388  		assert.Equal(t, aggregator.BucketLimit, segquery.MAX_GRP_BUCKS)
  2389  		// Timechart
  2390  		assert.NotNil(t, aggregator.TimeHistogram)
  2391  		assert.NotNil(t, aggregator.TimeHistogram.Timechart)
  2392  		assert.Equal(t, "http_status", pipeCommands.TimeHistogram.Timechart.ByField)
  2393  	}
  2394  }
  2395  
  2396  func Test_TimechartSpanArgWithoutGroupBy(t *testing.T) {
  2397  	queries := []string{
  2398  		`search A=1 | timechart span=1m avg(latency)`,
  2399  		`search A=1 | timechart avg(latency) span=1m`,
  2400  	}
  2401  
  2402  	for _, queryStr := range queries {
  2403  		query := []byte(queryStr)
  2404  		res, err := spl.Parse("", query)
  2405  		assert.Nil(t, err)
  2406  
  2407  		astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2408  
  2409  		assert.Nil(t, err)
  2410  		assert.NotNil(t, astNode)
  2411  		assert.NotNil(t, aggregator)
  2412  
  2413  		assert.Equal(t, uint64(60_000), aggregator.TimeHistogram.IntervalMillis)
  2414  
  2415  		pipeCommands := res.(ast.QueryStruct).PipeCommands
  2416  		assert.NotNil(t, pipeCommands)
  2417  		assert.Equal(t, pipeCommands.PipeCommandType, structs.GroupByType)
  2418  		assert.Len(t, pipeCommands.GroupByRequest.MeasureOperations, 1)
  2419  		assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[0].MeasureCol, "latency")
  2420  		assert.Equal(t, pipeCommands.GroupByRequest.MeasureOperations[0].MeasureFunc, utils.Avg)
  2421  		assert.Len(t, pipeCommands.GroupByRequest.GroupByColumns, 1)
  2422  		assert.Equal(t, pipeCommands.GroupByRequest.GroupByColumns[0], "timestamp")
  2423  		assert.Equal(t, pipeCommands.BucketLimit, segquery.MAX_GRP_BUCKS)
  2424  	}
  2425  }
  2426  
  2427  func Test_aggHasEvalFuncWithoutGroupBy(t *testing.T) {
  2428  	query := []byte(`city=Boston | stats max(latitude), range(eval(latitude >= 0))`)
  2429  	res, err := spl.Parse("", query)
  2430  	assert.Nil(t, err)
  2431  	filterNode := res.(ast.QueryStruct).SearchFilter
  2432  	assert.NotNil(t, filterNode)
  2433  
  2434  	assert.Equal(t, ast.NodeTerminal, filterNode.NodeType)
  2435  	assert.Equal(t, "city", filterNode.Comparison.Field)
  2436  	assert.Equal(t, "=", filterNode.Comparison.Op)
  2437  	assert.Equal(t, "\"Boston\"", filterNode.Comparison.Values)
  2438  
  2439  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  2440  	assert.NotNil(t, pipeCommands)
  2441  	assert.Equal(t, pipeCommands.PipeCommandType, structs.MeasureAggsType)
  2442  	assert.Len(t, pipeCommands.MeasureOperations, 2)
  2443  	assert.Equal(t, "latitude", pipeCommands.MeasureOperations[0].MeasureCol)
  2444  	assert.Equal(t, utils.Max, pipeCommands.MeasureOperations[0].MeasureFunc)
  2445  
  2446  	assert.Equal(t, "range(eval(latitude >= 0))", pipeCommands.MeasureOperations[1].StrEnc)
  2447  	assert.Equal(t, utils.Range, pipeCommands.MeasureOperations[1].MeasureFunc)
  2448  	assert.NotNil(t, pipeCommands.MeasureOperations[1].ValueColRequest)
  2449  	assert.Equal(t, structs.VEMBooleanExpr, int(pipeCommands.MeasureOperations[1].ValueColRequest.ValueExprMode))
  2450  	assert.NotNil(t, pipeCommands.MeasureOperations[1].ValueColRequest.BooleanExpr)
  2451  	assert.NotNil(t, pipeCommands.MeasureOperations[1].ValueColRequest.BooleanExpr.LeftValue)
  2452  	assert.NotNil(t, pipeCommands.MeasureOperations[1].ValueColRequest.BooleanExpr.RightValue)
  2453  	assert.NotNil(t, pipeCommands.MeasureOperations[1].ValueColRequest.BooleanExpr.LeftValue.NumericExpr)
  2454  	assert.NotNil(t, pipeCommands.MeasureOperations[1].ValueColRequest.BooleanExpr.RightValue.NumericExpr)
  2455  	assert.Equal(t, "latitude", pipeCommands.MeasureOperations[1].ValueColRequest.BooleanExpr.LeftValue.NumericExpr.Value)
  2456  	assert.Equal(t, "0", pipeCommands.MeasureOperations[1].ValueColRequest.BooleanExpr.RightValue.NumericExpr.Value)
  2457  }
  2458  
  2459  func Test_aggHasEvalFuncWithGroupBy(t *testing.T) {
  2460  	query := []byte(`* | stats count(eval(http_status >= 100)), values(eval(if(len(state) > 5, job_title, city))) BY state`)
  2461  	res, err := spl.Parse("", query)
  2462  	assert.Nil(t, err)
  2463  	filterNode := res.(ast.QueryStruct).SearchFilter
  2464  	assert.NotNil(t, filterNode)
  2465  
  2466  	assert.Equal(t, ast.NodeTerminal, filterNode.NodeType)
  2467  	assert.Equal(t, "*", filterNode.Comparison.Field)
  2468  	assert.Equal(t, "=", filterNode.Comparison.Op)
  2469  	assert.Equal(t, "\"*\"", filterNode.Comparison.Values)
  2470  
  2471  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  2472  	assert.NotNil(t, pipeCommands)
  2473  	assert.Equal(t, pipeCommands.PipeCommandType, structs.GroupByType)
  2474  	assert.NotNil(t, pipeCommands.GroupByRequest)
  2475  	assert.Len(t, pipeCommands.GroupByRequest.MeasureOperations, 2)
  2476  
  2477  	assert.Equal(t, "count(eval(http_status >= 100))", pipeCommands.GroupByRequest.MeasureOperations[0].StrEnc)
  2478  	assert.Equal(t, utils.Count, pipeCommands.GroupByRequest.MeasureOperations[0].MeasureFunc)
  2479  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[0].ValueColRequest)
  2480  	assert.Equal(t, structs.VEMBooleanExpr, int(pipeCommands.GroupByRequest.MeasureOperations[0].ValueColRequest.ValueExprMode))
  2481  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[0].ValueColRequest.BooleanExpr)
  2482  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[0].ValueColRequest.BooleanExpr.LeftValue)
  2483  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[0].ValueColRequest.BooleanExpr.RightValue)
  2484  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[0].ValueColRequest.BooleanExpr.LeftValue.NumericExpr)
  2485  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[0].ValueColRequest.BooleanExpr.RightValue.NumericExpr)
  2486  	assert.Equal(t, "http_status", pipeCommands.GroupByRequest.MeasureOperations[0].ValueColRequest.BooleanExpr.LeftValue.NumericExpr.Value)
  2487  	assert.Equal(t, "100", pipeCommands.GroupByRequest.MeasureOperations[0].ValueColRequest.BooleanExpr.RightValue.NumericExpr.Value)
  2488  
  2489  	assert.Equal(t, "values(eval(if(len(state) > 5, job_title, city)))", pipeCommands.GroupByRequest.MeasureOperations[1].StrEnc)
  2490  	assert.Equal(t, utils.Values, pipeCommands.GroupByRequest.MeasureOperations[1].MeasureFunc)
  2491  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest)
  2492  	assert.Equal(t, structs.VEMConditionExpr, int(pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ValueExprMode))
  2493  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ConditionExpr)
  2494  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ConditionExpr.BoolExpr)
  2495  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ConditionExpr.BoolExpr.LeftValue)
  2496  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ConditionExpr.BoolExpr.RightValue)
  2497  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr)
  2498  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ConditionExpr.BoolExpr.RightValue.NumericExpr)
  2499  	assert.Equal(t, "len", pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.Op)
  2500  	assert.Equal(t, "state", pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.Left.Value)
  2501  	assert.Equal(t, "5", pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ConditionExpr.BoolExpr.RightValue.NumericExpr.Value)
  2502  
  2503  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ConditionExpr.TrueValue)
  2504  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ConditionExpr.FalseValue)
  2505  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ConditionExpr.TrueValue.NumericExpr)
  2506  	assert.NotNil(t, pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ConditionExpr.FalseValue.NumericExpr)
  2507  	assert.Equal(t, "job_title", pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ConditionExpr.TrueValue.NumericExpr.Value)
  2508  	assert.Equal(t, "city", pipeCommands.GroupByRequest.MeasureOperations[1].ValueColRequest.ConditionExpr.FalseValue.NumericExpr.Value)
  2509  }
  2510  
  2511  func Test_groupbyFieldWithWildcard(t *testing.T) {
  2512  	query := []byte(`search A=1 | stats avg(latency) BY http*`)
  2513  	_, err := spl.Parse("", query)
  2514  	assert.NotNil(t, err)
  2515  }
  2516  
  2517  func Test_fieldSelectImplicitPlus(t *testing.T) {
  2518  	query := []byte(`search A=1 | fields weekday`)
  2519  	res, err := spl.Parse("", query)
  2520  	assert.Nil(t, err)
  2521  	filterNode := res.(ast.QueryStruct).SearchFilter
  2522  	assert.NotNil(t, filterNode)
  2523  
  2524  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2525  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2526  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2527  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2528  
  2529  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  2530  	assert.NotNil(t, pipeCommands)
  2531  	assert.Equal(t, pipeCommands.PipeCommandType, structs.OutputTransformType)
  2532  	assert.Len(t, pipeCommands.OutputTransforms.OutputColumns.ExcludeColumns, 0)
  2533  	assert.Len(t, pipeCommands.OutputTransforms.OutputColumns.IncludeColumns, 1)
  2534  	assert.Equal(t, pipeCommands.OutputTransforms.OutputColumns.IncludeColumns[0], "weekday")
  2535  
  2536  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2537  	assert.Nil(t, err)
  2538  	assert.NotNil(t, astNode)
  2539  	assert.NotNil(t, aggregator)
  2540  
  2541  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2542  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2543  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2544  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2545  
  2546  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  2547  	assert.Len(t, aggregator.OutputTransforms.OutputColumns.ExcludeColumns, 0)
  2548  	assert.Len(t, aggregator.OutputTransforms.OutputColumns.IncludeColumns, 1)
  2549  	assert.Equal(t, aggregator.OutputTransforms.OutputColumns.IncludeColumns[0], "weekday")
  2550  }
  2551  
  2552  func Test_fieldSelectExplicitPlus(t *testing.T) {
  2553  	query := []byte(`search A=1 | fields + weekday`)
  2554  	res, err := spl.Parse("", query)
  2555  	assert.Nil(t, err)
  2556  	filterNode := res.(ast.QueryStruct).SearchFilter
  2557  	assert.NotNil(t, filterNode)
  2558  
  2559  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2560  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2561  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2562  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2563  
  2564  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  2565  	assert.NotNil(t, pipeCommands)
  2566  	assert.Equal(t, pipeCommands.PipeCommandType, structs.OutputTransformType)
  2567  	assert.Len(t, pipeCommands.OutputTransforms.OutputColumns.ExcludeColumns, 0)
  2568  	assert.Len(t, pipeCommands.OutputTransforms.OutputColumns.IncludeColumns, 1)
  2569  	assert.Equal(t, pipeCommands.OutputTransforms.OutputColumns.IncludeColumns[0], "weekday")
  2570  
  2571  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2572  	assert.Nil(t, err)
  2573  	assert.NotNil(t, astNode)
  2574  	assert.NotNil(t, aggregator)
  2575  
  2576  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2577  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2578  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2579  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2580  
  2581  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  2582  	assert.Len(t, aggregator.OutputTransforms.OutputColumns.ExcludeColumns, 0)
  2583  	assert.Len(t, aggregator.OutputTransforms.OutputColumns.IncludeColumns, 1)
  2584  	assert.Equal(t, aggregator.OutputTransforms.OutputColumns.IncludeColumns[0], "weekday")
  2585  }
  2586  
  2587  func Test_fieldSelectMinus(t *testing.T) {
  2588  	query := []byte(`search A=1 | fields - weekday`)
  2589  	res, err := spl.Parse("", query)
  2590  	assert.Nil(t, err)
  2591  	filterNode := res.(ast.QueryStruct).SearchFilter
  2592  	assert.NotNil(t, filterNode)
  2593  
  2594  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2595  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2596  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2597  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2598  
  2599  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  2600  	assert.NotNil(t, pipeCommands)
  2601  	assert.Equal(t, pipeCommands.PipeCommandType, structs.OutputTransformType)
  2602  	assert.Len(t, pipeCommands.OutputTransforms.OutputColumns.IncludeColumns, 0)
  2603  	assert.Len(t, pipeCommands.OutputTransforms.OutputColumns.ExcludeColumns, 1)
  2604  	assert.Equal(t, pipeCommands.OutputTransforms.OutputColumns.ExcludeColumns[0], "weekday")
  2605  
  2606  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2607  	assert.Nil(t, err)
  2608  	assert.NotNil(t, astNode)
  2609  	assert.NotNil(t, aggregator)
  2610  
  2611  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2612  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2613  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2614  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2615  
  2616  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  2617  	assert.Len(t, aggregator.OutputTransforms.OutputColumns.IncludeColumns, 0)
  2618  	assert.Len(t, aggregator.OutputTransforms.OutputColumns.ExcludeColumns, 1)
  2619  	assert.Equal(t, aggregator.OutputTransforms.OutputColumns.ExcludeColumns[0], "weekday")
  2620  }
  2621  
  2622  func Test_fieldSelectManyFields(t *testing.T) {
  2623  	query := []byte(`search A=1 | fields weekday, latency, city`)
  2624  	res, err := spl.Parse("", query)
  2625  	assert.Nil(t, err)
  2626  	filterNode := res.(ast.QueryStruct).SearchFilter
  2627  	assert.NotNil(t, filterNode)
  2628  
  2629  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2630  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2631  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2632  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2633  
  2634  	pipeCommands := res.(ast.QueryStruct).PipeCommands
  2635  	assert.NotNil(t, pipeCommands)
  2636  	assert.Equal(t, pipeCommands.PipeCommandType, structs.OutputTransformType)
  2637  	assert.Len(t, pipeCommands.OutputTransforms.OutputColumns.ExcludeColumns, 0)
  2638  	assert.Len(t, pipeCommands.OutputTransforms.OutputColumns.IncludeColumns, 3)
  2639  	assert.Equal(t, pipeCommands.OutputTransforms.OutputColumns.IncludeColumns[0], "weekday")
  2640  	assert.Equal(t, pipeCommands.OutputTransforms.OutputColumns.IncludeColumns[1], "latency")
  2641  	assert.Equal(t, pipeCommands.OutputTransforms.OutputColumns.IncludeColumns[2], "city")
  2642  
  2643  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2644  	assert.Nil(t, err)
  2645  	assert.NotNil(t, astNode)
  2646  	assert.NotNil(t, aggregator)
  2647  
  2648  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2649  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2650  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2651  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2652  
  2653  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  2654  	assert.Len(t, aggregator.OutputTransforms.OutputColumns.ExcludeColumns, 0)
  2655  	assert.Len(t, aggregator.OutputTransforms.OutputColumns.IncludeColumns, 3)
  2656  	assert.Equal(t, aggregator.OutputTransforms.OutputColumns.IncludeColumns[0], "weekday")
  2657  	assert.Equal(t, aggregator.OutputTransforms.OutputColumns.IncludeColumns[1], "latency")
  2658  	assert.Equal(t, aggregator.OutputTransforms.OutputColumns.IncludeColumns[2], "city")
  2659  }
  2660  
  2661  func Test_commentAtStart(t *testing.T) {
  2662  	query := []byte("```Hello, world!``` A=1")
  2663  	res, err := spl.Parse("", query)
  2664  	assert.Nil(t, err)
  2665  	filterNode := res.(ast.QueryStruct).SearchFilter
  2666  
  2667  	assert.NotNil(t, filterNode)
  2668  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2669  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2670  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2671  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2672  
  2673  	astNode := &structs.ASTNode{}
  2674  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  2675  	assert.Nil(t, err)
  2676  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  2677  	assert.Len(t, astNode.AndFilterCondition.NestedNodes, 0)
  2678  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2679  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2680  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2681  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2682  }
  2683  
  2684  func Test_commentInMiddle(t *testing.T) {
  2685  	query := []byte("search A=1 ```Hello, world!``` OR C=3")
  2686  	res, err := spl.Parse("", query)
  2687  	assert.Nil(t, err)
  2688  	filterNode := res.(ast.QueryStruct).SearchFilter
  2689  
  2690  	assert.NotNil(t, filterNode)
  2691  	assert.Equal(t, ast.NodeOr, filterNode.NodeType)
  2692  
  2693  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
  2694  	assert.Equal(t, filterNode.Left.Comparison.Field, "A")
  2695  	assert.Equal(t, filterNode.Left.Comparison.Op, "=")
  2696  	assert.Equal(t, filterNode.Left.Comparison.Values, json.Number("1"))
  2697  
  2698  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeTerminal)
  2699  	assert.Equal(t, filterNode.Right.Comparison.Field, "C")
  2700  	assert.Equal(t, filterNode.Right.Comparison.Op, "=")
  2701  	assert.Equal(t, filterNode.Right.Comparison.Values, json.Number("3"))
  2702  
  2703  	astNode := &structs.ASTNode{}
  2704  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  2705  	assert.Nil(t, err)
  2706  	assert.NotNil(t, astNode.OrFilterCondition.FilterCriteria)
  2707  	assert.Len(t, astNode.OrFilterCondition.NestedNodes, 0)
  2708  	assert.Len(t, astNode.OrFilterCondition.FilterCriteria, 2)
  2709  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2710  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2711  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2712  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "C")
  2713  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
  2714  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(3))
  2715  }
  2716  
  2717  func Test_commentAtEnd(t *testing.T) {
  2718  	query := []byte("A=1 ```| search B=2```")
  2719  	res, err := spl.Parse("", query)
  2720  	assert.Nil(t, err)
  2721  	filterNode := res.(ast.QueryStruct).SearchFilter
  2722  
  2723  	assert.NotNil(t, filterNode)
  2724  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2725  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2726  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2727  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2728  
  2729  	astNode := &structs.ASTNode{}
  2730  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  2731  	assert.Nil(t, err)
  2732  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  2733  	assert.Len(t, astNode.AndFilterCondition.NestedNodes, 0)
  2734  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2735  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2736  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2737  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2738  }
  2739  
  2740  func Test_commentContainingQuotes(t *testing.T) {
  2741  	query := []byte("A=1 ```| search B=\"2\"```")
  2742  	res, err := spl.Parse("", query)
  2743  	assert.Nil(t, err)
  2744  	filterNode := res.(ast.QueryStruct).SearchFilter
  2745  
  2746  	assert.NotNil(t, filterNode)
  2747  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2748  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2749  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2750  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2751  
  2752  	astNode := &structs.ASTNode{}
  2753  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  2754  	assert.Nil(t, err)
  2755  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  2756  	assert.Len(t, astNode.AndFilterCondition.NestedNodes, 0)
  2757  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2758  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2759  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2760  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2761  }
  2762  
  2763  func Test_commentContainingBackticks(t *testing.T) {
  2764  	query := []byte("A=1 ```one ` and two `` still inside comment```")
  2765  	res, err := spl.Parse("", query)
  2766  	assert.Nil(t, err)
  2767  	filterNode := res.(ast.QueryStruct).SearchFilter
  2768  
  2769  	assert.NotNil(t, filterNode)
  2770  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2771  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2772  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2773  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2774  
  2775  	astNode := &structs.ASTNode{}
  2776  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  2777  	assert.Nil(t, err)
  2778  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  2779  	assert.Len(t, astNode.AndFilterCondition.NestedNodes, 0)
  2780  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2781  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2782  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2783  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2784  }
  2785  
  2786  func Test_commentInsideQuotes(t *testing.T) {
  2787  	query := []byte("A=\"Hello, ```this is not commented out``` world!\"")
  2788  	res, err := spl.Parse("", query)
  2789  	assert.Nil(t, err)
  2790  	filterNode := res.(ast.QueryStruct).SearchFilter
  2791  
  2792  	assert.NotNil(t, filterNode)
  2793  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2794  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2795  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2796  	assert.Equal(t, filterNode.Comparison.Values, "\"Hello, ```this is not commented out``` world!\"")
  2797  
  2798  	astNode := &structs.ASTNode{}
  2799  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  2800  	assert.Nil(t, err)
  2801  	assert.NotNil(t, astNode.AndFilterCondition.FilterCriteria)
  2802  	assert.Len(t, astNode.AndFilterCondition.NestedNodes, 0)
  2803  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2804  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2805  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2806  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.StringVal, "Hello, ```this is not commented out``` world!")
  2807  }
  2808  
  2809  func Test_renameOneAggField(t *testing.T) {
  2810  	query := []byte(`search A=1 | stats avg(latency) AS Average`)
  2811  	res, err := spl.Parse("", query)
  2812  	assert.Nil(t, err)
  2813  	filterNode := res.(ast.QueryStruct).SearchFilter
  2814  	assert.NotNil(t, filterNode)
  2815  
  2816  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2817  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2818  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2819  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2820  
  2821  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2822  	assert.Nil(t, err)
  2823  	assert.NotNil(t, astNode)
  2824  	assert.NotNil(t, aggregator)
  2825  
  2826  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2827  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2828  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2829  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2830  
  2831  	assert.Equal(t, aggregator.PipeCommandType, structs.MeasureAggsType)
  2832  	assert.Len(t, aggregator.MeasureOperations, 1)
  2833  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureCol, "latency")
  2834  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureFunc, utils.Avg)
  2835  
  2836  	renameAgg := aggregator.Next
  2837  	assert.NotNil(t, renameAgg)
  2838  	assert.Equal(t, renameAgg.PipeCommandType, structs.OutputTransformType)
  2839  	assert.NotNil(t, renameAgg.OutputTransforms)
  2840  	assert.NotNil(t, renameAgg.OutputTransforms.OutputColumns)
  2841  	assert.Len(t, renameAgg.OutputTransforms.OutputColumns.RenameColumns, 0)
  2842  	assert.Len(t, renameAgg.OutputTransforms.OutputColumns.RenameAggregationColumns, 1)
  2843  	assert.Equal(t, renameAgg.OutputTransforms.OutputColumns.RenameAggregationColumns["avg(latency)"], "Average")
  2844  }
  2845  
  2846  func Test_renameManyAggFields(t *testing.T) {
  2847  	query := []byte(`search A=1 | stats avg(latency) AS Average, count AS Count, min(latency)`)
  2848  	res, err := spl.Parse("", query)
  2849  	assert.Nil(t, err)
  2850  	filterNode := res.(ast.QueryStruct).SearchFilter
  2851  	assert.NotNil(t, filterNode)
  2852  
  2853  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2854  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2855  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2856  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2857  
  2858  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2859  	assert.Nil(t, err)
  2860  	assert.NotNil(t, astNode)
  2861  	assert.NotNil(t, aggregator)
  2862  
  2863  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2864  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2865  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2866  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2867  
  2868  	assert.Equal(t, aggregator.PipeCommandType, structs.MeasureAggsType)
  2869  	assert.Len(t, aggregator.MeasureOperations, 3)
  2870  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureCol, "latency")
  2871  	assert.Equal(t, aggregator.MeasureOperations[0].MeasureFunc, utils.Avg)
  2872  	assert.Equal(t, aggregator.MeasureOperations[1].MeasureCol, "*")
  2873  	assert.Equal(t, aggregator.MeasureOperations[1].MeasureFunc, utils.Count)
  2874  	assert.Equal(t, aggregator.MeasureOperations[2].MeasureCol, "latency")
  2875  	assert.Equal(t, aggregator.MeasureOperations[2].MeasureFunc, utils.Min)
  2876  
  2877  	renameAgg := aggregator.Next
  2878  	assert.NotNil(t, renameAgg)
  2879  	assert.Equal(t, renameAgg.PipeCommandType, structs.OutputTransformType)
  2880  	assert.Len(t, renameAgg.OutputTransforms.OutputColumns.RenameColumns, 0)
  2881  	assert.Len(t, renameAgg.OutputTransforms.OutputColumns.RenameAggregationColumns, 2)
  2882  	assert.Equal(t, renameAgg.OutputTransforms.OutputColumns.RenameAggregationColumns["avg(latency)"], "Average")
  2883  	assert.Equal(t, renameAgg.OutputTransforms.OutputColumns.RenameAggregationColumns["count(*)"], "Count")
  2884  }
  2885  
  2886  func Test_renameFieldsWithGroupby(t *testing.T) {
  2887  	query := []byte(`search A=1 | stats avg(latency) AS Average, count BY weekday, city`)
  2888  	res, err := spl.Parse("", query)
  2889  	assert.Nil(t, err)
  2890  	filterNode := res.(ast.QueryStruct).SearchFilter
  2891  	assert.NotNil(t, filterNode)
  2892  
  2893  	assert.Equal(t, filterNode.NodeType, ast.NodeTerminal)
  2894  	assert.Equal(t, filterNode.Comparison.Field, "A")
  2895  	assert.Equal(t, filterNode.Comparison.Op, "=")
  2896  	assert.Equal(t, filterNode.Comparison.Values, json.Number("1"))
  2897  
  2898  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2899  	assert.Nil(t, err)
  2900  	assert.NotNil(t, astNode)
  2901  	assert.NotNil(t, aggregator)
  2902  
  2903  	assert.Len(t, astNode.AndFilterCondition.FilterCriteria, 1)
  2904  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  2905  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  2906  	assert.Equal(t, astNode.AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  2907  
  2908  	assert.Equal(t, aggregator.PipeCommandType, structs.GroupByType)
  2909  	assert.Len(t, aggregator.GroupByRequest.MeasureOperations, 2)
  2910  	assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[0].MeasureCol, "latency")
  2911  	assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[0].MeasureFunc, utils.Avg)
  2912  	assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[1].MeasureCol, "*")
  2913  	assert.Equal(t, aggregator.GroupByRequest.MeasureOperations[1].MeasureFunc, utils.Count)
  2914  	assert.Len(t, aggregator.GroupByRequest.GroupByColumns, 2)
  2915  	assert.Equal(t, aggregator.GroupByRequest.GroupByColumns[0], "weekday")
  2916  	assert.Equal(t, aggregator.GroupByRequest.GroupByColumns[1], "city")
  2917  	assert.Equal(t, aggregator.BucketLimit, segquery.MAX_GRP_BUCKS)
  2918  
  2919  	renameAgg := aggregator.Next
  2920  	assert.NotNil(t, renameAgg)
  2921  	assert.Equal(t, renameAgg.PipeCommandType, structs.OutputTransformType)
  2922  	assert.Len(t, renameAgg.OutputTransforms.OutputColumns.RenameColumns, 0)
  2923  	assert.Len(t, renameAgg.OutputTransforms.OutputColumns.RenameAggregationColumns, 1)
  2924  	assert.Equal(t, renameAgg.OutputTransforms.OutputColumns.RenameAggregationColumns["avg(latency)"], "Average")
  2925  }
  2926  
  2927  func Test_rexBlockNewFieldWithoutGroupBy(t *testing.T) {
  2928  	query := []byte(`city=Boston | rex field=app_version "(?<first>\d+)\.(?<second>\d+)\.(?<third>\d+)"`)
  2929  	res, err := spl.Parse("", query)
  2930  	assert.Nil(t, err)
  2931  	filterNode := res.(ast.QueryStruct).SearchFilter
  2932  	assert.NotNil(t, filterNode)
  2933  
  2934  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2935  	assert.Nil(t, err)
  2936  	assert.NotNil(t, astNode)
  2937  	assert.NotNil(t, aggregator)
  2938  	assert.Nil(t, aggregator.Next)
  2939  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  2940  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns)
  2941  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns.RexColRequest)
  2942  	assert.Equal(t, aggregator.OutputTransforms.LetColumns.RexColRequest.Pattern, "(?P<first>\\d+)\\.(?P<second>\\d+)\\.(?P<third>\\d+)")
  2943  	assert.Equal(t, aggregator.OutputTransforms.LetColumns.RexColRequest.FieldName, "app_version")
  2944  	assert.Equal(t, aggregator.OutputTransforms.LetColumns.RexColRequest.RexColNames, []string{"first", "second", "third"})
  2945  }
  2946  
  2947  func Test_rexBlockOverideExistingFieldWithoutGroupBy(t *testing.T) {
  2948  	query := []byte(`city=Boston | rex field=app_version "(?<app_version>\d+)\.(?<city>\d+)\.(?<third>\d+)"`)
  2949  	res, err := spl.Parse("", query)
  2950  	assert.Nil(t, err)
  2951  	filterNode := res.(ast.QueryStruct).SearchFilter
  2952  	assert.NotNil(t, filterNode)
  2953  
  2954  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2955  	assert.Nil(t, err)
  2956  	assert.NotNil(t, astNode)
  2957  	assert.NotNil(t, aggregator)
  2958  	assert.Nil(t, aggregator.Next)
  2959  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  2960  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns)
  2961  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns.RexColRequest)
  2962  	assert.Equal(t, aggregator.OutputTransforms.LetColumns.RexColRequest.Pattern, "(?P<app_version>\\d+)\\.(?P<city>\\d+)\\.(?P<third>\\d+)")
  2963  	assert.Equal(t, aggregator.OutputTransforms.LetColumns.RexColRequest.FieldName, "app_version")
  2964  	assert.Equal(t, aggregator.OutputTransforms.LetColumns.RexColRequest.RexColNames, []string{"app_version", "city", "third"})
  2965  }
  2966  
  2967  func Test_rexBlockNewFieldWithGroupBy(t *testing.T) {
  2968  	query := []byte(`city=Boston | stats count AS Count BY user_email | rex field=user_email "(?<name>.+)@(?<provider>.+)"`)
  2969  	res, err := spl.Parse("", query)
  2970  	assert.Nil(t, err)
  2971  	filterNode := res.(ast.QueryStruct).SearchFilter
  2972  	assert.NotNil(t, filterNode)
  2973  
  2974  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2975  	assert.Nil(t, err)
  2976  	assert.NotNil(t, astNode)
  2977  	assert.NotNil(t, aggregator)
  2978  	assert.NotNil(t, aggregator.GroupByRequest)
  2979  	assert.Equal(t, aggregator.GroupByRequest.GroupByColumns, []string{"user_email"})
  2980  	assert.NotNil(t, aggregator.Next)
  2981  	assert.NotNil(t, aggregator.Next.Next)
  2982  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  2983  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.RexColRequest)
  2984  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.RexColRequest.Pattern, "(?P<name>.+)@(?P<provider>.+)")
  2985  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.RexColRequest.FieldName, "user_email")
  2986  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.RexColRequest.RexColNames, []string{"name", "provider"})
  2987  }
  2988  
  2989  func Test_rexBlockOverideExistingFieldWithGroupBy(t *testing.T) {
  2990  	query := []byte(`city=Boston | stats count AS Count BY http_status, weekday | rex field=http_status "(?<http_status>\d)(?<weekday>\d)(?<third>\d)"`)
  2991  	res, err := spl.Parse("", query)
  2992  	assert.Nil(t, err)
  2993  	filterNode := res.(ast.QueryStruct).SearchFilter
  2994  	assert.NotNil(t, filterNode)
  2995  
  2996  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  2997  	assert.Nil(t, err)
  2998  	assert.NotNil(t, astNode)
  2999  	assert.NotNil(t, aggregator)
  3000  	assert.NotNil(t, aggregator.GroupByRequest)
  3001  	assert.Equal(t, aggregator.GroupByRequest.GroupByColumns, []string{"http_status", "weekday"})
  3002  	assert.NotNil(t, aggregator.Next)
  3003  	assert.NotNil(t, aggregator.Next.Next)
  3004  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3005  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.RexColRequest)
  3006  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.RexColRequest.Pattern, "(?P<http_status>\\d)(?P<weekday>\\d)(?P<third>\\d)")
  3007  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.RexColRequest.FieldName, "http_status")
  3008  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.RexColRequest.RexColNames, []string{"http_status", "weekday", "third"})
  3009  }
  3010  
  3011  func Test_statisticBlockWithoutStatsGroupBy(t *testing.T) {
  3012  	query := []byte(`city=Boston | rare 3 http_method, gender by country, http_status useother=true otherstr=testOther percentfield=http_method countfield=gender showperc=false`)
  3013  	res, err := spl.Parse("", query)
  3014  	assert.Nil(t, err)
  3015  	filterNode := res.(ast.QueryStruct).SearchFilter
  3016  	assert.NotNil(t, filterNode)
  3017  
  3018  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3019  	assert.Nil(t, err)
  3020  	assert.NotNil(t, astNode)
  3021  	assert.NotNil(t, aggregator)
  3022  	assert.NotNil(t, aggregator.GroupByRequest)
  3023  	assert.Equal(t, []string{"http_method", "gender", "country", "http_status"}, aggregator.GroupByRequest.GroupByColumns)
  3024  	assert.NotNil(t, aggregator.Next)
  3025  	assert.Equal(t, structs.OutputTransformType, aggregator.Next.PipeCommandType)
  3026  	assert.NotNil(t, aggregator.Next.OutputTransforms)
  3027  	assert.NotNil(t, aggregator.Next.OutputTransforms.LetColumns)
  3028  	assert.NotNil(t, aggregator.Next.OutputTransforms.LetColumns.StatisticColRequest)
  3029  	assert.Equal(t, structs.SFMRare, int(aggregator.Next.OutputTransforms.LetColumns.StatisticColRequest.StatisticFunctionMode))
  3030  
  3031  	assert.Equal(t, "3", aggregator.Next.OutputTransforms.LetColumns.StatisticColRequest.Limit)
  3032  	assert.Equal(t, "gender", aggregator.Next.OutputTransforms.LetColumns.StatisticColRequest.StatisticOptions.CountField)
  3033  	assert.Equal(t, "testOther", aggregator.Next.OutputTransforms.LetColumns.StatisticColRequest.StatisticOptions.OtherStr)
  3034  	assert.Equal(t, "http_method", aggregator.Next.OutputTransforms.LetColumns.StatisticColRequest.StatisticOptions.PercentField)
  3035  	assert.Equal(t, true, aggregator.Next.OutputTransforms.LetColumns.StatisticColRequest.StatisticOptions.ShowCount)
  3036  	assert.Equal(t, false, aggregator.Next.OutputTransforms.LetColumns.StatisticColRequest.StatisticOptions.ShowPerc)
  3037  	assert.Equal(t, true, aggregator.Next.OutputTransforms.LetColumns.StatisticColRequest.StatisticOptions.UseOther)
  3038  
  3039  	assert.Equal(t, []string{"http_method", "gender"}, aggregator.Next.OutputTransforms.LetColumns.StatisticColRequest.FieldList)
  3040  	assert.Equal(t, []string{"country", "http_status"}, aggregator.Next.OutputTransforms.LetColumns.StatisticColRequest.ByClause)
  3041  }
  3042  
  3043  func Test_statisticBlockWithStatsGroupBy(t *testing.T) {
  3044  	query := []byte(`city=Boston | stats count AS gg BY http_status, weekday, gender, state | top 2 gg, state, http_status useother=true countfield=true percentfield=weekday`)
  3045  	res, err := spl.Parse("", query)
  3046  	assert.Nil(t, err)
  3047  	filterNode := res.(ast.QueryStruct).SearchFilter
  3048  	assert.NotNil(t, filterNode)
  3049  
  3050  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3051  	assert.Nil(t, err)
  3052  	assert.NotNil(t, astNode)
  3053  	assert.NotNil(t, aggregator)
  3054  	assert.Equal(t, structs.GroupByType, aggregator.PipeCommandType)
  3055  	assert.NotNil(t, aggregator.GroupByRequest)
  3056  	assert.Equal(t, []string{"http_status", "weekday", "gender", "state"}, aggregator.GroupByRequest.GroupByColumns)
  3057  	assert.NotNil(t, aggregator.Next)
  3058  	assert.NotNil(t, aggregator.Next.Next)
  3059  	assert.NotNil(t, aggregator.Next.Next.Next)
  3060  	assert.NotNil(t, aggregator.Next.Next.GroupByRequest)
  3061  	assert.Equal(t, structs.GroupByType, aggregator.Next.Next.PipeCommandType)
  3062  	assert.Equal(t, []string{"gg", "state", "http_status"}, aggregator.Next.Next.GroupByRequest.GroupByColumns)
  3063  	assert.Equal(t, structs.OutputTransformType, aggregator.Next.Next.Next.PipeCommandType)
  3064  	assert.NotNil(t, aggregator.Next.Next.Next.OutputTransforms)
  3065  	assert.NotNil(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns)
  3066  
  3067  	assert.NotNil(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.StatisticColRequest)
  3068  	assert.Equal(t, structs.SFMTop, int(aggregator.Next.Next.Next.OutputTransforms.LetColumns.StatisticColRequest.StatisticFunctionMode))
  3069  	assert.Equal(t, "2", aggregator.Next.Next.Next.OutputTransforms.LetColumns.StatisticColRequest.Limit)
  3070  	assert.Equal(t, "true", aggregator.Next.Next.Next.OutputTransforms.LetColumns.StatisticColRequest.StatisticOptions.CountField)
  3071  	assert.Equal(t, "other", aggregator.Next.Next.Next.OutputTransforms.LetColumns.StatisticColRequest.StatisticOptions.OtherStr)
  3072  	assert.Equal(t, "weekday", aggregator.Next.Next.Next.OutputTransforms.LetColumns.StatisticColRequest.StatisticOptions.PercentField)
  3073  	assert.Equal(t, true, aggregator.Next.Next.Next.OutputTransforms.LetColumns.StatisticColRequest.StatisticOptions.ShowCount)
  3074  	assert.Equal(t, true, aggregator.Next.Next.Next.OutputTransforms.LetColumns.StatisticColRequest.StatisticOptions.ShowPerc)
  3075  	assert.Equal(t, true, aggregator.Next.Next.Next.OutputTransforms.LetColumns.StatisticColRequest.StatisticOptions.UseOther)
  3076  
  3077  	assert.Equal(t, []string{"gg", "state", "http_status"}, aggregator.Next.Next.Next.OutputTransforms.LetColumns.StatisticColRequest.FieldList)
  3078  	assert.Equal(t, []string(nil), aggregator.Next.Next.Next.OutputTransforms.LetColumns.StatisticColRequest.ByClause)
  3079  }
  3080  
  3081  func Test_renameBlockPhrase(t *testing.T) {
  3082  	query := []byte(`city=Boston | fields city, country | rename city AS "test"`)
  3083  	res, err := spl.Parse("", query)
  3084  	assert.Nil(t, err)
  3085  	filterNode := res.(ast.QueryStruct).SearchFilter
  3086  	assert.NotNil(t, filterNode)
  3087  
  3088  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3089  	assert.Nil(t, err)
  3090  	assert.NotNil(t, astNode)
  3091  	assert.NotNil(t, aggregator)
  3092  	assert.NotNil(t, aggregator.Next)
  3093  	assert.Equal(t, aggregator.Next.PipeCommandType, structs.OutputTransformType)
  3094  	assert.NotNil(t, aggregator.Next.OutputTransforms.LetColumns)
  3095  	assert.NotNil(t, aggregator.Next.OutputTransforms.LetColumns.RenameColRequest)
  3096  	assert.Equal(t, structs.REMPhrase, int(aggregator.Next.OutputTransforms.LetColumns.RenameColRequest.RenameExprMode))
  3097  	assert.Equal(t, "city", aggregator.Next.OutputTransforms.LetColumns.RenameColRequest.OriginalPattern)
  3098  	assert.Equal(t, "test", aggregator.Next.OutputTransforms.LetColumns.RenameColRequest.NewPattern)
  3099  }
  3100  
  3101  func Test_renameBlockRegex(t *testing.T) {
  3102  	query := []byte(`city=Boston | stats count AS Count BY http_status, http_method | rename ht*_* AS start*mid*end`)
  3103  	res, err := spl.Parse("", query)
  3104  	assert.Nil(t, err)
  3105  	filterNode := res.(ast.QueryStruct).SearchFilter
  3106  	assert.NotNil(t, filterNode)
  3107  
  3108  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3109  	assert.Nil(t, err)
  3110  	assert.NotNil(t, astNode)
  3111  	assert.NotNil(t, aggregator)
  3112  	assert.NotNil(t, aggregator.Next)
  3113  	assert.NotNil(t, aggregator.Next.Next)
  3114  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3115  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3116  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.RenameColRequest)
  3117  	assert.Equal(t, structs.REMRegex, int(aggregator.Next.Next.OutputTransforms.LetColumns.RenameColRequest.RenameExprMode))
  3118  	assert.Equal(t, "ht*_*", aggregator.Next.Next.OutputTransforms.LetColumns.RenameColRequest.OriginalPattern)
  3119  	assert.Equal(t, "start*mid*end", aggregator.Next.Next.OutputTransforms.LetColumns.RenameColRequest.NewPattern)
  3120  }
  3121  
  3122  func Test_renameOverrideExistingField(t *testing.T) {
  3123  	query := []byte(`city=Boston | stats count AS Count BY http_status, http_method | rename http_status AS Count`)
  3124  	res, err := spl.Parse("", query)
  3125  	assert.Nil(t, err)
  3126  	filterNode := res.(ast.QueryStruct).SearchFilter
  3127  	assert.NotNil(t, filterNode)
  3128  
  3129  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3130  	assert.Nil(t, err)
  3131  	assert.NotNil(t, astNode)
  3132  	assert.NotNil(t, aggregator)
  3133  	assert.NotNil(t, aggregator.Next)
  3134  	assert.NotNil(t, aggregator.Next.Next)
  3135  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3136  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3137  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.RenameColRequest)
  3138  	assert.Equal(t, structs.REMOverride, int(aggregator.Next.Next.OutputTransforms.LetColumns.RenameColRequest.RenameExprMode))
  3139  	assert.Equal(t, "http_status", aggregator.Next.Next.OutputTransforms.LetColumns.RenameColRequest.OriginalPattern)
  3140  	assert.Equal(t, "Count", aggregator.Next.Next.OutputTransforms.LetColumns.RenameColRequest.NewPattern)
  3141  }
  3142  
  3143  func Test_evalNewField(t *testing.T) {
  3144  	query := []byte(`search A=1 | stats max(latency) AS Max | eval MaxSeconds=Max . " seconds"`)
  3145  	res, err := spl.Parse("", query)
  3146  	assert.Nil(t, err)
  3147  	filterNode := res.(ast.QueryStruct).SearchFilter
  3148  	assert.NotNil(t, filterNode)
  3149  
  3150  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3151  	assert.Nil(t, err)
  3152  	assert.NotNil(t, astNode)
  3153  	assert.NotNil(t, aggregator)
  3154  	assert.NotNil(t, aggregator.Next)
  3155  
  3156  	// Second agg is for renaming max(latency) to Max, the third is for eval.
  3157  	assert.NotNil(t, aggregator.Next.Next)
  3158  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3159  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3160  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "MaxSeconds")
  3161  	assert.Len(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms, 2)
  3162  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].IsField, true)
  3163  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].Value, "Max")
  3164  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].IsField, false)
  3165  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].Value, " seconds")
  3166  }
  3167  
  3168  func Test_evalReplaceField(t *testing.T) {
  3169  	query := []byte(`search A=1 | stats max(latency) AS Max | eval Max=Max . " seconds"`)
  3170  	res, err := spl.Parse("", query)
  3171  	assert.Nil(t, err)
  3172  	filterNode := res.(ast.QueryStruct).SearchFilter
  3173  	assert.NotNil(t, filterNode)
  3174  
  3175  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3176  	assert.Nil(t, err)
  3177  	assert.NotNil(t, astNode)
  3178  	assert.NotNil(t, aggregator)
  3179  	assert.NotNil(t, aggregator.Next)
  3180  
  3181  	// Second agg is for renaming max(latency) to Max, the third is for eval.
  3182  	assert.NotNil(t, aggregator.Next.Next)
  3183  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3184  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3185  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "Max")
  3186  	assert.Len(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms, 2)
  3187  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].IsField, true)
  3188  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].Value, "Max")
  3189  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].IsField, false)
  3190  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].Value, " seconds")
  3191  }
  3192  
  3193  func Test_evalAfterGroupBy(t *testing.T) {
  3194  	query := []byte(`search A=1 | stats max(latency) AS Max BY weekday | eval Max=Max . " seconds on " . weekday`)
  3195  	res, err := spl.Parse("", query)
  3196  	assert.Nil(t, err)
  3197  	filterNode := res.(ast.QueryStruct).SearchFilter
  3198  	assert.NotNil(t, filterNode)
  3199  
  3200  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3201  	assert.Nil(t, err)
  3202  	assert.NotNil(t, astNode)
  3203  	assert.NotNil(t, aggregator)
  3204  	assert.NotNil(t, aggregator.Next)
  3205  
  3206  	// Second agg is for renaming max(latency) to Max, the third is for eval.
  3207  	assert.NotNil(t, aggregator.Next.Next)
  3208  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3209  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3210  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "Max")
  3211  	assert.Len(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms, 3)
  3212  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].IsField, true)
  3213  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].Value, "Max")
  3214  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].IsField, false)
  3215  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].Value, " seconds on ")
  3216  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[2].IsField, true)
  3217  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[2].Value, "weekday")
  3218  }
  3219  
  3220  func Test_evalReplaceGroupByCol(t *testing.T) {
  3221  	query := []byte(`search A=1 | stats max(latency) AS Max BY weekday | eval weekday=weekday . ": " . Max . " seconds"`)
  3222  	res, err := spl.Parse("", query)
  3223  	assert.Nil(t, err)
  3224  	filterNode := res.(ast.QueryStruct).SearchFilter
  3225  	assert.NotNil(t, filterNode)
  3226  
  3227  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3228  	assert.Nil(t, err)
  3229  	assert.NotNil(t, astNode)
  3230  	assert.NotNil(t, aggregator)
  3231  	assert.NotNil(t, aggregator.Next)
  3232  
  3233  	// Second agg is for renaming max(latency) to Max, the third is for eval.
  3234  	assert.NotNil(t, aggregator.Next.Next)
  3235  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3236  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3237  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "weekday")
  3238  	assert.Len(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms, 4)
  3239  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].IsField, true)
  3240  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].Value, "weekday")
  3241  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].IsField, false)
  3242  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].Value, ": ")
  3243  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[2].IsField, true)
  3244  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[2].Value, "Max")
  3245  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[3].IsField, false)
  3246  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[3].Value, " seconds")
  3247  }
  3248  
  3249  func Test_evalFunctionsToNumber(t *testing.T) {
  3250  	query := []byte(`city=Boston | stats count AS Count BY state | eval result=tonumber("0A4",16)`)
  3251  	res, err := spl.Parse("", query)
  3252  	assert.Nil(t, err)
  3253  	filterNode := res.(ast.QueryStruct).SearchFilter
  3254  	assert.NotNil(t, filterNode)
  3255  
  3256  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3257  	assert.Nil(t, err)
  3258  	assert.NotNil(t, astNode)
  3259  	assert.NotNil(t, aggregator)
  3260  	assert.NotNil(t, aggregator.Next)
  3261  	assert.NotNil(t, aggregator.Next.Next)
  3262  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3263  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3264  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  3265  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3266  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMNumericExpr)
  3267  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.NumericExprMode), structs.NEMNumericExpr)
  3268  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.IsTerminal, false)
  3269  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Op, "tonumber")
  3270  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right)
  3271  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.NumericExprMode), structs.NEMNumber)
  3272  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.IsTerminal, true)
  3273  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.ValueIsField, false)
  3274  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.Value, "16")
  3275  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Val)
  3276  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Val.StringExprMode), structs.SEMRawString)
  3277  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Val.RawString, "0A4")
  3278  
  3279  }
  3280  
  3281  func Test_evalFunctionsAbs(t *testing.T) {
  3282  	query := []byte(`city=Boston | stats count AS Count BY http_status | eval myField=abs(http_status - 100)`)
  3283  	res, err := spl.Parse("", query)
  3284  	assert.Nil(t, err)
  3285  	filterNode := res.(ast.QueryStruct).SearchFilter
  3286  	assert.NotNil(t, filterNode)
  3287  
  3288  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3289  	assert.Nil(t, err)
  3290  	assert.NotNil(t, astNode)
  3291  	assert.NotNil(t, aggregator)
  3292  	assert.NotNil(t, aggregator.Next)
  3293  	assert.NotNil(t, aggregator.Next.Next)
  3294  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3295  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3296  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "myField")
  3297  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.IsTerminal, false)
  3298  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Op, "abs")
  3299  	assert.Nil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right, err)
  3300  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Op, "-")
  3301  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.IsTerminal, true)
  3302  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.ValueIsField, true)
  3303  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.Value, "http_status")
  3304  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.ValueIsField, false)
  3305  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.IsTerminal, true)
  3306  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Value, "100")
  3307  }
  3308  
  3309  func Test_evalFunctionsCeil(t *testing.T) {
  3310  	query := []byte(`city=Boston | stats count AS Count BY weekday | eval ceil=ceil(Count + 0.2)`)
  3311  	res, err := spl.Parse("", query)
  3312  	assert.Nil(t, err)
  3313  	filterNode := res.(ast.QueryStruct).SearchFilter
  3314  	assert.NotNil(t, filterNode)
  3315  
  3316  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3317  	assert.Nil(t, err)
  3318  	assert.NotNil(t, astNode)
  3319  	assert.NotNil(t, aggregator)
  3320  	assert.NotNil(t, aggregator.Next)
  3321  	assert.NotNil(t, aggregator.Next.Next)
  3322  	assert.Equal(t, aggregator.GroupByRequest.GroupByColumns[0], "weekday")
  3323  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3324  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Op, "ceil")
  3325  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.IsTerminal, false)
  3326  	assert.Nil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right, err)
  3327  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Op, "+")
  3328  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.ValueIsField, true)
  3329  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.IsTerminal, true)
  3330  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.Value, "Count")
  3331  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.IsTerminal, true)
  3332  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Value, "0.2")
  3333  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.ValueIsField, false)
  3334  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "ceil")
  3335  }
  3336  
  3337  func Test_evalFunctionsRound(t *testing.T) {
  3338  	query := []byte(`city=Detroit | stats count AS Count BY latitude | where latitude > 89.6 | eval round=round(latitude)`)
  3339  	res, err := spl.Parse("", query)
  3340  	assert.Nil(t, err)
  3341  	filterNode := res.(ast.QueryStruct).SearchFilter
  3342  	assert.NotNil(t, filterNode)
  3343  
  3344  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3345  	assert.Nil(t, err)
  3346  	assert.NotNil(t, astNode)
  3347  	assert.NotNil(t, aggregator)
  3348  	assert.NotNil(t, aggregator.Next)
  3349  	assert.NotNil(t, aggregator.Next.Next)
  3350  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3351  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.LeftValue.NumericExpr.Value, "latitude")
  3352  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.LeftValue.NumericExpr.ValueIsField, true)
  3353  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.RightValue.NumericExpr.Value, "89.6")
  3354  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.RightValue.NumericExpr.ValueIsField, false)
  3355  
  3356  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.IsTerminal, false)
  3357  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Op, "round")
  3358  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Value, "latitude")
  3359  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.ValueIsField, true)
  3360  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.IsTerminal, true)
  3361  	assert.Nil(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right)
  3362  }
  3363  
  3364  func Test_evalFunctionsRoundPrecision(t *testing.T) {
  3365  	query := []byte(`city=Detroit | stats count AS Count BY latitude | where latitude > 89.6 | eval round=round(latitude, 3)`)
  3366  	res, err := spl.Parse("", query)
  3367  	assert.Nil(t, err)
  3368  	filterNode := res.(ast.QueryStruct).SearchFilter
  3369  	assert.NotNil(t, filterNode)
  3370  
  3371  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3372  	assert.Nil(t, err)
  3373  	assert.NotNil(t, astNode)
  3374  	assert.NotNil(t, aggregator)
  3375  	assert.NotNil(t, aggregator.Next)
  3376  	assert.NotNil(t, aggregator.Next.Next)
  3377  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3378  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.LeftValue.NumericExpr.Value, "latitude")
  3379  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.LeftValue.NumericExpr.ValueIsField, true)
  3380  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.RightValue.NumericExpr.Value, "89.6")
  3381  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.RightValue.NumericExpr.ValueIsField, false)
  3382  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.IsTerminal, false)
  3383  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Op, "round")
  3384  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Value, "latitude")
  3385  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.ValueIsField, true)
  3386  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.IsTerminal, true)
  3387  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.Value, "3")
  3388  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.ValueIsField, false)
  3389  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.IsTerminal, true)
  3390  }
  3391  
  3392  func Test_evalFunctionsSqrt(t *testing.T) {
  3393  	query := []byte(`city=Columbus | stats count AS Count BY http_status | eval sqrt=sqrt(http_status + 200)`)
  3394  	res, err := spl.Parse("", query)
  3395  	assert.Nil(t, err)
  3396  	filterNode := res.(ast.QueryStruct).SearchFilter
  3397  	assert.NotNil(t, filterNode)
  3398  
  3399  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3400  	assert.Nil(t, err)
  3401  	assert.NotNil(t, astNode)
  3402  	assert.NotNil(t, aggregator)
  3403  	assert.NotNil(t, aggregator.Next)
  3404  	assert.NotNil(t, aggregator.Next.Next)
  3405  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3406  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3407  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "sqrt")
  3408  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.IsTerminal, false)
  3409  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Op, "sqrt")
  3410  	assert.Nil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right, err)
  3411  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Op, "+")
  3412  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.Value, "http_status")
  3413  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.ValueIsField, true)
  3414  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Value, "200")
  3415  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.ValueIsField, false)
  3416  }
  3417  
  3418  func Test_evalFunctionsLen(t *testing.T) {
  3419  	query := []byte(`city=Boston | stats count AS Count BY app_name | eval len=len(app_name) | where len > 22`)
  3420  	res, err := spl.Parse("", query)
  3421  	assert.Nil(t, err)
  3422  	filterNode := res.(ast.QueryStruct).SearchFilter
  3423  	assert.NotNil(t, filterNode)
  3424  
  3425  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3426  	assert.Nil(t, err)
  3427  	assert.NotNil(t, astNode)
  3428  	assert.NotNil(t, aggregator)
  3429  	assert.NotNil(t, aggregator.Next)
  3430  	assert.NotNil(t, aggregator.Next.Next)
  3431  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3432  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3433  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "len")
  3434  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3435  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr)
  3436  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.IsTerminal, false)
  3437  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Op, "len")
  3438  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.NumericExprMode), structs.NEMLenField)
  3439  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.IsTerminal, true)
  3440  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.ValueIsField, true)
  3441  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Value, "app_name")
  3442  	assert.Nil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right)
  3443  	assert.Nil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left)
  3444  	assert.Nil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right)
  3445  }
  3446  
  3447  func Test_evalFunctionsLower(t *testing.T) {
  3448  	query := []byte(`city=Boston | stats count AS Count BY state | eval myField="Test concat:" . lower(state) . "  end"`)
  3449  	res, err := spl.Parse("", query)
  3450  	assert.Nil(t, err)
  3451  	filterNode := res.(ast.QueryStruct).SearchFilter
  3452  	assert.NotNil(t, filterNode)
  3453  
  3454  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3455  	assert.Nil(t, err)
  3456  	assert.NotNil(t, astNode)
  3457  	assert.NotNil(t, aggregator)
  3458  	assert.NotNil(t, aggregator.Next)
  3459  	assert.NotNil(t, aggregator.Next.Next)
  3460  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3461  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3462  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "myField")
  3463  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3464  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMStringExpr)
  3465  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr)
  3466  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr)
  3467  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].Value, "Test concat:")
  3468  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].IsField, false)
  3469  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].TextExpr.Op, "lower")
  3470  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].TextExpr.Value.StringExprMode), structs.SEMField)
  3471  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].TextExpr.Value.FieldName, "state")
  3472  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].IsField, false)
  3473  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[2].Value, "  end")
  3474  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[2].IsField, false)
  3475  }
  3476  
  3477  func Test_evalFunctionsLtrim(t *testing.T) {
  3478  	query := []byte(`city=Boston | stats count AS Count BY state | eval myField=ltrim(state, "Ma") . " test end"`)
  3479  	res, err := spl.Parse("", query)
  3480  	assert.Nil(t, err)
  3481  	filterNode := res.(ast.QueryStruct).SearchFilter
  3482  	assert.NotNil(t, filterNode)
  3483  
  3484  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3485  	assert.Nil(t, err)
  3486  	assert.NotNil(t, astNode)
  3487  	assert.NotNil(t, aggregator)
  3488  	assert.NotNil(t, aggregator.Next)
  3489  	assert.NotNil(t, aggregator.Next.Next)
  3490  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3491  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3492  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "myField")
  3493  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3494  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMStringExpr)
  3495  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr)
  3496  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr)
  3497  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.Op, "ltrim")
  3498  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.StrToRemove, "Ma")
  3499  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.Value.FieldName, "state")
  3500  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.Value.StringExprMode), structs.SEMField)
  3501  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].IsField, false)
  3502  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].Value, " test end")
  3503  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].IsField, false)
  3504  }
  3505  
  3506  func Test_evalFunctionsRtrim(t *testing.T) {
  3507  	query := []byte(`city=Boston | stats count AS Count BY state | eval myField=state . " start:" . rtrim(state, "nd")`)
  3508  	res, err := spl.Parse("", query)
  3509  	assert.Nil(t, err)
  3510  	filterNode := res.(ast.QueryStruct).SearchFilter
  3511  	assert.NotNil(t, filterNode)
  3512  
  3513  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3514  	assert.Nil(t, err)
  3515  	assert.NotNil(t, astNode)
  3516  	assert.NotNil(t, aggregator)
  3517  	assert.NotNil(t, aggregator.Next)
  3518  	assert.NotNil(t, aggregator.Next.Next)
  3519  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3520  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3521  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "myField")
  3522  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3523  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMStringExpr)
  3524  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr)
  3525  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr)
  3526  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].Value, "state")
  3527  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].IsField, true)
  3528  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].Value, " start:")
  3529  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].IsField, false)
  3530  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[2].TextExpr.Op, "rtrim")
  3531  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[2].TextExpr.Value.FieldName, "state")
  3532  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[2].TextExpr.Value.StringExprMode), structs.SEMField)
  3533  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[2].TextExpr.StrToRemove, "nd")
  3534  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[2].IsField, false)
  3535  }
  3536  
  3537  func Test_evalFunctionsIf(t *testing.T) {
  3538  	query := []byte(`city=Boston | stats count AS Count BY http_status | eval myField=if(http_status > 400, http_status, "Error")`)
  3539  	res, err := spl.Parse("", query)
  3540  	assert.Nil(t, err)
  3541  	filterNode := res.(ast.QueryStruct).SearchFilter
  3542  	assert.NotNil(t, filterNode)
  3543  
  3544  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3545  	assert.Nil(t, err)
  3546  	assert.NotNil(t, astNode)
  3547  	assert.NotNil(t, aggregator)
  3548  	assert.NotNil(t, aggregator.Next)
  3549  	assert.NotNil(t, aggregator.Next.Next)
  3550  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3551  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3552  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "myField")
  3553  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3554  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMConditionExpr)
  3555  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr)
  3556  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.Op, "if")
  3557  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr)
  3558  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.IsTerminal, true)
  3559  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.IsTerminal, true)
  3560  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.ValueIsField, true)
  3561  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.Value, "http_status")
  3562  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.RightValue.NumericExpr.IsTerminal, true)
  3563  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.RightValue.NumericExpr.ValueIsField, false)
  3564  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.RightValue.NumericExpr.Value, "400")
  3565  
  3566  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.TrueValue.NumericExpr.IsTerminal, true)
  3567  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.TrueValue.NumericExpr.ValueIsField, true)
  3568  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.TrueValue.NumericExpr.Value, "http_status")
  3569  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.FalseValue.StringExpr.RawString, "Error")
  3570  }
  3571  
  3572  func Test_evalFunctionsIn(t *testing.T) {
  3573  	query := []byte(`city=Boston | stats count AS Count BY http_status | where http_status in(404, 301, "abc")`)
  3574  	res, err := spl.Parse("", query)
  3575  	assert.Nil(t, err)
  3576  	filterNode := res.(ast.QueryStruct).SearchFilter
  3577  	assert.NotNil(t, filterNode)
  3578  
  3579  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3580  	assert.Nil(t, err)
  3581  	assert.NotNil(t, astNode)
  3582  	assert.NotNil(t, aggregator)
  3583  	assert.NotNil(t, aggregator.Next)
  3584  	assert.NotNil(t, aggregator.Next.Next)
  3585  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3586  	assert.Nil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3587  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.FilterRows)
  3588  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.IsTerminal, true)
  3589  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.FilterRows.LeftValue)
  3590  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.FilterRows.LeftValue.NumericExpr)
  3591  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.LeftValue.NumericExpr.IsTerminal, true)
  3592  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.LeftValue.NumericExpr.ValueIsField, true)
  3593  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.FilterRows.LeftValue.NumericExpr.NumericExprMode), structs.NEMNumberField)
  3594  	assert.Nil(t, aggregator.Next.Next.OutputTransforms.FilterRows.RightValue)
  3595  
  3596  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.ValueOp, "in")
  3597  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.FilterRows.ValueList)
  3598  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.ValueList[0].NumericExpr.IsTerminal, true)
  3599  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.ValueList[0].NumericExpr.ValueIsField, false)
  3600  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.ValueList[0].NumericExpr.Value, "404")
  3601  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.ValueList[1].NumericExpr.IsTerminal, true)
  3602  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.ValueList[1].NumericExpr.ValueIsField, false)
  3603  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.ValueList[1].NumericExpr.Value, "301")
  3604  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.FilterRows.ValueList[2].StringExpr.RawString, "abc")
  3605  }
  3606  
  3607  func Test_evalFunctionsIfAndIn(t *testing.T) {
  3608  	query := []byte(`city=Boston | stats count AS Count BY state | eval myField=if(in(state, "Mary" . "land", "Hawaii", 99 + 1), state, "Error")`)
  3609  	res, err := spl.Parse("", query)
  3610  	assert.Nil(t, err)
  3611  	filterNode := res.(ast.QueryStruct).SearchFilter
  3612  	assert.NotNil(t, filterNode)
  3613  
  3614  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3615  	assert.Nil(t, err)
  3616  	assert.NotNil(t, astNode)
  3617  	assert.NotNil(t, aggregator)
  3618  	assert.NotNil(t, aggregator.Next)
  3619  	assert.NotNil(t, aggregator.Next.Next)
  3620  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3621  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3622  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "myField")
  3623  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3624  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMConditionExpr)
  3625  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr)
  3626  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.Op, "if")
  3627  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr)
  3628  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueOp, "in")
  3629  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.IsTerminal, true)
  3630  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.IsTerminal, true)
  3631  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.ValueIsField, true)
  3632  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.Value, "state")
  3633  	assert.Nil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.RightValue)
  3634  
  3635  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueList[0].StringExpr.ConcatExpr.Atoms[0].IsField, false)
  3636  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueList[0].StringExpr.ConcatExpr.Atoms[0].Value, "Mary")
  3637  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueList[0].StringExpr.ConcatExpr.Atoms[1].IsField, false)
  3638  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueList[0].StringExpr.ConcatExpr.Atoms[1].Value, "land")
  3639  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueList[1].StringExpr.RawString, "Hawaii")
  3640  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueList[2].NumericExpr.Op, "+")
  3641  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueList[2].NumericExpr.Left.IsTerminal, true)
  3642  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueList[2].NumericExpr.Left.ValueIsField, false)
  3643  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueList[2].NumericExpr.Left.Value, "99")
  3644  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueList[2].NumericExpr.Right.IsTerminal, true)
  3645  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueList[2].NumericExpr.Right.ValueIsField, false)
  3646  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueList[2].NumericExpr.Right.Value, "1")
  3647  
  3648  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.TrueValue.NumericExpr.IsTerminal, true)
  3649  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.TrueValue.NumericExpr.ValueIsField, true)
  3650  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.TrueValue.NumericExpr.Value, "state")
  3651  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.FalseValue.StringExpr.RawString, "Error")
  3652  }
  3653  
  3654  func Test_evalFunctionsCidrmatch(t *testing.T) {
  3655  	query := []byte(`city=Boston | stats count AS Count BY http_status | eval result=if(cidrmatch("192.0.2.0/24", "192.0.2.5"), "local", "not local")`)
  3656  	res, err := spl.Parse("", query)
  3657  	assert.Nil(t, err)
  3658  	filterNode := res.(ast.QueryStruct).SearchFilter
  3659  	assert.NotNil(t, filterNode)
  3660  
  3661  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3662  	assert.Nil(t, err)
  3663  	assert.NotNil(t, astNode)
  3664  	assert.NotNil(t, aggregator)
  3665  	assert.NotNil(t, aggregator.Next)
  3666  	assert.NotNil(t, aggregator.Next.Next)
  3667  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3668  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3669  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  3670  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3671  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMConditionExpr)
  3672  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr)
  3673  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.Op, "if")
  3674  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr)
  3675  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueOp, "cidrmatch")
  3676  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.IsTerminal, true)
  3677  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.StringExpr.RawString, "192.0.2.0/24")
  3678  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.RightValue.StringExpr.RawString, "192.0.2.5")
  3679  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.TrueValue.StringExpr.RawString, "local")
  3680  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.FalseValue.StringExpr.RawString, "not local")
  3681  
  3682  }
  3683  func Test_evalFunctionsIfAndIsString(t *testing.T) {
  3684  	query := []byte(`city=Boston | stats count AS Count BY country | eval result=if(isstr(country), "This is a string", "This is not a string")`)
  3685  	res, err := spl.Parse("", query)
  3686  	assert.Nil(t, err)
  3687  	filterNode := res.(ast.QueryStruct).SearchFilter
  3688  	assert.NotNil(t, filterNode)
  3689  
  3690  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3691  	assert.Nil(t, err)
  3692  	assert.NotNil(t, astNode)
  3693  	assert.NotNil(t, aggregator)
  3694  	assert.NotNil(t, aggregator.Next)
  3695  	assert.NotNil(t, aggregator.Next.Next)
  3696  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3697  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3698  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  3699  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3700  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMConditionExpr)
  3701  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr)
  3702  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.Op, "if")
  3703  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr)
  3704  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueOp, "isstr")
  3705  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.IsTerminal, true)
  3706  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.IsTerminal, true)
  3707  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.ValueIsField, true)
  3708  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.Value, "country")
  3709  	assert.Nil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.RightValue)
  3710  
  3711  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.TrueValue.StringExpr.RawString, "This is a string")
  3712  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.FalseValue.StringExpr.RawString, "This is not a string")
  3713  
  3714  }
  3715  
  3716  func Test_evalFunctionsIfAndIsInt(t *testing.T) {
  3717  	query := []byte(`city=Boston | stats count AS Count BY http_status | eval result=if(isint(http_status), "This is an integer", "This is not an integer")`)
  3718  	res, err := spl.Parse("", query)
  3719  	assert.Nil(t, err)
  3720  	filterNode := res.(ast.QueryStruct).SearchFilter
  3721  	assert.NotNil(t, filterNode)
  3722  
  3723  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3724  	assert.Nil(t, err)
  3725  	assert.NotNil(t, astNode)
  3726  	assert.NotNil(t, aggregator)
  3727  	assert.NotNil(t, aggregator.Next)
  3728  	assert.NotNil(t, aggregator.Next.Next)
  3729  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3730  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3731  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  3732  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3733  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMConditionExpr)
  3734  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr)
  3735  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.Op, "if")
  3736  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr)
  3737  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueOp, "isint")
  3738  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.IsTerminal, true)
  3739  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.IsTerminal, true)
  3740  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.ValueIsField, true)
  3741  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.Value, "http_status")
  3742  	assert.Nil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.RightValue)
  3743  
  3744  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.TrueValue.StringExpr.RawString, "This is an integer")
  3745  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.FalseValue.StringExpr.RawString, "This is not an integer")
  3746  
  3747  }
  3748  func Test_evalFunctionsIfAndIsBool(t *testing.T) {
  3749  	query := []byte(`city=Boston | stats count AS Count BY city | eval result=if(isbool(city), "This is a boolean value", "This is not a boolean value")`)
  3750  	res, err := spl.Parse("", query)
  3751  	assert.Nil(t, err)
  3752  	filterNode := res.(ast.QueryStruct).SearchFilter
  3753  	assert.NotNil(t, filterNode)
  3754  
  3755  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3756  	assert.Nil(t, err)
  3757  	assert.NotNil(t, astNode)
  3758  	assert.NotNil(t, aggregator)
  3759  	assert.NotNil(t, aggregator.Next)
  3760  	assert.NotNil(t, aggregator.Next.Next)
  3761  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3762  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3763  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  3764  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3765  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMConditionExpr)
  3766  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr)
  3767  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.Op, "if")
  3768  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr)
  3769  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueOp, "isbool")
  3770  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.IsTerminal, true)
  3771  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.IsTerminal, true)
  3772  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.ValueIsField, true)
  3773  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.Value, "city")
  3774  	assert.Nil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.RightValue)
  3775  
  3776  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.TrueValue.StringExpr.RawString, "This is a boolean value")
  3777  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.FalseValue.StringExpr.RawString, "This is not a boolean value")
  3778  
  3779  }
  3780  
  3781  func Test_evalFunctionsIfAndIsNull(t *testing.T) {
  3782  	query := []byte(`city=Boston | stats count AS Count BY state | eval result=if(isnull(state), "This is a null value", "This is not a null value")`)
  3783  	res, err := spl.Parse("", query)
  3784  	assert.Nil(t, err)
  3785  	filterNode := res.(ast.QueryStruct).SearchFilter
  3786  	assert.NotNil(t, filterNode)
  3787  
  3788  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3789  	assert.Nil(t, err)
  3790  	assert.NotNil(t, astNode)
  3791  	assert.NotNil(t, aggregator)
  3792  	assert.NotNil(t, aggregator.Next)
  3793  	assert.NotNil(t, aggregator.Next.Next)
  3794  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3795  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3796  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  3797  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3798  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMConditionExpr)
  3799  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr)
  3800  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.Op, "if")
  3801  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr)
  3802  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueOp, "isnull")
  3803  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.IsTerminal, true)
  3804  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.IsTerminal, true)
  3805  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.ValueIsField, true)
  3806  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.Value, "state")
  3807  	assert.Nil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.RightValue)
  3808  
  3809  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.TrueValue.StringExpr.RawString, "This is a null value")
  3810  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.FalseValue.StringExpr.RawString, "This is not a null value")
  3811  
  3812  }
  3813  
  3814  func Test_evalFunctionsLike(t *testing.T) {
  3815  	query := []byte(`city=Boston | stats count AS Count BY http_status | eval result=if(like(http_status, "4%"), "True", "False")`)
  3816  	res, err := spl.Parse("", query)
  3817  	assert.Nil(t, err)
  3818  	filterNode := res.(ast.QueryStruct).SearchFilter
  3819  	assert.NotNil(t, filterNode)
  3820  
  3821  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3822  	assert.Nil(t, err)
  3823  	assert.NotNil(t, astNode)
  3824  	assert.NotNil(t, aggregator)
  3825  	assert.NotNil(t, aggregator.Next)
  3826  	assert.NotNil(t, aggregator.Next.Next)
  3827  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3828  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3829  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  3830  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3831  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMConditionExpr)
  3832  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr)
  3833  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.Op, "if")
  3834  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr)
  3835  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueOp, "like")
  3836  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.IsTerminal, true)
  3837  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.ValueIsField, true)
  3838  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.Value, "http_status")
  3839  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.RightValue.StringExpr.RawString, "4%")
  3840  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.TrueValue.StringExpr.RawString, "True")
  3841  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.FalseValue.StringExpr.RawString, "False")
  3842  
  3843  }
  3844  
  3845  func Test_evalFunctionsMatch(t *testing.T) {
  3846  	query := []byte(`city=Boston | stats count AS Count BY country | eval result=if(match(country, "^Sa"), "yes", "no")`)
  3847  	res, err := spl.Parse("", query)
  3848  	assert.Nil(t, err)
  3849  	filterNode := res.(ast.QueryStruct).SearchFilter
  3850  	assert.NotNil(t, filterNode)
  3851  
  3852  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3853  	assert.Nil(t, err)
  3854  	assert.NotNil(t, astNode)
  3855  	assert.NotNil(t, aggregator)
  3856  	assert.NotNil(t, aggregator.Next)
  3857  	assert.NotNil(t, aggregator.Next.Next)
  3858  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3859  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3860  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  3861  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3862  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMConditionExpr)
  3863  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr)
  3864  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.Op, "if")
  3865  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr)
  3866  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.ValueOp, "match")
  3867  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.IsTerminal, true)
  3868  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.ValueIsField, true)
  3869  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.LeftValue.NumericExpr.Value, "country")
  3870  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.BoolExpr.RightValue.StringExpr.RawString, "^Sa")
  3871  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.TrueValue.StringExpr.RawString, "yes")
  3872  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ConditionExpr.FalseValue.StringExpr.RawString, "no")
  3873  
  3874  }
  3875  
  3876  func Test_evalFunctionsUrldecode(t *testing.T) {
  3877  	query := []byte(`city=Boston | stats count AS Count BY http_status | eval result=urldecode("http%3A%2F%2Fwww.splunk.com%2Fdownload%3Fr%3Dheader")`)
  3878  	res, err := spl.Parse("", query)
  3879  	assert.Nil(t, err)
  3880  	filterNode := res.(ast.QueryStruct).SearchFilter
  3881  	assert.NotNil(t, filterNode)
  3882  
  3883  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3884  	assert.Nil(t, err)
  3885  	assert.NotNil(t, astNode)
  3886  	assert.NotNil(t, aggregator)
  3887  	assert.NotNil(t, aggregator.Next)
  3888  	assert.NotNil(t, aggregator.Next.Next)
  3889  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3890  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3891  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  3892  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3893  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMStringExpr)
  3894  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr)
  3895  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr)
  3896  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Op, "urldecode")
  3897  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Value)
  3898  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Value.RawString, "http%3A%2F%2Fwww.splunk.com%2Fdownload%3Fr%3Dheader")
  3899  
  3900  }
  3901  
  3902  func Test_evalFunctionsSplit(t *testing.T) {
  3903  	query := []byte(`city=Boston | stats count AS Count BY ident | eval result=split(ident,"-")`)
  3904  	res, err := spl.Parse("", query)
  3905  	assert.Nil(t, err)
  3906  	filterNode := res.(ast.QueryStruct).SearchFilter
  3907  	assert.NotNil(t, filterNode)
  3908  
  3909  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3910  	assert.Nil(t, err)
  3911  	assert.NotNil(t, astNode)
  3912  	assert.NotNil(t, aggregator)
  3913  	assert.NotNil(t, aggregator.Next)
  3914  	assert.NotNil(t, aggregator.Next.Next)
  3915  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3916  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3917  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  3918  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3919  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMStringExpr)
  3920  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Op, "split")
  3921  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Value)
  3922  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Value.StringExprMode), structs.SEMField)
  3923  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Value.FieldName, "ident")
  3924  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Delimiter)
  3925  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Delimiter.RawString, "-")
  3926  
  3927  }
  3928  
  3929  func Test_evalFunctionsNow(t *testing.T) {
  3930  	query := []byte(`city=Boston | stats count AS Count BY ident | eval result=now()`)
  3931  	res, err := spl.Parse("", query)
  3932  	assert.Nil(t, err)
  3933  	filterNode := res.(ast.QueryStruct).SearchFilter
  3934  	assert.NotNil(t, filterNode)
  3935  
  3936  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3937  	assert.Nil(t, err)
  3938  	assert.NotNil(t, astNode)
  3939  	assert.NotNil(t, aggregator)
  3940  	assert.NotNil(t, aggregator.Next)
  3941  	assert.NotNil(t, aggregator.Next.Next)
  3942  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3943  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3944  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  3945  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3946  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMNumericExpr)
  3947  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr)
  3948  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.NumericExprMode), structs.NEMNumber)
  3949  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.IsTerminal, true)
  3950  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Op, "now")
  3951  
  3952  }
  3953  
  3954  func Test_evalFunctionsMax(t *testing.T) {
  3955  	query := []byte(`city=Boston | stats count AS Count BY http_status | eval result=max(1, 3, 450, http_status)`)
  3956  	res, err := spl.Parse("", query)
  3957  	assert.Nil(t, err)
  3958  	filterNode := res.(ast.QueryStruct).SearchFilter
  3959  	assert.NotNil(t, filterNode)
  3960  
  3961  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3962  	assert.Nil(t, err)
  3963  	assert.NotNil(t, astNode)
  3964  	assert.NotNil(t, aggregator)
  3965  	assert.NotNil(t, aggregator.Next)
  3966  	assert.NotNil(t, aggregator.Next.Next)
  3967  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3968  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3969  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  3970  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3971  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMStringExpr)
  3972  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Op, "max")
  3973  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues)
  3974  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues[0].ConcatExpr.Atoms[0].IsField, false)
  3975  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues[0].ConcatExpr.Atoms[0].Value, "1")
  3976  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues[1].ConcatExpr.Atoms[0].IsField, false)
  3977  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues[1].ConcatExpr.Atoms[0].Value, "3")
  3978  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues[2].ConcatExpr.Atoms[0].IsField, false)
  3979  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues[2].ConcatExpr.Atoms[0].Value, "450")
  3980  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues[3].FieldName, "http_status")
  3981  }
  3982  func Test_evalFunctionsMin(t *testing.T) {
  3983  	query := []byte(`city=Boston | stats count AS Count BY http_status | eval result=min(1, 3, 450, http_status)`)
  3984  	res, err := spl.Parse("", query)
  3985  	assert.Nil(t, err)
  3986  	filterNode := res.(ast.QueryStruct).SearchFilter
  3987  	assert.NotNil(t, filterNode)
  3988  
  3989  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  3990  	assert.Nil(t, err)
  3991  	assert.NotNil(t, astNode)
  3992  	assert.NotNil(t, aggregator)
  3993  	assert.NotNil(t, aggregator.Next)
  3994  	assert.NotNil(t, aggregator.Next.Next)
  3995  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  3996  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  3997  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  3998  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  3999  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMStringExpr)
  4000  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Op, "min")
  4001  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues)
  4002  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues[0].ConcatExpr.Atoms[0].IsField, false)
  4003  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues[0].ConcatExpr.Atoms[0].Value, "1")
  4004  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues[1].ConcatExpr.Atoms[0].IsField, false)
  4005  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues[1].ConcatExpr.Atoms[0].Value, "3")
  4006  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues[2].ConcatExpr.Atoms[0].IsField, false)
  4007  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues[2].ConcatExpr.Atoms[0].Value, "450")
  4008  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.MaxMinValues[3].FieldName, "http_status")
  4009  }
  4010  
  4011  func Test_evalFunctionsSubstr(t *testing.T) {
  4012  	query := []byte(`city=Boston | stats count AS Count BY state | eval result=substr("splendid", 1, 3) . substr("chunk", -3)`)
  4013  	res, err := spl.Parse("", query)
  4014  	assert.Nil(t, err)
  4015  	filterNode := res.(ast.QueryStruct).SearchFilter
  4016  	assert.NotNil(t, filterNode)
  4017  
  4018  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4019  	assert.Nil(t, err)
  4020  	assert.NotNil(t, astNode)
  4021  	assert.NotNil(t, aggregator)
  4022  	assert.NotNil(t, aggregator.Next)
  4023  	assert.NotNil(t, aggregator.Next.Next)
  4024  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4025  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  4026  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  4027  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  4028  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMStringExpr)
  4029  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr)
  4030  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr)
  4031  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr)
  4032  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].IsField, false)
  4033  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.IsTerminal, false)
  4034  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.Op, "substr")
  4035  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.Value)
  4036  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.Value.StringExprMode), structs.SEMRawString)
  4037  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.Value.RawString, "splendid")
  4038  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.StartIndex)
  4039  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.StartIndex.NumericExprMode), structs.NEMNumber)
  4040  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.StartIndex.IsTerminal, true)
  4041  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.StartIndex.ValueIsField, false)
  4042  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.StartIndex.Value, "1")
  4043  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.LengthExpr)
  4044  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.LengthExpr.NumericExprMode), structs.NEMNumber)
  4045  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.LengthExpr.IsTerminal, true)
  4046  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.LengthExpr.ValueIsField, false)
  4047  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].TextExpr.LengthExpr.Value, "3")
  4048  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].IsField, false)
  4049  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].TextExpr.IsTerminal, false)
  4050  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].TextExpr.Op, "substr")
  4051  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].TextExpr.Value)
  4052  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].TextExpr.Value.StringExprMode), structs.SEMRawString)
  4053  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].TextExpr.Value.RawString, "chunk")
  4054  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].TextExpr.StartIndex)
  4055  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].TextExpr.StartIndex.NumericExprMode), structs.NEMNumber)
  4056  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].TextExpr.StartIndex.IsTerminal, true)
  4057  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].TextExpr.StartIndex.ValueIsField, false)
  4058  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].TextExpr.StartIndex.Value, "-3")
  4059  }
  4060  
  4061  func Test_evalFunctionsToStringBooleanValue(t *testing.T) {
  4062  	query := []byte(`city=Boston | stats count AS Count BY state | eval result=tostring((2 > 1))`)
  4063  	res, err := spl.Parse("", query)
  4064  	assert.Nil(t, err)
  4065  	filterNode := res.(ast.QueryStruct).SearchFilter
  4066  	assert.NotNil(t, filterNode)
  4067  
  4068  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4069  	assert.Nil(t, err)
  4070  	assert.NotNil(t, astNode)
  4071  	assert.NotNil(t, aggregator)
  4072  	assert.NotNil(t, aggregator.Next)
  4073  	assert.NotNil(t, aggregator.Next.Next)
  4074  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4075  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  4076  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  4077  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  4078  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMStringExpr)
  4079  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Op, "tostring")
  4080  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val)
  4081  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.ValueExprMode), structs.VEMBooleanExpr)
  4082  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.BooleanExpr.ValueOp, ">")
  4083  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.BooleanExpr.IsTerminal, true)
  4084  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.BooleanExpr.LeftValue.NumericExpr.IsTerminal, true)
  4085  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.BooleanExpr.LeftValue.NumericExpr.ValueIsField, false)
  4086  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.BooleanExpr.LeftValue.NumericExpr.Value, "2")
  4087  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.BooleanExpr.RightValue.NumericExpr.IsTerminal, true)
  4088  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.BooleanExpr.RightValue.NumericExpr.ValueIsField, false)
  4089  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.BooleanExpr.RightValue.NumericExpr.Value, "1")
  4090  }
  4091  
  4092  func Test_evalFunctionsToStringHex(t *testing.T) {
  4093  	query := []byte(`city=Boston | stats count AS Count BY state | eval result=tostring(15,"hex")`)
  4094  	res, err := spl.Parse("", query)
  4095  	assert.Nil(t, err)
  4096  	filterNode := res.(ast.QueryStruct).SearchFilter
  4097  	assert.NotNil(t, filterNode)
  4098  
  4099  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4100  	assert.Nil(t, err)
  4101  	assert.NotNil(t, astNode)
  4102  	assert.NotNil(t, aggregator)
  4103  	assert.NotNil(t, aggregator.Next)
  4104  	assert.NotNil(t, aggregator.Next.Next)
  4105  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4106  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  4107  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  4108  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  4109  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMStringExpr)
  4110  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Op, "tostring")
  4111  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val)
  4112  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.ValueExprMode), structs.VEMNumericExpr)
  4113  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.NumericExpr.IsTerminal, true)
  4114  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.NumericExpr.ValueIsField, false)
  4115  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.NumericExpr.Value, "15")
  4116  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Format.StringExprMode), structs.SEMRawString)
  4117  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Format.RawString, "hex")
  4118  }
  4119  
  4120  func Test_evalFunctionsToStringCommas(t *testing.T) {
  4121  	query := []byte(`city=Boston | stats count AS Count BY state | eval result=tostring(12345.6789,"commas")`)
  4122  	res, err := spl.Parse("", query)
  4123  	assert.Nil(t, err)
  4124  	filterNode := res.(ast.QueryStruct).SearchFilter
  4125  	assert.NotNil(t, filterNode)
  4126  
  4127  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4128  	assert.Nil(t, err)
  4129  	assert.NotNil(t, astNode)
  4130  	assert.NotNil(t, aggregator)
  4131  	assert.NotNil(t, aggregator.Next)
  4132  	assert.NotNil(t, aggregator.Next.Next)
  4133  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4134  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  4135  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  4136  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  4137  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMStringExpr)
  4138  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Op, "tostring")
  4139  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val)
  4140  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.ValueExprMode), structs.VEMNumericExpr)
  4141  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.NumericExpr.IsTerminal, true)
  4142  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.NumericExpr.ValueIsField, false)
  4143  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.NumericExpr.Value, "12345.6789")
  4144  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Format.StringExprMode), structs.SEMRawString)
  4145  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Format.RawString, "commas")
  4146  }
  4147  
  4148  func Test_evalFunctionsToStringDuration(t *testing.T) {
  4149  	query := []byte(`city=Boston | stats count AS Count BY state | eval result=tostring(615,"duration")`)
  4150  	res, err := spl.Parse("", query)
  4151  	assert.Nil(t, err)
  4152  	filterNode := res.(ast.QueryStruct).SearchFilter
  4153  	assert.NotNil(t, filterNode)
  4154  
  4155  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4156  	assert.Nil(t, err)
  4157  	assert.NotNil(t, astNode)
  4158  	assert.NotNil(t, aggregator)
  4159  	assert.NotNil(t, aggregator.Next)
  4160  	assert.NotNil(t, aggregator.Next.Next)
  4161  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4162  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  4163  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  4164  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest)
  4165  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.ValueExprMode), structs.VEMStringExpr)
  4166  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Op, "tostring")
  4167  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val)
  4168  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.ValueExprMode), structs.VEMNumericExpr)
  4169  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.NumericExpr.IsTerminal, true)
  4170  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.NumericExpr.ValueIsField, false)
  4171  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Val.NumericExpr.Value, "615")
  4172  	assert.Equal(t, int(aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Format.StringExprMode), structs.SEMRawString)
  4173  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.TextExpr.Format.RawString, "duration")
  4174  }
  4175  
  4176  func Test_evalFunctionsExact(t *testing.T) {
  4177  	query := []byte(`city=Boston | stats count AS Count BY http_status | eval result=exact(3.14 * http_status)`)
  4178  	res, err := spl.Parse("", query)
  4179  	assert.Nil(t, err)
  4180  	filterNode := res.(ast.QueryStruct).SearchFilter
  4181  	assert.NotNil(t, filterNode)
  4182  
  4183  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4184  	assert.Nil(t, err)
  4185  	assert.NotNil(t, astNode)
  4186  	assert.NotNil(t, aggregator)
  4187  	assert.NotNil(t, aggregator.Next)
  4188  	assert.NotNil(t, aggregator.Next.Next)
  4189  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4190  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  4191  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  4192  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.IsTerminal, false)
  4193  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Op, "exact")
  4194  	assert.Nil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right, err)
  4195  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Op, "*")
  4196  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.IsTerminal, true)
  4197  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.ValueIsField, false)
  4198  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.Value, "3.14")
  4199  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.ValueIsField, true)
  4200  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.IsTerminal, true)
  4201  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Value, "http_status")
  4202  }
  4203  
  4204  func Test_evalFunctionsExp(t *testing.T) {
  4205  	query := []byte(`city=Boston | stats count AS Count BY http_status | eval result=exp(3)`)
  4206  	res, err := spl.Parse("", query)
  4207  	assert.Nil(t, err)
  4208  	filterNode := res.(ast.QueryStruct).SearchFilter
  4209  	assert.NotNil(t, filterNode)
  4210  
  4211  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4212  	assert.Nil(t, err)
  4213  	assert.NotNil(t, astNode)
  4214  	assert.NotNil(t, aggregator)
  4215  	assert.NotNil(t, aggregator.Next)
  4216  	assert.NotNil(t, aggregator.Next.Next)
  4217  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4218  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  4219  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "result")
  4220  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.IsTerminal, false)
  4221  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Op, "exp")
  4222  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.IsTerminal, true)
  4223  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.ValueIsField, false)
  4224  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Value, "3")
  4225  }
  4226  
  4227  func Test_ChainedEval(t *testing.T) {
  4228  	query := []byte(`search A=1 | stats max(latency) AS Max | eval Max=Max . " seconds" | eval Max="Max Latency: " . Max`)
  4229  	res, err := spl.Parse("", query)
  4230  	assert.Nil(t, err)
  4231  	filterNode := res.(ast.QueryStruct).SearchFilter
  4232  	assert.NotNil(t, filterNode)
  4233  
  4234  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4235  	assert.Nil(t, err)
  4236  	assert.NotNil(t, astNode)
  4237  	assert.NotNil(t, aggregator)
  4238  	assert.NotNil(t, aggregator.Next)
  4239  
  4240  	// Second agg is for renaming max(latency) to Max, the third is for the first eval.
  4241  	assert.NotNil(t, aggregator.Next.Next)
  4242  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4243  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  4244  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "Max")
  4245  	assert.Len(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms, 2)
  4246  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].IsField, true)
  4247  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].Value, "Max")
  4248  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].IsField, false)
  4249  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].Value, " seconds")
  4250  
  4251  	// The fourth agg is for the second eval.
  4252  	assert.NotNil(t, aggregator.Next.Next.Next)
  4253  	assert.Equal(t, aggregator.Next.Next.Next.PipeCommandType, structs.OutputTransformType)
  4254  	assert.NotNil(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns)
  4255  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.NewColName, "Max")
  4256  	assert.Len(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms, 2)
  4257  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].IsField, false)
  4258  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].Value, "Max Latency: ")
  4259  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].IsField, true)
  4260  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].Value, "Max")
  4261  }
  4262  
  4263  func Test_evalWithMultipleElements(t *testing.T) {
  4264  	query := []byte(`search A=1 | stats max(latency) AS Max | eval Max=Max . " seconds", Max="Max Latency: " . Max`)
  4265  	res, err := spl.Parse("", query)
  4266  	assert.Nil(t, err)
  4267  	filterNode := res.(ast.QueryStruct).SearchFilter
  4268  	assert.NotNil(t, filterNode)
  4269  
  4270  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4271  	assert.Nil(t, err)
  4272  	assert.NotNil(t, astNode)
  4273  	assert.NotNil(t, aggregator)
  4274  	assert.NotNil(t, aggregator.Next)
  4275  
  4276  	// Second agg is for renaming max(latency) to Max, the third is for the first statement in eval.
  4277  	assert.NotNil(t, aggregator.Next.Next)
  4278  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4279  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  4280  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "Max")
  4281  	assert.Len(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms, 2)
  4282  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].IsField, true)
  4283  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].Value, "Max")
  4284  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].IsField, false)
  4285  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].Value, " seconds")
  4286  
  4287  	// The fourth agg is for the second statement in eval.
  4288  	assert.NotNil(t, aggregator.Next.Next.Next)
  4289  	assert.Equal(t, aggregator.Next.Next.Next.PipeCommandType, structs.OutputTransformType)
  4290  	assert.NotNil(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns)
  4291  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.NewColName, "Max")
  4292  	assert.Len(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms, 2)
  4293  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].IsField, false)
  4294  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].Value, "Max Latency: ")
  4295  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].IsField, true)
  4296  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].Value, "Max")
  4297  }
  4298  
  4299  func Test_evalWithMultipleSpaces(t *testing.T) {
  4300  	query := []byte(`search      A    =   1  |   stats   max(  latency  )   AS   Max | eval Max  =  Max   .  " seconds", Max="Max Latency: " . Max`)
  4301  	res, err := spl.Parse("", query)
  4302  	assert.Nil(t, err)
  4303  	filterNode := res.(ast.QueryStruct).SearchFilter
  4304  	assert.NotNil(t, filterNode)
  4305  
  4306  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4307  	assert.Nil(t, err)
  4308  	assert.NotNil(t, astNode)
  4309  	assert.NotNil(t, aggregator)
  4310  	assert.NotNil(t, aggregator.Next)
  4311  
  4312  	// Second agg is for renaming max(latency) to Max, the third is for the first statement in eval.
  4313  	assert.NotNil(t, aggregator.Next.Next)
  4314  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4315  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  4316  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "Max")
  4317  	assert.Len(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms, 2)
  4318  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].IsField, true)
  4319  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].Value, "Max")
  4320  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].IsField, false)
  4321  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].Value, " seconds")
  4322  
  4323  	// The fourth agg is for the second statement in eval.
  4324  	assert.NotNil(t, aggregator.Next.Next.Next)
  4325  	assert.Equal(t, aggregator.Next.Next.Next.PipeCommandType, structs.OutputTransformType)
  4326  	assert.NotNil(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns)
  4327  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.NewColName, "Max")
  4328  	assert.Len(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms, 2)
  4329  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].IsField, false)
  4330  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[0].Value, "Max Latency: ")
  4331  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].IsField, true)
  4332  	assert.Equal(t, aggregator.Next.Next.Next.OutputTransforms.LetColumns.ValueColRequest.StringExpr.ConcatExpr.Atoms[1].Value, "Max")
  4333  }
  4334  
  4335  func Test_evalWithMultipleSpaces2(t *testing.T) {
  4336  	query := []byte(`search   A   =   1   OR  ( B =  2  AND   C =  3)`)
  4337  	res, err := spl.Parse("", query)
  4338  	assert.Nil(t, err)
  4339  	filterNode := res.(ast.QueryStruct).SearchFilter
  4340  
  4341  	assert.NotNil(t, filterNode)
  4342  	assert.Equal(t, filterNode.NodeType, ast.NodeOr)
  4343  	assert.Equal(t, filterNode.Right.NodeType, ast.NodeAnd)
  4344  
  4345  	assert.Equal(t, filterNode.Left.NodeType, ast.NodeTerminal)
  4346  	assert.Equal(t, filterNode.Left.Comparison.Field, "A")
  4347  	assert.Equal(t, filterNode.Left.Comparison.Op, "=")
  4348  	assert.Equal(t, filterNode.Left.Comparison.Values, json.Number("1"))
  4349  
  4350  	assert.Equal(t, filterNode.Right.Left.NodeType, ast.NodeTerminal)
  4351  	assert.Equal(t, filterNode.Right.Left.Comparison.Field, "B")
  4352  	assert.Equal(t, filterNode.Right.Left.Comparison.Op, "=")
  4353  	assert.Equal(t, filterNode.Right.Left.Comparison.Values, json.Number("2"))
  4354  
  4355  	assert.Equal(t, filterNode.Right.Right.NodeType, ast.NodeTerminal)
  4356  	assert.Equal(t, filterNode.Right.Right.Comparison.Field, "C")
  4357  	assert.Equal(t, filterNode.Right.Right.Comparison.Op, "=")
  4358  	assert.Equal(t, filterNode.Right.Right.Comparison.Values, json.Number("3"))
  4359  
  4360  	astNode := &structs.ASTNode{}
  4361  	err = pipesearch.SearchQueryToASTnode(filterNode, astNode, 0)
  4362  	assert.Nil(t, err)
  4363  	assert.NotNil(t, astNode.OrFilterCondition.FilterCriteria)
  4364  	assert.Len(t, astNode.OrFilterCondition.FilterCriteria, 1)
  4365  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "A")
  4366  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  4367  	assert.Equal(t, astNode.OrFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(1))
  4368  	assert.Len(t, astNode.OrFilterCondition.NestedNodes, 1)
  4369  	assert.Len(t, astNode.OrFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria, 2)
  4370  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[0].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "B")
  4371  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[0].ExpressionFilter.FilterOperator, utils.Equals)
  4372  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[0].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(2))
  4373  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[1].ExpressionFilter.LeftInput.Expression.LeftInput.ColumnName, "C")
  4374  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[1].ExpressionFilter.FilterOperator, utils.Equals)
  4375  	assert.Equal(t, astNode.OrFilterCondition.NestedNodes[0].AndFilterCondition.FilterCriteria[1].ExpressionFilter.RightInput.Expression.LeftInput.ColumnValue.UnsignedVal, uint64(3))
  4376  }
  4377  
  4378  func Test_SimpleNumericEval(t *testing.T) {
  4379  	query := []byte(`search A=1 | stats count AS Count | eval Thousands=Count / 1000`)
  4380  	res, err := spl.Parse("", query)
  4381  	assert.Nil(t, err)
  4382  	filterNode := res.(ast.QueryStruct).SearchFilter
  4383  	assert.NotNil(t, filterNode)
  4384  
  4385  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4386  	assert.Nil(t, err)
  4387  	assert.NotNil(t, astNode)
  4388  	assert.NotNil(t, aggregator)
  4389  	assert.NotNil(t, aggregator.Next)
  4390  
  4391  	// Second agg is for renaming count(*) to Count, the third is for eval.
  4392  	assert.NotNil(t, aggregator.Next.Next)
  4393  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4394  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  4395  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "Thousands")
  4396  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr)
  4397  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.IsTerminal, false)
  4398  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Op, "/")
  4399  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.IsTerminal, true)
  4400  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.ValueIsField, true)
  4401  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Value, "Count")
  4402  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.IsTerminal, true)
  4403  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.ValueIsField, false)
  4404  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.Value, "1000")
  4405  }
  4406  
  4407  func Test_NumericEvalLeftAssociative(t *testing.T) {
  4408  	query := []byte(`search A=1 | stats count AS Count | eval Custom=100 - Count - 40.5`)
  4409  	res, err := spl.Parse("", query)
  4410  	assert.Nil(t, err)
  4411  	filterNode := res.(ast.QueryStruct).SearchFilter
  4412  	assert.NotNil(t, filterNode)
  4413  
  4414  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4415  	assert.Nil(t, err)
  4416  	assert.NotNil(t, astNode)
  4417  	assert.NotNil(t, aggregator)
  4418  	assert.NotNil(t, aggregator.Next)
  4419  
  4420  	// Second agg is for renaming count(*) to Count, the third is for eval.
  4421  	// The node structure should be:
  4422  	//         Minus
  4423  	//         /   \
  4424  	//      Minus  40.5
  4425  	//      /   \
  4426  	//    100  Count
  4427  	assert.NotNil(t, aggregator.Next.Next)
  4428  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4429  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  4430  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "Custom")
  4431  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr)
  4432  
  4433  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.IsTerminal, false)
  4434  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Op, "-")
  4435  
  4436  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.IsTerminal, false)
  4437  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Op, "-")
  4438  
  4439  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.IsTerminal, true)
  4440  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.ValueIsField, false)
  4441  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.Value, "100")
  4442  
  4443  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.IsTerminal, true)
  4444  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.ValueIsField, true)
  4445  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Value, "Count")
  4446  
  4447  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.IsTerminal, true)
  4448  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.ValueIsField, false)
  4449  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.Value, "40.5")
  4450  }
  4451  
  4452  func Test_NumericEvalOrderOfOperations(t *testing.T) {
  4453  	query := []byte(`search A=1 | stats count AS Count | eval Custom=100 - 17 * 22 / 5 + 11`)
  4454  	res, err := spl.Parse("", query)
  4455  	assert.Nil(t, err)
  4456  	filterNode := res.(ast.QueryStruct).SearchFilter
  4457  	assert.NotNil(t, filterNode)
  4458  
  4459  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4460  	assert.Nil(t, err)
  4461  	assert.NotNil(t, astNode)
  4462  	assert.NotNil(t, aggregator)
  4463  	assert.NotNil(t, aggregator.Next)
  4464  
  4465  	// Second agg is for renaming count(*) to Count, the third is for eval.
  4466  	// The eval should be parsed as (100 - ((17 * 22) / 5)) + 11, which is:
  4467  	//         Plus
  4468  	//         /   \
  4469  	//      Minus   11
  4470  	//      /   \
  4471  	//    100  Divide
  4472  	//         /    \
  4473  	//      Times    5
  4474  	//      /   \
  4475  	//     17   22
  4476  	assert.NotNil(t, aggregator.Next.Next)
  4477  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4478  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  4479  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "Custom")
  4480  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr)
  4481  
  4482  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.IsTerminal, false)
  4483  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Op, "+")
  4484  
  4485  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.IsTerminal, false)
  4486  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Op, "-")
  4487  
  4488  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.IsTerminal, true)
  4489  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.ValueIsField, false)
  4490  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Left.Value, "100")
  4491  
  4492  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.IsTerminal, false)
  4493  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Op, "/")
  4494  
  4495  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Left.IsTerminal, false)
  4496  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Left.Op, "*")
  4497  
  4498  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Left.Left.IsTerminal, true)
  4499  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Left.Left.ValueIsField, false)
  4500  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Left.Left.Value, "17")
  4501  
  4502  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Left.Right.IsTerminal, true)
  4503  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Left.Right.ValueIsField, false)
  4504  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Left.Right.Value, "22")
  4505  
  4506  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Right.IsTerminal, true)
  4507  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Right.ValueIsField, false)
  4508  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Right.Right.Value, "5")
  4509  
  4510  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.IsTerminal, true)
  4511  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.ValueIsField, false)
  4512  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.Value, "11")
  4513  }
  4514  
  4515  func Test_NumericEvalParentheses(t *testing.T) {
  4516  	query := []byte(`search A=1 | stats count AS Count | eval Custom=22 * (100 - 17)`)
  4517  	res, err := spl.Parse("", query)
  4518  	assert.Nil(t, err)
  4519  	filterNode := res.(ast.QueryStruct).SearchFilter
  4520  	assert.NotNil(t, filterNode)
  4521  
  4522  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4523  	assert.Nil(t, err)
  4524  	assert.NotNil(t, astNode)
  4525  	assert.NotNil(t, aggregator)
  4526  	assert.NotNil(t, aggregator.Next)
  4527  
  4528  	// Second agg is for renaming count(*) to Count, the third is for eval.
  4529  	// The node structure should be:
  4530  	//      Times
  4531  	//      /   \
  4532  	//    22   Minus
  4533  	//         /   \
  4534  	//       100   17
  4535  	assert.NotNil(t, aggregator.Next.Next)
  4536  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4537  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns)
  4538  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.NewColName, "Custom")
  4539  	assert.NotNil(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr)
  4540  
  4541  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.IsTerminal, false)
  4542  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Op, "*")
  4543  
  4544  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.IsTerminal, true)
  4545  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.ValueIsField, false)
  4546  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Left.Value, "22")
  4547  
  4548  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.IsTerminal, false)
  4549  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.Op, "-")
  4550  
  4551  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.Left.IsTerminal, true)
  4552  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.Left.ValueIsField, false)
  4553  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.Left.Value, "100")
  4554  
  4555  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.Right.IsTerminal, true)
  4556  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.Right.ValueIsField, false)
  4557  	assert.Equal(t, aggregator.Next.Next.OutputTransforms.LetColumns.ValueColRequest.NumericExpr.Right.Right.Value, "17")
  4558  }
  4559  
  4560  func Test_WhereNumeric(t *testing.T) {
  4561  	query := []byte(`search A=1 | stats count AS Count | where 22 * (100 - 17) >= Count / 50`)
  4562  	res, err := spl.Parse("", query)
  4563  	assert.Nil(t, err)
  4564  	filterNode := res.(ast.QueryStruct).SearchFilter
  4565  	assert.NotNil(t, filterNode)
  4566  
  4567  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4568  	assert.Nil(t, err)
  4569  	assert.NotNil(t, astNode)
  4570  	assert.NotNil(t, aggregator)
  4571  	assert.NotNil(t, aggregator.Next)
  4572  
  4573  	// Second agg is for renaming count(*) to Count, the third is for eval.
  4574  	// The node structure should be:
  4575  	//             BoolExpr
  4576  	//            /   |    \
  4577  	//    ValueExpr   >=   ValueExpr
  4578  	//        |                |
  4579  	//      Times             Div
  4580  	//      /   \            /   \
  4581  	//    22   Minus      Count   50
  4582  	//         /   \
  4583  	//       100   17
  4584  	assert.NotNil(t, aggregator.Next.Next)
  4585  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4586  
  4587  	filterRows := aggregator.Next.Next.OutputTransforms.FilterRows
  4588  	assert.NotNil(t, filterRows)
  4589  	assert.Equal(t, filterRows.IsTerminal, true)
  4590  	assert.Equal(t, filterRows.ValueOp, ">=")
  4591  
  4592  	assert.Equal(t, int(filterRows.LeftValue.ValueExprMode), structs.VEMNumericExpr)
  4593  	assert.NotNil(t, filterRows.LeftValue.NumericExpr)
  4594  
  4595  	assert.Equal(t, filterRows.LeftValue.NumericExpr.IsTerminal, false)
  4596  	assert.Equal(t, filterRows.LeftValue.NumericExpr.Op, "*")
  4597  
  4598  	assert.Equal(t, filterRows.LeftValue.NumericExpr.Left.IsTerminal, true)
  4599  	assert.Equal(t, filterRows.LeftValue.NumericExpr.Left.ValueIsField, false)
  4600  	assert.Equal(t, filterRows.LeftValue.NumericExpr.Left.Value, "22")
  4601  
  4602  	assert.Equal(t, filterRows.LeftValue.NumericExpr.Right.IsTerminal, false)
  4603  	assert.Equal(t, filterRows.LeftValue.NumericExpr.Right.Op, "-")
  4604  
  4605  	assert.Equal(t, filterRows.LeftValue.NumericExpr.Right.Left.IsTerminal, true)
  4606  	assert.Equal(t, filterRows.LeftValue.NumericExpr.Right.Left.ValueIsField, false)
  4607  	assert.Equal(t, filterRows.LeftValue.NumericExpr.Right.Left.Value, "100")
  4608  
  4609  	assert.Equal(t, filterRows.LeftValue.NumericExpr.Right.Right.IsTerminal, true)
  4610  	assert.Equal(t, filterRows.LeftValue.NumericExpr.Right.Right.ValueIsField, false)
  4611  	assert.Equal(t, filterRows.LeftValue.NumericExpr.Right.Right.Value, "17")
  4612  
  4613  	assert.Equal(t, int(filterRows.RightValue.ValueExprMode), structs.VEMNumericExpr)
  4614  	assert.NotNil(t, filterRows.RightValue.NumericExpr)
  4615  
  4616  	assert.Equal(t, filterRows.RightValue.NumericExpr.IsTerminal, false)
  4617  	assert.Equal(t, filterRows.RightValue.NumericExpr.Op, "/")
  4618  
  4619  	assert.Equal(t, filterRows.RightValue.NumericExpr.Left.IsTerminal, true)
  4620  	assert.Equal(t, filterRows.RightValue.NumericExpr.Left.ValueIsField, true)
  4621  	assert.Equal(t, filterRows.RightValue.NumericExpr.Left.Value, "Count")
  4622  
  4623  	assert.Equal(t, filterRows.RightValue.NumericExpr.Right.IsTerminal, true)
  4624  	assert.Equal(t, filterRows.RightValue.NumericExpr.Right.ValueIsField, false)
  4625  	assert.Equal(t, filterRows.RightValue.NumericExpr.Right.Value, "50")
  4626  }
  4627  
  4628  func Test_WhereConcat(t *testing.T) {
  4629  	query := []byte(`search A=1 | stats count AS Count BY weekday | where Count = 10 . " items"`)
  4630  	res, err := spl.Parse("", query)
  4631  	assert.Nil(t, err)
  4632  	filterNode := res.(ast.QueryStruct).SearchFilter
  4633  	assert.NotNil(t, filterNode)
  4634  
  4635  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4636  	assert.Nil(t, err)
  4637  	assert.NotNil(t, astNode)
  4638  	assert.NotNil(t, aggregator)
  4639  	assert.NotNil(t, aggregator.Next)
  4640  
  4641  	// Second agg is for renaming count(*) to Count, the third is for eval.
  4642  	// The node structure should be:
  4643  	//             BoolExpr
  4644  	//            /   |    \
  4645  	//    ValueExpr   =   ValueExpr
  4646  	//        |               |
  4647  	//      Count         ConcatExpr
  4648  	//                      /   \
  4649  	//                    10   " items"
  4650  	assert.NotNil(t, aggregator.Next.Next)
  4651  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4652  
  4653  	filterRows := aggregator.Next.Next.OutputTransforms.FilterRows
  4654  	assert.NotNil(t, filterRows)
  4655  	assert.Equal(t, filterRows.IsTerminal, true)
  4656  	assert.Equal(t, filterRows.ValueOp, "=")
  4657  
  4658  	assert.Equal(t, int(filterRows.LeftValue.ValueExprMode), structs.VEMNumericExpr)
  4659  	assert.Equal(t, filterRows.LeftValue.NumericExpr.Value, "Count")
  4660  	assert.Equal(t, filterRows.LeftValue.NumericExpr.ValueIsField, true)
  4661  
  4662  	assert.Equal(t, int(filterRows.RightValue.ValueExprMode), structs.VEMStringExpr)
  4663  	assert.NotNil(t, filterRows.RightValue.StringExpr.ConcatExpr)
  4664  
  4665  	assert.Len(t, filterRows.RightValue.StringExpr.ConcatExpr.Atoms, 2)
  4666  	assert.Equal(t, filterRows.RightValue.StringExpr.ConcatExpr.Atoms[0].IsField, false)
  4667  	assert.Equal(t, filterRows.RightValue.StringExpr.ConcatExpr.Atoms[0].Value, "10")
  4668  	assert.Equal(t, filterRows.RightValue.StringExpr.ConcatExpr.Atoms[1].IsField, false)
  4669  	assert.Equal(t, filterRows.RightValue.StringExpr.ConcatExpr.Atoms[1].Value, " items")
  4670  }
  4671  
  4672  func Test_WhereBooleanOrderOfOperations(t *testing.T) {
  4673  	query := []byte(`search A=1 | stats count AS Count | where Count > 1 OR Count > 2 AND NOT (Count > 3) OR Count > 4`)
  4674  	res, err := spl.Parse("", query)
  4675  	assert.Nil(t, err)
  4676  	filterNode := res.(ast.QueryStruct).SearchFilter
  4677  	assert.NotNil(t, filterNode)
  4678  
  4679  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4680  	assert.Nil(t, err)
  4681  	assert.NotNil(t, astNode)
  4682  	assert.NotNil(t, aggregator)
  4683  	assert.NotNil(t, aggregator.Next)
  4684  
  4685  	// Second agg is for renaming count(*) to Count, the third is for eval.
  4686  	// This should be parsed as: (Count > 1 OR (Count > 2 AND NOT (Count > 3))) OR Count > 4
  4687  	// The node structure should be:
  4688  	//                          BoolExpr
  4689  	//                        /     |    \
  4690  	//                BoolExpr     OR     BoolExpr
  4691  	//               /   |    \           /   |   \
  4692  	//       BoolExpr   OR    BoolExpr  Count >    4
  4693  	//       /   |   \        /   |   \
  4694  	//    Count  >    1  BoolExpr AND  BoolExpr
  4695  	//                  /   |   \      /      |
  4696  	//               Count  >    2  BoolExp  Not
  4697  	//                             /   |   \
  4698  	//                          Count  >    3
  4699  
  4700  	assert.NotNil(t, aggregator.Next.Next)
  4701  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4702  
  4703  	filterRows := aggregator.Next.Next.OutputTransforms.FilterRows
  4704  	assert.NotNil(t, filterRows)
  4705  	assert.Equal(t, filterRows.IsTerminal, false)
  4706  	assert.Equal(t, filterRows.BoolOp, structs.BoolOpOr)
  4707  
  4708  	assert.Equal(t, filterRows.LeftBool.IsTerminal, false)
  4709  	assert.Equal(t, filterRows.LeftBool.BoolOp, structs.BoolOpOr)
  4710  
  4711  	assert.Equal(t, filterRows.LeftBool.LeftBool.IsTerminal, true)
  4712  	assert.Equal(t, filterRows.LeftBool.LeftBool.RightValue.NumericExpr.Value, "1")
  4713  	assert.Equal(t, filterRows.LeftBool.LeftBool.RightValue.NumericExpr.ValueIsField, false)
  4714  
  4715  	assert.Equal(t, filterRows.LeftBool.RightBool.IsTerminal, false)
  4716  	assert.Equal(t, filterRows.LeftBool.RightBool.BoolOp, structs.BoolOpAnd)
  4717  
  4718  	assert.Equal(t, filterRows.LeftBool.RightBool.LeftBool.IsTerminal, true)
  4719  	assert.Equal(t, filterRows.LeftBool.RightBool.LeftBool.RightValue.NumericExpr.Value, "2")
  4720  	assert.Equal(t, filterRows.LeftBool.RightBool.LeftBool.RightValue.NumericExpr.ValueIsField, false)
  4721  
  4722  	assert.Equal(t, filterRows.LeftBool.RightBool.RightBool.IsTerminal, false)
  4723  	assert.Equal(t, filterRows.LeftBool.RightBool.RightBool.BoolOp, structs.BoolOpNot)
  4724  
  4725  	assert.Equal(t, filterRows.LeftBool.RightBool.RightBool.LeftBool.IsTerminal, true)
  4726  	assert.Equal(t, filterRows.LeftBool.RightBool.RightBool.LeftBool.RightValue.NumericExpr.Value, "3")
  4727  	assert.Equal(t, filterRows.LeftBool.RightBool.RightBool.LeftBool.RightValue.NumericExpr.ValueIsField, false)
  4728  
  4729  	assert.Equal(t, filterRows.RightBool.IsTerminal, true)
  4730  	assert.Equal(t, filterRows.RightBool.RightValue.NumericExpr.Value, "4")
  4731  	assert.Equal(t, filterRows.RightBool.RightValue.NumericExpr.ValueIsField, false)
  4732  
  4733  }
  4734  
  4735  func Test_WhereBoolean(t *testing.T) {
  4736  	query := []byte(`search A=1 | stats count AS Count, min(latency) AS Min, max(latency) AS Max | where Count > 100 OR (Max > 1000 AND NOT ((Max - Min) / 2 <= 600))`)
  4737  	res, err := spl.Parse("", query)
  4738  	assert.Nil(t, err)
  4739  	filterNode := res.(ast.QueryStruct).SearchFilter
  4740  	assert.NotNil(t, filterNode)
  4741  
  4742  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4743  	assert.Nil(t, err)
  4744  	assert.NotNil(t, astNode)
  4745  	assert.NotNil(t, aggregator)
  4746  	assert.NotNil(t, aggregator.Next)
  4747  
  4748  	// Second agg is for renaming, the third is for eval.
  4749  	// The node structure should be:
  4750  	//                  BoolExpr
  4751  	//                /     |    \
  4752  	//        BoolExpr     OR     BoolExpr
  4753  	//        /  |  \            /    |   \
  4754  	//    Count  >  100   BoolExpr   And   BoolExpr
  4755  	//                    /  |  \          /      |
  4756  	//                  Max  >  1000   BoolExpr  Not
  4757  	//                                /   |   \
  4758  	//                       ValueExpr   <=   ValueExpr
  4759  	//                           |                |
  4760  	//                      NumericExpr          600
  4761  	//                          |
  4762  	//                         Div
  4763  	//                        /   \
  4764  	//                     Minus   2
  4765  	//                    /     \
  4766  	//                  Max     Min
  4767  
  4768  	assert.NotNil(t, aggregator.Next.Next)
  4769  	assert.Equal(t, aggregator.Next.Next.PipeCommandType, structs.OutputTransformType)
  4770  
  4771  	filterRows := aggregator.Next.Next.OutputTransforms.FilterRows
  4772  	assert.NotNil(t, filterRows)
  4773  	assert.Equal(t, filterRows.IsTerminal, false)
  4774  	assert.Equal(t, filterRows.BoolOp, structs.BoolOpOr)
  4775  
  4776  	assert.Equal(t, filterRows.LeftBool.IsTerminal, true)
  4777  	assert.Equal(t, int(filterRows.LeftBool.LeftValue.ValueExprMode), structs.VEMNumericExpr)
  4778  	assert.Equal(t, filterRows.LeftBool.LeftValue.NumericExpr.Value, "Count")
  4779  	assert.Equal(t, filterRows.LeftBool.LeftValue.NumericExpr.ValueIsField, true)
  4780  
  4781  	assert.Equal(t, filterRows.LeftBool.ValueOp, ">")
  4782  	assert.Equal(t, int(filterRows.LeftBool.RightValue.ValueExprMode), structs.VEMNumericExpr)
  4783  	assert.NotNil(t, filterRows.LeftBool.RightValue.NumericExpr)
  4784  	assert.Equal(t, filterRows.LeftBool.RightValue.NumericExpr.IsTerminal, true)
  4785  	assert.Equal(t, filterRows.LeftBool.RightValue.NumericExpr.Value, "100")
  4786  	assert.Equal(t, filterRows.LeftBool.RightValue.NumericExpr.ValueIsField, false)
  4787  
  4788  	assert.Equal(t, filterRows.RightBool.IsTerminal, false)
  4789  	assert.Equal(t, filterRows.RightBool.BoolOp, structs.BoolOpAnd)
  4790  
  4791  	assert.Equal(t, filterRows.RightBool.LeftBool.IsTerminal, true)
  4792  	assert.Equal(t, int(filterRows.RightBool.LeftBool.LeftValue.ValueExprMode), structs.VEMNumericExpr)
  4793  	assert.Equal(t, filterRows.RightBool.LeftBool.LeftValue.NumericExpr.Value, "Max")
  4794  	assert.Equal(t, filterRows.RightBool.LeftBool.LeftValue.NumericExpr.ValueIsField, true)
  4795  
  4796  	assert.Equal(t, filterRows.RightBool.LeftBool.ValueOp, ">")
  4797  	assert.Equal(t, int(filterRows.RightBool.LeftBool.RightValue.ValueExprMode), structs.VEMNumericExpr)
  4798  	assert.NotNil(t, filterRows.RightBool.LeftBool.RightValue.NumericExpr)
  4799  	assert.Equal(t, filterRows.RightBool.LeftBool.RightValue.NumericExpr.IsTerminal, true)
  4800  	assert.Equal(t, filterRows.RightBool.LeftBool.RightValue.NumericExpr.Value, "1000")
  4801  	assert.Equal(t, filterRows.RightBool.LeftBool.RightValue.NumericExpr.ValueIsField, false)
  4802  
  4803  	assert.Equal(t, filterRows.RightBool.RightBool.IsTerminal, false)
  4804  	assert.Equal(t, filterRows.RightBool.RightBool.BoolOp, structs.BoolOpNot)
  4805  	assert.Nil(t, filterRows.RightBool.RightBool.RightBool)
  4806  
  4807  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.IsTerminal, true)
  4808  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.ValueOp, "<=")
  4809  
  4810  	assert.Equal(t, int(filterRows.RightBool.RightBool.LeftBool.LeftValue.ValueExprMode), structs.VEMNumericExpr)
  4811  	assert.NotNil(t, filterRows.RightBool.RightBool.LeftBool.LeftValue.NumericExpr)
  4812  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.LeftValue.NumericExpr.IsTerminal, false)
  4813  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.LeftValue.NumericExpr.Op, "/")
  4814  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.LeftValue.NumericExpr.Left.IsTerminal, false)
  4815  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.LeftValue.NumericExpr.Left.Op, "-")
  4816  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.LeftValue.NumericExpr.Left.Left.IsTerminal, true)
  4817  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.LeftValue.NumericExpr.Left.Left.Value, "Max")
  4818  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.LeftValue.NumericExpr.Left.Left.ValueIsField, true)
  4819  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.LeftValue.NumericExpr.Left.Right.Value, "Min")
  4820  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.LeftValue.NumericExpr.Left.Right.ValueIsField, true)
  4821  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.LeftValue.NumericExpr.Right.IsTerminal, true)
  4822  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.LeftValue.NumericExpr.Right.Value, "2")
  4823  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.LeftValue.NumericExpr.Right.ValueIsField, false)
  4824  
  4825  	assert.Equal(t, int(filterRows.RightBool.RightBool.LeftBool.RightValue.ValueExprMode), structs.VEMNumericExpr)
  4826  	assert.NotNil(t, filterRows.RightBool.RightBool.LeftBool.RightValue.NumericExpr)
  4827  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.RightValue.NumericExpr.IsTerminal, true)
  4828  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.RightValue.NumericExpr.Value, "600")
  4829  	assert.Equal(t, filterRows.RightBool.RightBool.LeftBool.RightValue.NumericExpr.ValueIsField, false)
  4830  }
  4831  
  4832  func Test_head(t *testing.T) {
  4833  	query := []byte(`A=1 | head`)
  4834  	res, err := spl.Parse("", query)
  4835  	assert.Nil(t, err)
  4836  	filterNode := res.(ast.QueryStruct).SearchFilter
  4837  	assert.NotNil(t, filterNode)
  4838  
  4839  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4840  	assert.Nil(t, err)
  4841  	assert.NotNil(t, astNode)
  4842  	assert.NotNil(t, aggregator)
  4843  	assert.Nil(t, aggregator.Next)
  4844  
  4845  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  4846  	assert.Equal(t, aggregator.OutputTransforms.MaxRows, uint64(10)) // This is the SPL default when no value is given.
  4847  }
  4848  
  4849  func Test_headWithNumber(t *testing.T) {
  4850  	query := []byte(`A=1 | head 22`)
  4851  	res, err := spl.Parse("", query)
  4852  	assert.Nil(t, err)
  4853  	filterNode := res.(ast.QueryStruct).SearchFilter
  4854  	assert.NotNil(t, filterNode)
  4855  
  4856  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4857  	assert.Nil(t, err)
  4858  	assert.NotNil(t, astNode)
  4859  	assert.NotNil(t, aggregator)
  4860  	assert.Nil(t, aggregator.Next)
  4861  
  4862  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  4863  	assert.Equal(t, aggregator.OutputTransforms.MaxRows, uint64(22))
  4864  }
  4865  
  4866  // SPL allows "limit=" right before the number.
  4867  func Test_headWithLimitKeyword(t *testing.T) {
  4868  	query := []byte(`A=1 | head limit=15`)
  4869  	res, err := spl.Parse("", query)
  4870  	assert.Nil(t, err)
  4871  	filterNode := res.(ast.QueryStruct).SearchFilter
  4872  	assert.NotNil(t, filterNode)
  4873  
  4874  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4875  	assert.Nil(t, err)
  4876  	assert.NotNil(t, astNode)
  4877  	assert.NotNil(t, aggregator)
  4878  	assert.Nil(t, aggregator.Next)
  4879  
  4880  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  4881  	assert.Equal(t, aggregator.OutputTransforms.MaxRows, uint64(15))
  4882  }
  4883  
  4884  func Test_dedupOneField(t *testing.T) {
  4885  	query := []byte(`A=1 | dedup state`)
  4886  	res, err := spl.Parse("", query)
  4887  	assert.Nil(t, err)
  4888  	filterNode := res.(ast.QueryStruct).SearchFilter
  4889  	assert.NotNil(t, filterNode)
  4890  
  4891  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4892  	assert.Nil(t, err)
  4893  	assert.NotNil(t, astNode)
  4894  	assert.NotNil(t, aggregator)
  4895  	assert.Nil(t, aggregator.Next)
  4896  
  4897  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  4898  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns)
  4899  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns.DedupColRequest)
  4900  
  4901  	dedupExpr := aggregator.OutputTransforms.LetColumns.DedupColRequest
  4902  	assert.Equal(t, dedupExpr.Limit, uint64(1))
  4903  	assert.Equal(t, dedupExpr.FieldList, []string{"state"})
  4904  	assert.NotNil(t, dedupExpr.DedupOptions)
  4905  	assert.Equal(t, dedupExpr.DedupOptions.Consecutive, false)
  4906  	assert.Equal(t, dedupExpr.DedupOptions.KeepEmpty, false)
  4907  	assert.Equal(t, dedupExpr.DedupOptions.KeepEvents, false)
  4908  	assert.Len(t, dedupExpr.DedupSortEles, 0)
  4909  }
  4910  
  4911  func Test_dedupMultipleFields(t *testing.T) {
  4912  	query := []byte(`A=1 | dedup state weekday http_status`)
  4913  	res, err := spl.Parse("", query)
  4914  	assert.Nil(t, err)
  4915  	filterNode := res.(ast.QueryStruct).SearchFilter
  4916  	assert.NotNil(t, filterNode)
  4917  
  4918  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4919  	assert.Nil(t, err)
  4920  	assert.NotNil(t, astNode)
  4921  	assert.NotNil(t, aggregator)
  4922  	assert.Nil(t, aggregator.Next)
  4923  
  4924  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  4925  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns)
  4926  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns.DedupColRequest)
  4927  
  4928  	dedupExpr := aggregator.OutputTransforms.LetColumns.DedupColRequest
  4929  	assert.Equal(t, dedupExpr.Limit, uint64(1))
  4930  	assert.Equal(t, dedupExpr.FieldList, []string{"state", "weekday", "http_status"})
  4931  	assert.NotNil(t, dedupExpr.DedupOptions)
  4932  	assert.Equal(t, dedupExpr.DedupOptions.Consecutive, false)
  4933  	assert.Equal(t, dedupExpr.DedupOptions.KeepEmpty, false)
  4934  	assert.Equal(t, dedupExpr.DedupOptions.KeepEvents, false)
  4935  	assert.Len(t, dedupExpr.DedupSortEles, 0)
  4936  }
  4937  
  4938  func Test_dedupWithLimit(t *testing.T) {
  4939  	query := []byte(`A=1 | dedup 4 state weekday http_status`)
  4940  	res, err := spl.Parse("", query)
  4941  	assert.Nil(t, err)
  4942  	filterNode := res.(ast.QueryStruct).SearchFilter
  4943  	assert.NotNil(t, filterNode)
  4944  
  4945  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4946  	assert.Nil(t, err)
  4947  	assert.NotNil(t, astNode)
  4948  	assert.NotNil(t, aggregator)
  4949  	assert.Nil(t, aggregator.Next)
  4950  
  4951  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  4952  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns)
  4953  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns.DedupColRequest)
  4954  
  4955  	dedupExpr := aggregator.OutputTransforms.LetColumns.DedupColRequest
  4956  	assert.Equal(t, dedupExpr.Limit, uint64(4))
  4957  	assert.Equal(t, dedupExpr.FieldList, []string{"state", "weekday", "http_status"})
  4958  	assert.NotNil(t, dedupExpr.DedupOptions)
  4959  	assert.Equal(t, dedupExpr.DedupOptions.Consecutive, false)
  4960  	assert.Equal(t, dedupExpr.DedupOptions.KeepEmpty, false)
  4961  	assert.Equal(t, dedupExpr.DedupOptions.KeepEvents, false)
  4962  	assert.Len(t, dedupExpr.DedupSortEles, 0)
  4963  }
  4964  
  4965  func Test_dedupWithOptionsBeforeFieldList(t *testing.T) {
  4966  	query := []byte(`A=1 | dedup keepevents=true keepempty=false consecutive=true state weekday http_status `)
  4967  	res, err := spl.Parse("", query)
  4968  	assert.Nil(t, err)
  4969  	filterNode := res.(ast.QueryStruct).SearchFilter
  4970  	assert.NotNil(t, filterNode)
  4971  
  4972  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  4973  	assert.Nil(t, err)
  4974  	assert.NotNil(t, astNode)
  4975  	assert.NotNil(t, aggregator)
  4976  	assert.Nil(t, aggregator.Next)
  4977  
  4978  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  4979  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns)
  4980  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns.DedupColRequest)
  4981  
  4982  	dedupExpr := aggregator.OutputTransforms.LetColumns.DedupColRequest
  4983  	assert.Equal(t, dedupExpr.Limit, uint64(1))
  4984  	assert.Equal(t, dedupExpr.FieldList, []string{"state", "weekday", "http_status"})
  4985  	assert.NotNil(t, dedupExpr.DedupOptions)
  4986  	assert.Equal(t, dedupExpr.DedupOptions.Consecutive, true)
  4987  	assert.Equal(t, dedupExpr.DedupOptions.KeepEmpty, false)
  4988  	assert.Equal(t, dedupExpr.DedupOptions.KeepEvents, true)
  4989  	assert.Len(t, dedupExpr.DedupSortEles, 0)
  4990  }
  4991  
  4992  func Test_dedupWithOptionsAfterFieldList(t *testing.T) {
  4993  	query := []byte(`A=1 | dedup state weekday http_status keepevents=true keepempty=true consecutive=false`)
  4994  	res, err := spl.Parse("", query)
  4995  	assert.Nil(t, err)
  4996  	filterNode := res.(ast.QueryStruct).SearchFilter
  4997  	assert.NotNil(t, filterNode)
  4998  
  4999  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  5000  	assert.Nil(t, err)
  5001  	assert.NotNil(t, astNode)
  5002  	assert.NotNil(t, aggregator)
  5003  	assert.Nil(t, aggregator.Next)
  5004  
  5005  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  5006  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns)
  5007  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns.DedupColRequest)
  5008  
  5009  	dedupExpr := aggregator.OutputTransforms.LetColumns.DedupColRequest
  5010  	assert.Equal(t, dedupExpr.Limit, uint64(1))
  5011  	assert.Equal(t, dedupExpr.FieldList, []string{"state", "weekday", "http_status"})
  5012  	assert.NotNil(t, dedupExpr.DedupOptions)
  5013  	assert.Equal(t, dedupExpr.DedupOptions.Consecutive, false)
  5014  	assert.Equal(t, dedupExpr.DedupOptions.KeepEmpty, true)
  5015  	assert.Equal(t, dedupExpr.DedupOptions.KeepEvents, true)
  5016  	assert.Len(t, dedupExpr.DedupSortEles, 0)
  5017  }
  5018  
  5019  func Test_dedupWithSortBy(t *testing.T) {
  5020  	query := []byte(`A=1 | dedup state weekday http_status sortby +weekday -state`)
  5021  	res, err := spl.Parse("", query)
  5022  	assert.Nil(t, err)
  5023  	filterNode := res.(ast.QueryStruct).SearchFilter
  5024  	assert.NotNil(t, filterNode)
  5025  
  5026  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  5027  	assert.Nil(t, err)
  5028  	assert.NotNil(t, astNode)
  5029  	assert.NotNil(t, aggregator)
  5030  	assert.Nil(t, aggregator.Next)
  5031  
  5032  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  5033  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns)
  5034  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns.DedupColRequest)
  5035  
  5036  	dedupExpr := aggregator.OutputTransforms.LetColumns.DedupColRequest
  5037  	assert.Equal(t, dedupExpr.Limit, uint64(1))
  5038  	assert.Equal(t, dedupExpr.FieldList, []string{"state", "weekday", "http_status"})
  5039  	assert.NotNil(t, dedupExpr.DedupOptions)
  5040  	assert.Equal(t, dedupExpr.DedupOptions.Consecutive, false)
  5041  	assert.Equal(t, dedupExpr.DedupOptions.KeepEmpty, false)
  5042  	assert.Equal(t, dedupExpr.DedupOptions.KeepEvents, false)
  5043  	assert.Len(t, dedupExpr.DedupSortEles, 2)
  5044  	assert.Equal(t, dedupExpr.DedupSortEles[0].SortByAsc, true)
  5045  	assert.Equal(t, dedupExpr.DedupSortEles[0].Op, "")
  5046  	assert.Equal(t, dedupExpr.DedupSortEles[0].Field, "weekday")
  5047  	assert.Equal(t, dedupExpr.DedupSortEles[1].SortByAsc, false)
  5048  	assert.Equal(t, dedupExpr.DedupSortEles[1].Op, "")
  5049  	assert.Equal(t, dedupExpr.DedupSortEles[1].Field, "state")
  5050  }
  5051  
  5052  func Test_sortWithOneField(t *testing.T) {
  5053  	query := []byte(`A=1 | sort auto(city)`)
  5054  	res, err := spl.Parse("", query)
  5055  	assert.Nil(t, err)
  5056  	filterNode := res.(ast.QueryStruct).SearchFilter
  5057  	assert.NotNil(t, filterNode)
  5058  
  5059  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  5060  	assert.Nil(t, err)
  5061  	assert.NotNil(t, astNode)
  5062  	assert.NotNil(t, aggregator)
  5063  	assert.Nil(t, aggregator.Next)
  5064  
  5065  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  5066  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns)
  5067  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns.SortColRequest)
  5068  
  5069  	sortExpr := aggregator.OutputTransforms.LetColumns.SortColRequest
  5070  	assert.Len(t, sortExpr.SortEles, 1)
  5071  	assert.Equal(t, []int{1}, sortExpr.SortAscending)
  5072  	assert.Equal(t, "city", sortExpr.SortEles[0].Field)
  5073  	assert.Equal(t, "auto", sortExpr.SortEles[0].Op)
  5074  	assert.Equal(t, true, sortExpr.SortEles[0].SortByAsc)
  5075  }
  5076  
  5077  func Test_sortWithMultipleFields(t *testing.T) {
  5078  	query := []byte(`A=1 | sort str(app_name), -city, num(latency)`)
  5079  	res, err := spl.Parse("", query)
  5080  	assert.Nil(t, err)
  5081  	filterNode := res.(ast.QueryStruct).SearchFilter
  5082  	assert.NotNil(t, filterNode)
  5083  
  5084  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  5085  	assert.Nil(t, err)
  5086  	assert.NotNil(t, astNode)
  5087  	assert.NotNil(t, aggregator)
  5088  	assert.Nil(t, aggregator.Next)
  5089  
  5090  	assert.Equal(t, aggregator.PipeCommandType, structs.OutputTransformType)
  5091  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns)
  5092  	assert.NotNil(t, aggregator.OutputTransforms.LetColumns.SortColRequest)
  5093  
  5094  	sortExpr := aggregator.OutputTransforms.LetColumns.SortColRequest
  5095  	assert.Len(t, sortExpr.SortEles, 3)
  5096  	assert.Equal(t, []int{1, -1, 1}, sortExpr.SortAscending)
  5097  	assert.Equal(t, "app_name", sortExpr.SortEles[0].Field)
  5098  	assert.Equal(t, "str", sortExpr.SortEles[0].Op)
  5099  	assert.Equal(t, true, sortExpr.SortEles[0].SortByAsc)
  5100  
  5101  	assert.Equal(t, "city", sortExpr.SortEles[1].Field)
  5102  	assert.Equal(t, "", sortExpr.SortEles[1].Op)
  5103  	assert.Equal(t, false, sortExpr.SortEles[1].SortByAsc)
  5104  
  5105  	assert.Equal(t, "latency", sortExpr.SortEles[2].Field)
  5106  	assert.Equal(t, "num", sortExpr.SortEles[2].Op)
  5107  	assert.Equal(t, true, sortExpr.SortEles[2].SortByAsc)
  5108  }
  5109  
  5110  // SPL Transaction command.
  5111  func Test_TransactionRequestWithFields(t *testing.T) {
  5112  	query := []byte(`A=1 | transaction A`)
  5113  	res, err := spl.Parse("", query)
  5114  	assert.Nil(t, err)
  5115  	filterNode := res.(ast.QueryStruct).SearchFilter
  5116  	assert.NotNil(t, filterNode)
  5117  
  5118  	astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  5119  	assert.Nil(t, err)
  5120  	assert.NotNil(t, astNode)
  5121  	assert.NotNil(t, aggregator)
  5122  	assert.NotNil(t, aggregator.TransactionArguments)
  5123  
  5124  	transactionRequest := aggregator.TransactionArguments
  5125  	assert.Equal(t, aggregator.PipeCommandType, structs.TransactionType)
  5126  	assert.Equal(t, transactionRequest.Fields, []string{"A"})
  5127  
  5128  	query = []byte(`A=1 | transaction A B C`)
  5129  	res, err = spl.Parse("", query)
  5130  	assert.Nil(t, err)
  5131  	filterNode = res.(ast.QueryStruct).SearchFilter
  5132  	assert.NotNil(t, filterNode)
  5133  
  5134  	astNode, aggregator, err = pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  5135  	assert.Nil(t, err)
  5136  	assert.NotNil(t, astNode)
  5137  	assert.NotNil(t, aggregator)
  5138  	assert.NotNil(t, aggregator.TransactionArguments)
  5139  
  5140  	transactionRequest = aggregator.TransactionArguments
  5141  	assert.Equal(t, aggregator.PipeCommandType, structs.TransactionType)
  5142  	assert.Equal(t, transactionRequest.Fields, []string{"A", "B", "C"})
  5143  }
  5144  
  5145  func Test_TransactionRequestWithStartsAndEndsWith(t *testing.T) {
  5146  	query1 := []byte(`A=1 | transaction A B C startswith="foo" endswith="bar"`)
  5147  	query1Res := &structs.TransactionArguments{
  5148  		Fields:     []string{"A", "B", "C"},
  5149  		StartsWith: &structs.FilterStringExpr{StringValue: "foo"},
  5150  		EndsWith:   &structs.FilterStringExpr{StringValue: "bar"},
  5151  	}
  5152  
  5153  	query2 := []byte(`A=1 | transaction endswith="bar" startswith="foo"`)
  5154  	query2Res := &structs.TransactionArguments{
  5155  		Fields:     []string(nil),
  5156  		StartsWith: &structs.FilterStringExpr{StringValue: "foo"},
  5157  		EndsWith:   &structs.FilterStringExpr{StringValue: "bar"},
  5158  	}
  5159  
  5160  	query3 := []byte(`A=1 | transaction startswith="foo" endswith="bar"`)
  5161  	query3Res := &structs.TransactionArguments{
  5162  		Fields:     []string(nil),
  5163  		StartsWith: &structs.FilterStringExpr{StringValue: "foo"},
  5164  		EndsWith:   &structs.FilterStringExpr{StringValue: "bar"},
  5165  	}
  5166  
  5167  	query4 := []byte(`A=1 | transaction endswith="bar"`)
  5168  	query4Res := &structs.TransactionArguments{
  5169  		Fields:     []string(nil),
  5170  		StartsWith: nil,
  5171  		EndsWith:   &structs.FilterStringExpr{StringValue: "bar"},
  5172  	}
  5173  
  5174  	query5 := []byte(`A=1 | transaction startswith="foo"`)
  5175  	query5Res := &structs.TransactionArguments{
  5176  		Fields:     []string(nil),
  5177  		StartsWith: &structs.FilterStringExpr{StringValue: "foo"},
  5178  		EndsWith:   nil,
  5179  	}
  5180  
  5181  	query6 := []byte(`A=1 | transaction startswith="foo" endswith="bar" A B C`)
  5182  	query6Res := &structs.TransactionArguments{
  5183  		Fields:     []string{"A", "B", "C"},
  5184  		StartsWith: &structs.FilterStringExpr{StringValue: "foo"},
  5185  		EndsWith:   &structs.FilterStringExpr{StringValue: "bar"},
  5186  	}
  5187  
  5188  	queries := [][]byte{query1, query2, query3, query4, query5, query6}
  5189  	results := []*structs.TransactionArguments{query1Res, query2Res, query3Res, query4Res, query5Res, query6Res}
  5190  
  5191  	for ind, query := range queries {
  5192  		res, err := spl.Parse("", query)
  5193  		assert.Nil(t, err)
  5194  		filterNode := res.(ast.QueryStruct).SearchFilter
  5195  		assert.NotNil(t, filterNode)
  5196  
  5197  		astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  5198  		assert.Nil(t, err)
  5199  		assert.NotNil(t, astNode)
  5200  		assert.NotNil(t, aggregator)
  5201  		assert.NotNil(t, aggregator.TransactionArguments)
  5202  
  5203  		transactionRequest := aggregator.TransactionArguments
  5204  		assert.Equal(t, aggregator.PipeCommandType, structs.TransactionType)
  5205  		assert.Equal(t, transactionRequest.Fields, results[ind].Fields)
  5206  		assert.Equal(t, transactionRequest.StartsWith, results[ind].StartsWith)
  5207  		assert.Equal(t, transactionRequest.EndsWith, results[ind].EndsWith)
  5208  	}
  5209  }
  5210  
  5211  func Test_TransactionRequestWithFilterStringExpr(t *testing.T) {
  5212  	// CASE 1: Fields + StartsWith is Eval + EndsWith is TransactionQueryString With only OR
  5213  	query1 := []byte(`A=1 | transaction A B C startswith=eval(duration > 10) endswith=("foo" OR "bar2")`)
  5214  	query1Res := &structs.TransactionArguments{
  5215  		Fields: []string{"A", "B", "C"},
  5216  		StartsWith: &structs.FilterStringExpr{
  5217  			EvalBoolExpr: &structs.BoolExpr{
  5218  				IsTerminal: true,
  5219  				LeftValue: &structs.ValueExpr{
  5220  					NumericExpr: &structs.NumericExpr{
  5221  						IsTerminal:      true,
  5222  						NumericExprMode: structs.NEMNumberField,
  5223  						ValueIsField:    true,
  5224  						Value:           "duration",
  5225  					},
  5226  				},
  5227  				RightValue: &structs.ValueExpr{
  5228  					NumericExpr: &structs.NumericExpr{
  5229  						IsTerminal:      true,
  5230  						NumericExprMode: structs.NEMNumber,
  5231  						ValueIsField:    false,
  5232  						Value:           "10",
  5233  					},
  5234  				},
  5235  				ValueOp: ">",
  5236  			},
  5237  		},
  5238  		EndsWith: &structs.FilterStringExpr{
  5239  			SearchNode: &structs.ASTNode{
  5240  				OrFilterCondition: &structs.Condition{
  5241  					FilterCriteria: []*structs.FilterCriteria{
  5242  						{
  5243  							MatchFilter: &structs.MatchFilter{
  5244  								MatchColumn: "*",
  5245  								MatchWords: [][]byte{
  5246  									[]byte("foo"),
  5247  								},
  5248  								MatchOperator: utils.And,
  5249  								MatchPhrase:   []byte("foo"),
  5250  								MatchType:     structs.MATCH_PHRASE,
  5251  							},
  5252  						},
  5253  						{
  5254  							MatchFilter: &structs.MatchFilter{
  5255  								MatchColumn: "*",
  5256  								MatchWords: [][]byte{
  5257  									[]byte("bar2"),
  5258  								},
  5259  								MatchOperator: utils.And,
  5260  								MatchPhrase:   []byte("bar2"),
  5261  								MatchType:     structs.MATCH_PHRASE,
  5262  							},
  5263  						},
  5264  					},
  5265  				},
  5266  			},
  5267  		},
  5268  	}
  5269  
  5270  	// CASE 2: Fields + StartsWith is searchTerm (String) + EndsWith is TransactionQueryString With OR & AND
  5271  	query2 := []byte(`A=1 | transaction A B C startswith=status="ok" endswith=("foo" OR "foo1" AND "bar")`)
  5272  	query2Res := &structs.TransactionArguments{
  5273  		Fields: []string{"A", "B", "C"},
  5274  		StartsWith: &structs.FilterStringExpr{
  5275  			SearchNode: &structs.ASTNode{
  5276  				AndFilterCondition: &structs.Condition{
  5277  					FilterCriteria: []*structs.FilterCriteria{
  5278  						{
  5279  							ExpressionFilter: &structs.ExpressionFilter{
  5280  								LeftInput: &structs.FilterInput{
  5281  									Expression: &structs.Expression{
  5282  										LeftInput: &structs.ExpressionInput{
  5283  											ColumnValue: nil,
  5284  											ColumnName:  "status",
  5285  										},
  5286  										ExpressionOp: utils.Add,
  5287  										RightInput:   nil,
  5288  									},
  5289  								},
  5290  								FilterOperator: utils.Equals,
  5291  								RightInput: &structs.FilterInput{
  5292  									Expression: &structs.Expression{
  5293  										LeftInput: &structs.ExpressionInput{
  5294  											ColumnValue: &utils.DtypeEnclosure{
  5295  												Dtype:     utils.SS_DT_STRING,
  5296  												StringVal: "ok",
  5297  											},
  5298  										},
  5299  									},
  5300  								},
  5301  							},
  5302  						},
  5303  					},
  5304  				},
  5305  			},
  5306  		},
  5307  		EndsWith: &structs.FilterStringExpr{
  5308  			SearchNode: &structs.ASTNode{
  5309  				AndFilterCondition: &structs.Condition{
  5310  					FilterCriteria: []*structs.FilterCriteria{
  5311  						{
  5312  							MatchFilter: &structs.MatchFilter{
  5313  								MatchColumn: "*",
  5314  								MatchWords: [][]byte{
  5315  									[]byte("bar"),
  5316  								},
  5317  								MatchOperator: utils.And,
  5318  								MatchPhrase:   []byte("bar"),
  5319  								MatchType:     structs.MATCH_PHRASE,
  5320  							},
  5321  						},
  5322  					},
  5323  					NestedNodes: []*structs.ASTNode{
  5324  						{
  5325  							OrFilterCondition: &structs.Condition{
  5326  								FilterCriteria: []*structs.FilterCriteria{
  5327  									{
  5328  										MatchFilter: &structs.MatchFilter{
  5329  											MatchColumn: "*",
  5330  											MatchWords: [][]byte{
  5331  												[]byte("foo"),
  5332  											},
  5333  											MatchOperator: utils.And,
  5334  											MatchPhrase:   []byte("foo"),
  5335  											MatchType:     structs.MATCH_PHRASE,
  5336  										},
  5337  									},
  5338  									{
  5339  										MatchFilter: &structs.MatchFilter{
  5340  											MatchColumn: "*",
  5341  											MatchWords: [][]byte{
  5342  												[]byte("foo1"),
  5343  											},
  5344  											MatchOperator: utils.And,
  5345  											MatchPhrase:   []byte("foo1"),
  5346  											MatchType:     structs.MATCH_PHRASE,
  5347  										},
  5348  									},
  5349  								},
  5350  							},
  5351  						},
  5352  					},
  5353  				},
  5354  			},
  5355  		},
  5356  	}
  5357  
  5358  	// CASE 3: Fields + StartWith is searchTerm (Number) + endswith is Eval
  5359  	query3 := []byte(`A=1 | transaction A B C startswith=duration>10 endswith=eval(status<400)`)
  5360  	query3Res := &structs.TransactionArguments{
  5361  		Fields: []string{"A", "B", "C"},
  5362  		StartsWith: &structs.FilterStringExpr{
  5363  			SearchNode: &structs.ASTNode{
  5364  				AndFilterCondition: &structs.Condition{
  5365  					FilterCriteria: []*structs.FilterCriteria{
  5366  						{
  5367  							ExpressionFilter: &structs.ExpressionFilter{
  5368  								LeftInput: &structs.FilterInput{
  5369  									Expression: &structs.Expression{
  5370  										LeftInput: &structs.ExpressionInput{
  5371  											ColumnValue: nil,
  5372  											ColumnName:  "duration",
  5373  										},
  5374  										ExpressionOp: utils.Add,
  5375  										RightInput:   nil,
  5376  									},
  5377  								},
  5378  								FilterOperator: utils.GreaterThan,
  5379  								RightInput: &structs.FilterInput{
  5380  									Expression: &structs.Expression{
  5381  										LeftInput: &structs.ExpressionInput{
  5382  											ColumnValue: &utils.DtypeEnclosure{
  5383  												Dtype:       utils.SS_DT_UNSIGNED_NUM,
  5384  												UnsignedVal: uint64(10),
  5385  												SignedVal:   int64(10),
  5386  												FloatVal:    float64(10),
  5387  												StringVal:   "10",
  5388  											},
  5389  										},
  5390  									},
  5391  								},
  5392  							},
  5393  						},
  5394  					},
  5395  				},
  5396  			},
  5397  		},
  5398  		EndsWith: &structs.FilterStringExpr{
  5399  			EvalBoolExpr: &structs.BoolExpr{
  5400  				IsTerminal: true,
  5401  				LeftValue: &structs.ValueExpr{
  5402  					NumericExpr: &structs.NumericExpr{
  5403  						IsTerminal:      true,
  5404  						NumericExprMode: structs.NEMNumberField,
  5405  						ValueIsField:    true,
  5406  						Value:           "status",
  5407  					},
  5408  				},
  5409  				RightValue: &structs.ValueExpr{
  5410  					NumericExpr: &structs.NumericExpr{
  5411  						IsTerminal:      true,
  5412  						NumericExprMode: structs.NEMNumber,
  5413  						ValueIsField:    false,
  5414  						Value:           "400",
  5415  					},
  5416  				},
  5417  				ValueOp: "<",
  5418  			},
  5419  		},
  5420  	}
  5421  
  5422  	// CASE 4: Fields + StartWith is searchTerm (String) + endswith is String Value
  5423  	query4 := []byte(`A=1 | transaction A B C startswith=status="Ok" endswith="foo"`)
  5424  	query4Res := &structs.TransactionArguments{
  5425  		Fields: []string{"A", "B", "C"},
  5426  		StartsWith: &structs.FilterStringExpr{
  5427  			SearchNode: &structs.ASTNode{
  5428  				AndFilterCondition: &structs.Condition{
  5429  					FilterCriteria: []*structs.FilterCriteria{
  5430  						{
  5431  							ExpressionFilter: &structs.ExpressionFilter{
  5432  								LeftInput: &structs.FilterInput{
  5433  									Expression: &structs.Expression{
  5434  										LeftInput: &structs.ExpressionInput{
  5435  											ColumnValue: nil,
  5436  											ColumnName:  "status",
  5437  										},
  5438  										ExpressionOp: utils.Add,
  5439  										RightInput:   nil,
  5440  									},
  5441  								},
  5442  								FilterOperator: utils.Equals,
  5443  								RightInput: &structs.FilterInput{
  5444  									Expression: &structs.Expression{
  5445  										LeftInput: &structs.ExpressionInput{
  5446  											ColumnValue: &utils.DtypeEnclosure{
  5447  												Dtype:     utils.SS_DT_STRING,
  5448  												StringVal: "Ok",
  5449  											},
  5450  										},
  5451  									},
  5452  								},
  5453  							},
  5454  						},
  5455  					},
  5456  				},
  5457  			},
  5458  		},
  5459  		EndsWith: &structs.FilterStringExpr{
  5460  			StringValue: "foo",
  5461  		},
  5462  	}
  5463  
  5464  	// CASE 5: Fields + StartWith is String Search Expression + endswith is String Value
  5465  	query5 := []byte(`A=1 | transaction A B C startswith="status=300 OR status=bar" endswith="bar"`)
  5466  	query5Res := &structs.TransactionArguments{
  5467  		Fields: []string{"A", "B", "C"},
  5468  		StartsWith: &structs.FilterStringExpr{
  5469  			SearchNode: &structs.ASTNode{
  5470  				OrFilterCondition: &structs.Condition{
  5471  					FilterCriteria: []*structs.FilterCriteria{
  5472  						{
  5473  							ExpressionFilter: &structs.ExpressionFilter{
  5474  								LeftInput: &structs.FilterInput{
  5475  									Expression: &structs.Expression{
  5476  										LeftInput: &structs.ExpressionInput{
  5477  											ColumnValue: nil,
  5478  											ColumnName:  "status",
  5479  										},
  5480  										ExpressionOp: utils.Add,
  5481  										RightInput:   nil,
  5482  									},
  5483  								},
  5484  								RightInput: &structs.FilterInput{
  5485  									Expression: &structs.Expression{
  5486  										LeftInput: &structs.ExpressionInput{
  5487  											ColumnValue: &utils.DtypeEnclosure{
  5488  												Dtype:       utils.SS_DT_UNSIGNED_NUM,
  5489  												StringVal:   "300",
  5490  												UnsignedVal: uint64(300),
  5491  												SignedVal:   int64(300),
  5492  												FloatVal:    float64(300),
  5493  											},
  5494  											ColumnName: "",
  5495  										},
  5496  										ExpressionOp: utils.Add,
  5497  										RightInput:   nil,
  5498  									},
  5499  								},
  5500  								FilterOperator: utils.Equals,
  5501  							},
  5502  						},
  5503  						{
  5504  							ExpressionFilter: &structs.ExpressionFilter{
  5505  								LeftInput: &structs.FilterInput{
  5506  									Expression: &structs.Expression{
  5507  										LeftInput: &structs.ExpressionInput{
  5508  											ColumnValue: nil,
  5509  											ColumnName:  "status",
  5510  										},
  5511  										ExpressionOp: utils.Add,
  5512  										RightInput:   nil,
  5513  									},
  5514  								},
  5515  								RightInput: &structs.FilterInput{
  5516  									Expression: &structs.Expression{
  5517  										LeftInput: &structs.ExpressionInput{
  5518  											ColumnValue: &utils.DtypeEnclosure{
  5519  												Dtype:     utils.SS_DT_STRING,
  5520  												StringVal: "bar",
  5521  											},
  5522  											ColumnName: "",
  5523  										},
  5524  										ExpressionOp: utils.Add,
  5525  										RightInput:   nil,
  5526  									},
  5527  								},
  5528  								FilterOperator: utils.Equals,
  5529  							},
  5530  						},
  5531  					},
  5532  					NestedNodes: nil,
  5533  				},
  5534  			},
  5535  		},
  5536  		EndsWith: &structs.FilterStringExpr{
  5537  			StringValue: "bar",
  5538  		},
  5539  	}
  5540  
  5541  	// CASE 6: Fields + StartWith is String Search Expression + endswith is Eval
  5542  	query6 := []byte(`A=1 | transaction A B C startswith="status=foo OR status=bar AND action=login" endswith=eval(status<400)`)
  5543  	query6Res := &structs.TransactionArguments{
  5544  		Fields: []string{"A", "B", "C"},
  5545  		StartsWith: &structs.FilterStringExpr{
  5546  			SearchNode: &structs.ASTNode{
  5547  				AndFilterCondition: &structs.Condition{
  5548  					FilterCriteria: []*structs.FilterCriteria{
  5549  						{
  5550  							ExpressionFilter: &structs.ExpressionFilter{
  5551  								LeftInput: &structs.FilterInput{
  5552  									Expression: &structs.Expression{
  5553  										LeftInput: &structs.ExpressionInput{
  5554  											ColumnValue: nil,
  5555  											ColumnName:  "action",
  5556  										},
  5557  										ExpressionOp: utils.Add,
  5558  										RightInput:   nil,
  5559  									},
  5560  								},
  5561  								RightInput: &structs.FilterInput{
  5562  									Expression: &structs.Expression{
  5563  										LeftInput: &structs.ExpressionInput{
  5564  											ColumnValue: &utils.DtypeEnclosure{
  5565  												Dtype:     utils.SS_DT_STRING,
  5566  												StringVal: "login",
  5567  											},
  5568  											ColumnName: "",
  5569  										},
  5570  										ExpressionOp: utils.Add,
  5571  										RightInput:   nil,
  5572  									},
  5573  								},
  5574  								FilterOperator: utils.Equals,
  5575  							},
  5576  						},
  5577  					},
  5578  					NestedNodes: []*structs.ASTNode{
  5579  						{
  5580  							OrFilterCondition: &structs.Condition{
  5581  								FilterCriteria: []*structs.FilterCriteria{
  5582  									{
  5583  										ExpressionFilter: &structs.ExpressionFilter{
  5584  											LeftInput: &structs.FilterInput{
  5585  												Expression: &structs.Expression{
  5586  													LeftInput: &structs.ExpressionInput{
  5587  														ColumnValue: nil,
  5588  														ColumnName:  "status",
  5589  													},
  5590  													ExpressionOp: utils.Add,
  5591  													RightInput:   nil,
  5592  												},
  5593  											},
  5594  											RightInput: &structs.FilterInput{
  5595  												Expression: &structs.Expression{
  5596  													LeftInput: &structs.ExpressionInput{
  5597  														ColumnValue: &utils.DtypeEnclosure{
  5598  															Dtype:     utils.SS_DT_STRING,
  5599  															StringVal: "foo",
  5600  														},
  5601  														ColumnName: "",
  5602  													},
  5603  													ExpressionOp: utils.Add,
  5604  													RightInput:   nil,
  5605  												},
  5606  											},
  5607  											FilterOperator: utils.Equals,
  5608  										},
  5609  									},
  5610  									{
  5611  										ExpressionFilter: &structs.ExpressionFilter{
  5612  											LeftInput: &structs.FilterInput{
  5613  												Expression: &structs.Expression{
  5614  													LeftInput: &structs.ExpressionInput{
  5615  														ColumnValue: nil,
  5616  														ColumnName:  "status",
  5617  													},
  5618  													ExpressionOp: utils.Add,
  5619  													RightInput:   nil,
  5620  												},
  5621  											},
  5622  											RightInput: &structs.FilterInput{
  5623  												Expression: &structs.Expression{
  5624  													LeftInput: &structs.ExpressionInput{
  5625  														ColumnValue: &utils.DtypeEnclosure{
  5626  															Dtype:     utils.SS_DT_STRING,
  5627  															StringVal: "bar",
  5628  														},
  5629  														ColumnName: "",
  5630  													},
  5631  													ExpressionOp: utils.Add,
  5632  													RightInput:   nil,
  5633  												},
  5634  											},
  5635  											FilterOperator: utils.Equals,
  5636  										},
  5637  									},
  5638  								},
  5639  								NestedNodes: nil,
  5640  							},
  5641  						},
  5642  					},
  5643  				},
  5644  			},
  5645  		},
  5646  		EndsWith: &structs.FilterStringExpr{
  5647  			EvalBoolExpr: &structs.BoolExpr{
  5648  				IsTerminal: true,
  5649  				LeftValue: &structs.ValueExpr{
  5650  					NumericExpr: &structs.NumericExpr{
  5651  						IsTerminal:      true,
  5652  						NumericExprMode: structs.NEMNumberField,
  5653  						ValueIsField:    true,
  5654  						Value:           "status",
  5655  					},
  5656  				},
  5657  				RightValue: &structs.ValueExpr{
  5658  					NumericExpr: &structs.NumericExpr{
  5659  						IsTerminal:      true,
  5660  						NumericExprMode: structs.NEMNumber,
  5661  						ValueIsField:    false,
  5662  						Value:           "400",
  5663  					},
  5664  				},
  5665  				ValueOp: "<",
  5666  			},
  5667  		},
  5668  	}
  5669  
  5670  	// CASE 7: Fileds + StartWith is Search Term (With Number) + endsWith is String Search Expression
  5671  	query7 := []byte(`A=1 | transaction A B C startswith=(status>300 OR status=201) endswith="status=foo OR status=bar AND action=login"`)
  5672  	query7Res := &structs.TransactionArguments{
  5673  		Fields: []string{"A", "B", "C"},
  5674  		StartsWith: &structs.FilterStringExpr{
  5675  			SearchNode: &structs.ASTNode{
  5676  				OrFilterCondition: &structs.Condition{
  5677  					FilterCriteria: []*structs.FilterCriteria{
  5678  						{
  5679  							ExpressionFilter: &structs.ExpressionFilter{
  5680  								LeftInput: &structs.FilterInput{
  5681  									Expression: &structs.Expression{
  5682  										LeftInput: &structs.ExpressionInput{
  5683  											ColumnValue: nil,
  5684  											ColumnName:  "status",
  5685  										},
  5686  										ExpressionOp: utils.Add,
  5687  										RightInput:   nil,
  5688  									},
  5689  								},
  5690  								FilterOperator: utils.GreaterThan,
  5691  								RightInput: &structs.FilterInput{
  5692  									Expression: &structs.Expression{
  5693  										LeftInput: &structs.ExpressionInput{
  5694  											ColumnValue: &utils.DtypeEnclosure{
  5695  												Dtype:       utils.SS_DT_UNSIGNED_NUM,
  5696  												UnsignedVal: uint64(300),
  5697  												SignedVal:   int64(300),
  5698  												FloatVal:    float64(300),
  5699  												StringVal:   "300",
  5700  											},
  5701  										},
  5702  									},
  5703  								},
  5704  							},
  5705  						},
  5706  						{
  5707  							ExpressionFilter: &structs.ExpressionFilter{
  5708  								LeftInput: &structs.FilterInput{
  5709  									Expression: &structs.Expression{
  5710  										LeftInput: &structs.ExpressionInput{
  5711  											ColumnValue: nil,
  5712  											ColumnName:  "status",
  5713  										},
  5714  										ExpressionOp: utils.Add,
  5715  										RightInput:   nil,
  5716  									},
  5717  								},
  5718  								FilterOperator: utils.Equals,
  5719  								RightInput: &structs.FilterInput{
  5720  									Expression: &structs.Expression{
  5721  										LeftInput: &structs.ExpressionInput{
  5722  											ColumnValue: &utils.DtypeEnclosure{
  5723  												Dtype:       utils.SS_DT_UNSIGNED_NUM,
  5724  												UnsignedVal: uint64(201),
  5725  												SignedVal:   int64(201),
  5726  												FloatVal:    float64(201),
  5727  												StringVal:   "201",
  5728  											},
  5729  										},
  5730  									},
  5731  								},
  5732  							},
  5733  						},
  5734  					},
  5735  				},
  5736  			},
  5737  		},
  5738  		EndsWith: &structs.FilterStringExpr{
  5739  			SearchNode: &structs.ASTNode{
  5740  				AndFilterCondition: &structs.Condition{
  5741  					FilterCriteria: []*structs.FilterCriteria{
  5742  						{
  5743  							ExpressionFilter: &structs.ExpressionFilter{
  5744  								LeftInput: &structs.FilterInput{
  5745  									Expression: &structs.Expression{
  5746  										LeftInput: &structs.ExpressionInput{
  5747  											ColumnValue: nil,
  5748  											ColumnName:  "action",
  5749  										},
  5750  										ExpressionOp: utils.Add,
  5751  										RightInput:   nil,
  5752  									},
  5753  								},
  5754  								RightInput: &structs.FilterInput{
  5755  									Expression: &structs.Expression{
  5756  										LeftInput: &structs.ExpressionInput{
  5757  											ColumnValue: &utils.DtypeEnclosure{
  5758  												Dtype:     utils.SS_DT_STRING,
  5759  												StringVal: "login",
  5760  											},
  5761  											ColumnName: "",
  5762  										},
  5763  										ExpressionOp: utils.Add,
  5764  										RightInput:   nil,
  5765  									},
  5766  								},
  5767  								FilterOperator: utils.Equals,
  5768  							},
  5769  						},
  5770  					},
  5771  					NestedNodes: []*structs.ASTNode{
  5772  						{
  5773  							OrFilterCondition: &structs.Condition{
  5774  								FilterCriteria: []*structs.FilterCriteria{
  5775  									{
  5776  										ExpressionFilter: &structs.ExpressionFilter{
  5777  											LeftInput: &structs.FilterInput{
  5778  												Expression: &structs.Expression{
  5779  													LeftInput: &structs.ExpressionInput{
  5780  														ColumnValue: nil,
  5781  														ColumnName:  "status",
  5782  													},
  5783  													ExpressionOp: utils.Add,
  5784  													RightInput:   nil,
  5785  												},
  5786  											},
  5787  											RightInput: &structs.FilterInput{
  5788  												Expression: &structs.Expression{
  5789  													LeftInput: &structs.ExpressionInput{
  5790  														ColumnValue: &utils.DtypeEnclosure{
  5791  															Dtype:     utils.SS_DT_STRING,
  5792  															StringVal: "foo",
  5793  														},
  5794  														ColumnName: "",
  5795  													},
  5796  													ExpressionOp: utils.Add,
  5797  													RightInput:   nil,
  5798  												},
  5799  											},
  5800  											FilterOperator: utils.Equals,
  5801  										},
  5802  									},
  5803  									{
  5804  										ExpressionFilter: &structs.ExpressionFilter{
  5805  											LeftInput: &structs.FilterInput{
  5806  												Expression: &structs.Expression{
  5807  													LeftInput: &structs.ExpressionInput{
  5808  														ColumnValue: nil,
  5809  														ColumnName:  "status",
  5810  													},
  5811  													ExpressionOp: utils.Add,
  5812  													RightInput:   nil,
  5813  												},
  5814  											},
  5815  											RightInput: &structs.FilterInput{
  5816  												Expression: &structs.Expression{
  5817  													LeftInput: &structs.ExpressionInput{
  5818  														ColumnValue: &utils.DtypeEnclosure{
  5819  															Dtype:     utils.SS_DT_STRING,
  5820  															StringVal: "bar",
  5821  														},
  5822  														ColumnName: "",
  5823  													},
  5824  													ExpressionOp: utils.Add,
  5825  													RightInput:   nil,
  5826  												},
  5827  											},
  5828  											FilterOperator: utils.Equals,
  5829  										},
  5830  									},
  5831  								},
  5832  								NestedNodes: nil,
  5833  							},
  5834  						},
  5835  					},
  5836  				},
  5837  			},
  5838  		},
  5839  	}
  5840  
  5841  	// CASE 8: Fields + StartsWith=OR String Clauses + EndsWith=OR Clauses
  5842  	query8 := []byte(`A=1 | transaction A B C startswith=("GET" OR "POST1") endswith=("DELETE" OR "POST2")`)
  5843  	query8Res := &structs.TransactionArguments{
  5844  		Fields: []string{"A", "B", "C"},
  5845  		StartsWith: &structs.FilterStringExpr{
  5846  			SearchNode: &structs.ASTNode{
  5847  				OrFilterCondition: &structs.Condition{
  5848  					FilterCriteria: []*structs.FilterCriteria{
  5849  						{
  5850  							MatchFilter: &structs.MatchFilter{
  5851  								MatchColumn: "*",
  5852  								MatchWords: [][]byte{
  5853  									[]byte("GET"),
  5854  								},
  5855  								MatchOperator: utils.And,
  5856  								MatchPhrase:   []byte("GET"),
  5857  								MatchType:     structs.MATCH_PHRASE,
  5858  							},
  5859  						},
  5860  						{
  5861  							MatchFilter: &structs.MatchFilter{
  5862  								MatchColumn: "*",
  5863  								MatchWords: [][]byte{
  5864  									[]byte("POST1"),
  5865  								},
  5866  								MatchOperator: utils.And,
  5867  								MatchPhrase:   []byte("POST1"),
  5868  								MatchType:     structs.MATCH_PHRASE,
  5869  							},
  5870  						},
  5871  					},
  5872  				},
  5873  			},
  5874  		},
  5875  		EndsWith: &structs.FilterStringExpr{
  5876  			SearchNode: &structs.ASTNode{
  5877  				OrFilterCondition: &structs.Condition{
  5878  					FilterCriteria: []*structs.FilterCriteria{
  5879  						{
  5880  							MatchFilter: &structs.MatchFilter{
  5881  								MatchColumn: "*",
  5882  								MatchWords: [][]byte{
  5883  									[]byte("DELETE"),
  5884  								},
  5885  								MatchOperator: utils.And,
  5886  								MatchPhrase:   []byte("DELETE"),
  5887  								MatchType:     structs.MATCH_PHRASE,
  5888  							},
  5889  						},
  5890  						{
  5891  							MatchFilter: &structs.MatchFilter{
  5892  								MatchColumn: "*",
  5893  								MatchWords: [][]byte{
  5894  									[]byte("POST2"),
  5895  								},
  5896  								MatchOperator: utils.And,
  5897  								MatchPhrase:   []byte("POST2"),
  5898  								MatchType:     structs.MATCH_PHRASE,
  5899  							},
  5900  						},
  5901  					},
  5902  				},
  5903  			},
  5904  		},
  5905  	}
  5906  
  5907  	// CASE 9: Fields + StartsWith=AND String Clauses + EndsWith=Single String Clause
  5908  	query9 := []byte(`A=1 | transaction A B C startswith=("GET" AND "POST1") endswith=("DELETE")`)
  5909  	query9Res := &structs.TransactionArguments{
  5910  		Fields: []string{"A", "B", "C"},
  5911  		StartsWith: &structs.FilterStringExpr{
  5912  			SearchNode: &structs.ASTNode{
  5913  				AndFilterCondition: &structs.Condition{
  5914  					FilterCriteria: []*structs.FilterCriteria{
  5915  						{
  5916  							MatchFilter: &structs.MatchFilter{
  5917  								MatchColumn: "*",
  5918  								MatchWords: [][]byte{
  5919  									[]byte("GET"),
  5920  								},
  5921  								MatchOperator: utils.And,
  5922  								MatchPhrase:   []byte("GET"),
  5923  								MatchType:     structs.MATCH_PHRASE,
  5924  							},
  5925  						},
  5926  						{
  5927  							MatchFilter: &structs.MatchFilter{
  5928  								MatchColumn: "*",
  5929  								MatchWords: [][]byte{
  5930  									[]byte("POST1"),
  5931  								},
  5932  								MatchOperator: utils.And,
  5933  								MatchPhrase:   []byte("POST1"),
  5934  								MatchType:     structs.MATCH_PHRASE,
  5935  							},
  5936  						},
  5937  					},
  5938  				},
  5939  			},
  5940  		},
  5941  		EndsWith: &structs.FilterStringExpr{
  5942  			SearchNode: &structs.ASTNode{
  5943  				AndFilterCondition: &structs.Condition{
  5944  					FilterCriteria: []*structs.FilterCriteria{
  5945  						{
  5946  							MatchFilter: &structs.MatchFilter{
  5947  								MatchColumn: "*",
  5948  								MatchWords: [][]byte{
  5949  									[]byte("DELETE"),
  5950  								},
  5951  								MatchOperator: utils.And,
  5952  								MatchPhrase:   []byte("DELETE"),
  5953  								MatchType:     structs.MATCH_PHRASE,
  5954  							},
  5955  						},
  5956  					},
  5957  				},
  5958  			},
  5959  		},
  5960  	}
  5961  
  5962  	queries := [][]byte{query1, query2, query3, query4, query5, query6, query7, query8, query9}
  5963  	results := []*structs.TransactionArguments{query1Res, query2Res, query3Res, query4Res, query5Res, query6Res, query7Res, query8Res, query9Res}
  5964  
  5965  	for ind, query := range queries {
  5966  		res, err := spl.Parse("", query)
  5967  		assert.Nil(t, err)
  5968  		filterNode := res.(ast.QueryStruct).SearchFilter
  5969  		assert.NotNil(t, filterNode)
  5970  
  5971  		astNode, aggregator, err := pipesearch.ParseQuery(string(query), 0, "Splunk QL")
  5972  		assert.Nil(t, err)
  5973  		assert.NotNil(t, astNode)
  5974  		assert.NotNil(t, aggregator)
  5975  		assert.NotNil(t, aggregator.TransactionArguments)
  5976  
  5977  		transactionRequest := aggregator.TransactionArguments
  5978  		assert.Equal(t, structs.TransactionType, aggregator.PipeCommandType)
  5979  		assert.Equal(t, results[ind].Fields, transactionRequest.Fields)
  5980  		assert.Equal(t, results[ind].StartsWith, transactionRequest.StartsWith)
  5981  		assert.Equal(t, results[ind].EndsWith, transactionRequest.EndsWith)
  5982  	}
  5983  }