github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/logql/syntax/parser_test.go (about)

     1  package syntax
     2  
     3  import (
     4  	"errors"
     5  	"reflect"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/prometheus/prometheus/model/labels"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/grafana/loki/pkg/logql/log"
    13  	"github.com/grafana/loki/pkg/logqlmodel"
    14  )
    15  
    16  func NewStringLabelFilter(s string) *string {
    17  	return &s
    18  }
    19  
    20  func TestParse(t *testing.T) {
    21  	for _, tc := range []struct {
    22  		in  string
    23  		exp Expr
    24  		err error
    25  	}{
    26  		{
    27  			// raw string
    28  			in: "count_over_time({foo=~`bar\\w+`}[12h] |~ `error\\`)",
    29  			exp: &RangeAggregationExpr{
    30  				Operation: "count_over_time",
    31  				Left: &LogRange{
    32  					Left: &PipelineExpr{
    33  						MultiStages: MultiStageExpr{
    34  							newLineFilterExpr(labels.MatchRegexp, "", "error\\"),
    35  						},
    36  						Left: &MatchersExpr{
    37  							Mts: []*labels.Matcher{
    38  								mustNewMatcher(labels.MatchRegexp, "foo", "bar\\w+"),
    39  							},
    40  						},
    41  					},
    42  					Interval: 12 * time.Hour,
    43  				},
    44  			},
    45  		},
    46  		{
    47  			// test [12h] before filter expr
    48  			in: `count_over_time({foo="bar"}[12h] |= "error")`,
    49  			exp: &RangeAggregationExpr{
    50  				Operation: "count_over_time",
    51  				Left: &LogRange{
    52  					Left: newPipelineExpr(
    53  						newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "foo", Value: "bar"}}),
    54  						MultiStageExpr{
    55  							newLineFilterExpr(labels.MatchEqual, "", "error"),
    56  						},
    57  					),
    58  					Interval: 12 * time.Hour,
    59  				},
    60  			},
    61  		},
    62  		{
    63  			// test [12h] after filter expr
    64  			in: `count_over_time({foo="bar"} |= "error" [12h])`,
    65  			exp: &RangeAggregationExpr{
    66  				Operation: "count_over_time",
    67  				Left: &LogRange{
    68  					Left: newPipelineExpr(
    69  						newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "foo", Value: "bar"}}),
    70  						MultiStageExpr{newLineFilterExpr(labels.MatchEqual, "", "error")},
    71  					),
    72  					Interval: 12 * time.Hour,
    73  				},
    74  			},
    75  		},
    76  		{
    77  			in:  `{foo="bar"}`,
    78  			exp: &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
    79  		},
    80  		{
    81  			in:  `{ foo = "bar" }`,
    82  			exp: &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
    83  		},
    84  		{
    85  			in: `{ namespace="buzz", foo != "bar" }`,
    86  			exp: &MatchersExpr{Mts: []*labels.Matcher{
    87  				mustNewMatcher(labels.MatchEqual, "namespace", "buzz"),
    88  				mustNewMatcher(labels.MatchNotEqual, "foo", "bar"),
    89  			}},
    90  		},
    91  		{
    92  			in:  `{ foo =~ "bar" }`,
    93  			exp: &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchRegexp, "foo", "bar")}},
    94  		},
    95  		{
    96  			in: `{ namespace="buzz", foo !~ "bar" }`,
    97  			exp: &MatchersExpr{Mts: []*labels.Matcher{
    98  				mustNewMatcher(labels.MatchEqual, "namespace", "buzz"),
    99  				mustNewMatcher(labels.MatchNotRegexp, "foo", "bar"),
   100  			}},
   101  		},
   102  		{
   103  			in: `count_over_time({ foo = "bar" }[12m])`,
   104  			exp: &RangeAggregationExpr{
   105  				Left: &LogRange{
   106  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   107  					Interval: 12 * time.Minute,
   108  				},
   109  				Operation: "count_over_time",
   110  			},
   111  		},
   112  		{
   113  			in: `bytes_over_time({ foo = "bar" }[12m])`,
   114  			exp: &RangeAggregationExpr{
   115  				Left: &LogRange{
   116  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   117  					Interval: 12 * time.Minute,
   118  				},
   119  				Operation: OpRangeTypeBytes,
   120  			},
   121  		},
   122  		{
   123  			in: `bytes_rate({ foo = "bar" }[12m])`,
   124  			exp: &RangeAggregationExpr{
   125  				Left: &LogRange{
   126  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   127  					Interval: 12 * time.Minute,
   128  				},
   129  				Operation: OpRangeTypeBytesRate,
   130  			},
   131  		},
   132  		{
   133  			in: `rate({ foo = "bar" }[5h])`,
   134  			exp: &RangeAggregationExpr{
   135  				Left: &LogRange{
   136  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   137  					Interval: 5 * time.Hour,
   138  				},
   139  				Operation: "rate",
   140  			},
   141  		},
   142  		{
   143  			in: `{ foo = "bar" }|logfmt|rate="a"`, // rate should also be able to use it as IDENTIFIER
   144  			exp: newPipelineExpr(
   145  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   146  				MultiStageExpr{
   147  					newLabelParserExpr(OpParserTypeLogfmt, ""),
   148  					newLabelFilterExpr(log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "rate", "a"))),
   149  				},
   150  			),
   151  		},
   152  		{
   153  			in: `{ foo = "bar" }|logfmt|length>5d`,
   154  			exp: newPipelineExpr(
   155  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   156  				MultiStageExpr{
   157  					newLabelParserExpr(OpParserTypeLogfmt, ""),
   158  					newLabelFilterExpr(log.NewDurationLabelFilter(log.LabelFilterGreaterThan, "length", 5*24*time.Hour)),
   159  				},
   160  			),
   161  		},
   162  		{
   163  			in: `rate({ foo = "bar" }[5d])`,
   164  			exp: &RangeAggregationExpr{
   165  				Left: &LogRange{
   166  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   167  					Interval: 5 * 24 * time.Hour,
   168  				},
   169  				Operation: "rate",
   170  			},
   171  		},
   172  		{
   173  			in: `count_over_time({ foo = "bar" }[1w])`,
   174  			exp: &RangeAggregationExpr{
   175  				Left: &LogRange{
   176  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   177  					Interval: 7 * 24 * time.Hour,
   178  				},
   179  				Operation: "count_over_time",
   180  			},
   181  		},
   182  		{
   183  			in: `absent_over_time({ foo = "bar" }[1w])`,
   184  			exp: &RangeAggregationExpr{
   185  				Left: &LogRange{
   186  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   187  					Interval: 7 * 24 * time.Hour,
   188  				},
   189  				Operation: OpRangeTypeAbsent,
   190  			},
   191  		},
   192  		{
   193  			in: `sum(rate({ foo = "bar" }[5h]))`,
   194  			exp: mustNewVectorAggregationExpr(&RangeAggregationExpr{
   195  				Left: &LogRange{
   196  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   197  					Interval: 5 * time.Hour,
   198  				},
   199  				Operation: "rate",
   200  			}, "sum", nil, nil),
   201  		},
   202  		{
   203  			in: `sum(rate({ foo ="bar" }[1y]))`,
   204  			exp: mustNewVectorAggregationExpr(&RangeAggregationExpr{
   205  				Left: &LogRange{
   206  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   207  					Interval: 365 * 24 * time.Hour,
   208  				},
   209  				Operation: "rate",
   210  			}, "sum", nil, nil),
   211  		},
   212  		{
   213  			in: `avg(count_over_time({ foo = "bar" }[5h])) by (bar,foo)`,
   214  			exp: mustNewVectorAggregationExpr(&RangeAggregationExpr{
   215  				Left: &LogRange{
   216  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   217  					Interval: 5 * time.Hour,
   218  				},
   219  				Operation: "count_over_time",
   220  			}, "avg", &Grouping{
   221  				Without: false,
   222  				Groups:  []string{"bar", "foo"},
   223  			}, nil),
   224  		},
   225  		{
   226  			in: `avg(
   227  					label_replace(
   228  						count_over_time({ foo = "bar" }[5h]),
   229  						"bar",
   230  						"$1$2",
   231  						"foo",
   232  						"(.*).(.*)"
   233  					)
   234  				) by (bar,foo)`,
   235  			exp: mustNewVectorAggregationExpr(
   236  				mustNewLabelReplaceExpr(
   237  					&RangeAggregationExpr{
   238  						Left: &LogRange{
   239  							Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   240  							Interval: 5 * time.Hour,
   241  						},
   242  						Operation: "count_over_time",
   243  					},
   244  					"bar", "$1$2", "foo", "(.*).(.*)",
   245  				),
   246  				"avg", &Grouping{
   247  					Without: false,
   248  					Groups:  []string{"bar", "foo"},
   249  				}, nil),
   250  		},
   251  		{
   252  			in: `avg(count_over_time({ foo = "bar" }[5h])) by ()`,
   253  			exp: mustNewVectorAggregationExpr(&RangeAggregationExpr{
   254  				Left: &LogRange{
   255  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   256  					Interval: 5 * time.Hour,
   257  				},
   258  				Operation: "count_over_time",
   259  			}, "avg", &Grouping{
   260  				Without: false,
   261  				Groups:  nil,
   262  			}, nil),
   263  		},
   264  		{
   265  			in: `max without (bar) (count_over_time({ foo = "bar" }[5h]))`,
   266  			exp: mustNewVectorAggregationExpr(&RangeAggregationExpr{
   267  				Left: &LogRange{
   268  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   269  					Interval: 5 * time.Hour,
   270  				},
   271  				Operation: "count_over_time",
   272  			}, "max", &Grouping{
   273  				Without: true,
   274  				Groups:  []string{"bar"},
   275  			}, nil),
   276  		},
   277  		{
   278  			in: `max without () (count_over_time({ foo = "bar" }[5h]))`,
   279  			exp: mustNewVectorAggregationExpr(&RangeAggregationExpr{
   280  				Left: &LogRange{
   281  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   282  					Interval: 5 * time.Hour,
   283  				},
   284  				Operation: "count_over_time",
   285  			}, "max", &Grouping{
   286  				Without: true,
   287  				Groups:  nil,
   288  			}, nil),
   289  		},
   290  		{
   291  			in: `topk(10,count_over_time({ foo = "bar" }[5h])) without (bar)`,
   292  			exp: mustNewVectorAggregationExpr(&RangeAggregationExpr{
   293  				Left: &LogRange{
   294  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   295  					Interval: 5 * time.Hour,
   296  				},
   297  				Operation: "count_over_time",
   298  			}, "topk", &Grouping{
   299  				Without: true,
   300  				Groups:  []string{"bar"},
   301  			}, NewStringLabelFilter("10")),
   302  		},
   303  		{
   304  			in: `bottomk(30 ,sum(rate({ foo = "bar" }[5h])) by (foo))`,
   305  			exp: mustNewVectorAggregationExpr(mustNewVectorAggregationExpr(&RangeAggregationExpr{
   306  				Left: &LogRange{
   307  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   308  					Interval: 5 * time.Hour,
   309  				},
   310  				Operation: "rate",
   311  			}, "sum", &Grouping{
   312  				Groups:  []string{"foo"},
   313  				Without: false,
   314  			}, nil), "bottomk", nil,
   315  				NewStringLabelFilter("30")),
   316  		},
   317  		{
   318  			in: `max( sum(count_over_time({ foo = "bar" }[5h])) without (foo,bar) ) by (foo)`,
   319  			exp: mustNewVectorAggregationExpr(mustNewVectorAggregationExpr(&RangeAggregationExpr{
   320  				Left: &LogRange{
   321  					Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
   322  					Interval: 5 * time.Hour,
   323  				},
   324  				Operation: "count_over_time",
   325  			}, "sum", &Grouping{
   326  				Groups:  []string{"foo", "bar"},
   327  				Without: true,
   328  			}, nil), "max", &Grouping{
   329  				Groups:  []string{"foo"},
   330  				Without: false,
   331  			}, nil),
   332  		},
   333  		{
   334  			in:  `unk({ foo = "bar" }[5m])`,
   335  			err: logqlmodel.NewParseError("syntax error: unexpected IDENTIFIER", 1, 1),
   336  		},
   337  		{
   338  			in:  `absent_over_time({ foo = "bar" }[5h]) by (foo)`,
   339  			err: logqlmodel.NewParseError("grouping not allowed for absent_over_time aggregation", 0, 0),
   340  		},
   341  		{
   342  			in:  `rate({ foo = "bar" }[5minutes])`,
   343  			err: logqlmodel.NewParseError(`not a valid duration string: "5minutes"`, 0, 21),
   344  		},
   345  		{
   346  			in:  `label_replace(rate({ foo = "bar" }[5m]),"")`,
   347  			err: logqlmodel.NewParseError(`syntax error: unexpected ), expecting ,`, 1, 43),
   348  		},
   349  		{
   350  			in:  `label_replace(rate({ foo = "bar" }[5m]),"foo","$1","bar","^^^^x43\\q")`,
   351  			err: logqlmodel.NewParseError("invalid regex in label_replace: error parsing regexp: invalid escape sequence: `\\q`", 0, 0),
   352  		},
   353  		{
   354  			in:  `rate({ foo = "bar" }[5)`,
   355  			err: logqlmodel.NewParseError("missing closing ']' in duration", 0, 21),
   356  		},
   357  		{
   358  			in:  `min({ foo = "bar" }[5m])`,
   359  			err: logqlmodel.NewParseError("syntax error: unexpected RANGE", 0, 20),
   360  		},
   361  		// line filter for ip-matcher
   362  		{
   363  			in: `{foo="bar"} |= "baz" |= ip("123.123.123.123")`,
   364  			exp: newPipelineExpr(
   365  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   366  				MultiStageExpr{
   367  					newNestedLineFilterExpr(
   368  						newLineFilterExpr(labels.MatchEqual, "", "baz"),
   369  						newLineFilterExpr(labels.MatchEqual, OpFilterIP, "123.123.123.123"),
   370  					),
   371  				},
   372  			),
   373  		},
   374  		{
   375  			in: `{ foo = "bar" , ip="foo"}|logfmt|= ip("127.0.0.1")|ip="2.3.4.5"|ip="abc"|ipaddr=ip("4.5.6.7")|ip=ip("6.7.8.9")`,
   376  			exp: newPipelineExpr(
   377  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar"), mustNewMatcher(labels.MatchEqual, "ip", "foo")}),
   378  				MultiStageExpr{
   379  					newLabelParserExpr(OpParserTypeLogfmt, ""),
   380  					newLineFilterExpr(labels.MatchEqual, OpFilterIP, "127.0.0.1"),
   381  					newLabelFilterExpr(log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "ip", "2.3.4.5"))),
   382  					newLabelFilterExpr(log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "ip", "abc"))),
   383  					newLabelFilterExpr(log.NewIPLabelFilter("4.5.6.7", "ipaddr", log.LabelFilterEqual)),
   384  					newLabelFilterExpr(log.NewIPLabelFilter("6.7.8.9", "ip", log.LabelFilterEqual)),
   385  				},
   386  			),
   387  		},
   388  		{
   389  			in: `{foo="bar"} |= ip("123.123.123.123")`,
   390  			exp: newPipelineExpr(
   391  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   392  				MultiStageExpr{
   393  					newLineFilterExpr(labels.MatchEqual, OpFilterIP, "123.123.123.123"),
   394  				},
   395  			),
   396  		},
   397  		{
   398  			in: `{foo="bar"} |= ip("123.123.123.123")|= "baz"`,
   399  			exp: newPipelineExpr(
   400  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   401  				MultiStageExpr{
   402  					newNestedLineFilterExpr(
   403  						newLineFilterExpr(labels.MatchEqual, OpFilterIP, "123.123.123.123"),
   404  						newLineFilterExpr(labels.MatchEqual, "", "baz"),
   405  					),
   406  				},
   407  			),
   408  		},
   409  		{
   410  			in: `{foo="bar"} |= ip("123.123.123.123")|= "baz" |=ip("123.123.123.123")`,
   411  			exp: newPipelineExpr(
   412  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   413  				MultiStageExpr{
   414  					newNestedLineFilterExpr(
   415  						newNestedLineFilterExpr(
   416  							newLineFilterExpr(labels.MatchEqual, OpFilterIP, "123.123.123.123"),
   417  							newLineFilterExpr(labels.MatchEqual, "", "baz"),
   418  						),
   419  						newLineFilterExpr(labels.MatchEqual, OpFilterIP, "123.123.123.123"),
   420  					),
   421  				},
   422  			),
   423  		},
   424  		{
   425  			in: `{foo="bar"} |= "baz" |= ip("123.123.123.123")`,
   426  			exp: newPipelineExpr(
   427  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   428  				MultiStageExpr{
   429  					newNestedLineFilterExpr(
   430  						newLineFilterExpr(labels.MatchEqual, "", "baz"),
   431  						newLineFilterExpr(labels.MatchEqual, OpFilterIP, "123.123.123.123"),
   432  					),
   433  				},
   434  			),
   435  		},
   436  		{
   437  			in: `{foo="bar"} != ip("123.123.123.123")`,
   438  			exp: newPipelineExpr(
   439  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   440  				MultiStageExpr{
   441  					newLineFilterExpr(labels.MatchNotEqual, OpFilterIP, "123.123.123.123"),
   442  				},
   443  			),
   444  		},
   445  		{
   446  			in: `{foo="bar"} != ip("123.123.123.123")|= "baz"`,
   447  			exp: newPipelineExpr(
   448  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   449  				MultiStageExpr{
   450  					newNestedLineFilterExpr(
   451  						newLineFilterExpr(labels.MatchNotEqual, OpFilterIP, "123.123.123.123"),
   452  						newLineFilterExpr(labels.MatchEqual, "", "baz"),
   453  					),
   454  				},
   455  			),
   456  		},
   457  		{
   458  			in: `{foo="bar"} != ip("123.123.123.123")|= "baz" !=ip("123.123.123.123")`,
   459  			exp: newPipelineExpr(
   460  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   461  				MultiStageExpr{
   462  					newNestedLineFilterExpr(
   463  						newNestedLineFilterExpr(
   464  							newLineFilterExpr(labels.MatchNotEqual, OpFilterIP, "123.123.123.123"),
   465  							newLineFilterExpr(labels.MatchEqual, "", "baz"),
   466  						),
   467  						newLineFilterExpr(labels.MatchNotEqual, OpFilterIP, "123.123.123.123"),
   468  					),
   469  				},
   470  			),
   471  		},
   472  		// label filter for ip-matcher
   473  		{
   474  			in:  `{ foo = "bar" }|logfmt|addr>=ip("1.2.3.4")`,
   475  			err: logqlmodel.NewParseError("syntax error: unexpected ip, expecting BYTES or NUMBER or DURATION", 1, 30),
   476  		},
   477  		{
   478  			in:  `{ foo = "bar" }|logfmt|addr>ip("1.2.3.4")`,
   479  			err: logqlmodel.NewParseError("syntax error: unexpected ip, expecting BYTES or NUMBER or DURATION", 1, 29),
   480  		},
   481  		{
   482  			in:  `{ foo = "bar" }|logfmt|addr<=ip("1.2.3.4")`,
   483  			err: logqlmodel.NewParseError("syntax error: unexpected ip, expecting BYTES or NUMBER or DURATION", 1, 30),
   484  		},
   485  		{
   486  			in:  `{ foo = "bar" }|logfmt|addr<ip("1.2.3.4")`,
   487  			err: logqlmodel.NewParseError("syntax error: unexpected ip, expecting BYTES or NUMBER or DURATION", 1, 29),
   488  		},
   489  		{
   490  			in: `{ foo = "bar" }|logfmt|addr=ip("1.2.3.4")`,
   491  			exp: newPipelineExpr(
   492  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   493  				MultiStageExpr{newLabelParserExpr(OpParserTypeLogfmt, ""), newLabelFilterExpr(log.NewIPLabelFilter("1.2.3.4", "addr", log.LabelFilterEqual))},
   494  			),
   495  		},
   496  		{
   497  			in: `{ foo = "bar" }|logfmt|addr!=ip("1.2.3.4")`,
   498  			exp: newPipelineExpr(
   499  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   500  				MultiStageExpr{newLabelParserExpr(OpParserTypeLogfmt, ""), newLabelFilterExpr(log.NewIPLabelFilter("1.2.3.4", "addr", log.LabelFilterNotEqual))},
   501  			),
   502  		},
   503  		{
   504  			in: `{ foo = "bar" }|logfmt|level="error"|addr=ip("1.2.3.4")`,
   505  			exp: newPipelineExpr(
   506  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   507  				MultiStageExpr{
   508  					newLabelParserExpr(OpParserTypeLogfmt, ""),
   509  					newLabelFilterExpr(log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "level", "error"))),
   510  					newLabelFilterExpr(log.NewIPLabelFilter("1.2.3.4", "addr", log.LabelFilterEqual)),
   511  				},
   512  			),
   513  		},
   514  		{
   515  			in: `{ foo = "bar" }|logfmt|level="error"|addr!=ip("1.2.3.4")`,
   516  			exp: newPipelineExpr(
   517  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   518  				MultiStageExpr{
   519  					newLabelParserExpr(OpParserTypeLogfmt, ""),
   520  					newLabelFilterExpr(log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "level", "error"))),
   521  					newLabelFilterExpr(log.NewIPLabelFilter("1.2.3.4", "addr", log.LabelFilterNotEqual)),
   522  				},
   523  			),
   524  		},
   525  		{
   526  			in: `{ foo = "bar" }|logfmt|remote_addr=ip("2.3.4.5")|level="error"|addr=ip("1.2.3.4")`, // chain label filters with ip matcher
   527  			exp: newPipelineExpr(
   528  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   529  				MultiStageExpr{
   530  					newLabelParserExpr(OpParserTypeLogfmt, ""),
   531  					newLabelFilterExpr(log.NewIPLabelFilter("2.3.4.5", "remote_addr", log.LabelFilterEqual)),
   532  					newLabelFilterExpr(log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "level", "error"))),
   533  					newLabelFilterExpr(log.NewIPLabelFilter("1.2.3.4", "addr", log.LabelFilterEqual)),
   534  				},
   535  			),
   536  		},
   537  		{
   538  			in: `{ foo = "bar" }|logfmt|remote_addr!=ip("2.3.4.5")|level="error"|addr!=ip("1.2.3.4")`, // chain label filters with ip matcher
   539  			exp: newPipelineExpr(
   540  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   541  				MultiStageExpr{
   542  					newLabelParserExpr(OpParserTypeLogfmt, ""),
   543  					newLabelFilterExpr(log.NewIPLabelFilter("2.3.4.5", "remote_addr", log.LabelFilterNotEqual)),
   544  					newLabelFilterExpr(log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "level", "error"))),
   545  					newLabelFilterExpr(log.NewIPLabelFilter("1.2.3.4", "addr", log.LabelFilterNotEqual)),
   546  				},
   547  			),
   548  		},
   549  		{
   550  			in: `{ foo = "bar" }|logfmt|remote_addr=ip("2.3.4.5")|level="error"|addr!=ip("1.2.3.4")`, // chain label filters with ip matcher
   551  			exp: newPipelineExpr(
   552  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   553  				MultiStageExpr{
   554  					newLabelParserExpr(OpParserTypeLogfmt, ""),
   555  					newLabelFilterExpr(log.NewIPLabelFilter("2.3.4.5", "remote_addr", log.LabelFilterEqual)),
   556  					newLabelFilterExpr(log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "level", "error"))),
   557  					newLabelFilterExpr(log.NewIPLabelFilter("1.2.3.4", "addr", log.LabelFilterNotEqual)),
   558  				},
   559  			),
   560  		},
   561  		{
   562  			in: `{ foo = "bar" }|logfmt|ip="2.3.4.5"`, // just using `ip` as a label name(identifier) should work
   563  			exp: newPipelineExpr(
   564  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   565  				MultiStageExpr{
   566  					newLabelParserExpr(OpParserTypeLogfmt, ""),
   567  					newLabelFilterExpr(log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "ip", "2.3.4.5"))),
   568  				},
   569  			),
   570  		},
   571  		{
   572  			in: `{ foo = "bar" }|logfmt|ip="2.3.4.5"|ip="abc"`, // just using `ip` as a label name should work with chaining
   573  			exp: newPipelineExpr(
   574  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   575  				MultiStageExpr{
   576  					newLabelParserExpr(OpParserTypeLogfmt, ""),
   577  					newLabelFilterExpr(log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "ip", "2.3.4.5"))),
   578  					newLabelFilterExpr(log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "ip", "abc"))),
   579  				},
   580  			),
   581  		},
   582  		{
   583  			in: `{ foo = "bar" }|logfmt|ip="2.3.4.5"|ip="abc"|ipaddr=ip("4.5.6.7")`, // `ip` should work as both label name and filter in same query
   584  			exp: newPipelineExpr(
   585  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   586  				MultiStageExpr{
   587  					newLabelParserExpr(OpParserTypeLogfmt, ""),
   588  					newLabelFilterExpr(log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "ip", "2.3.4.5"))),
   589  					newLabelFilterExpr(log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "ip", "abc"))),
   590  					newLabelFilterExpr(log.NewIPLabelFilter("4.5.6.7", "ipaddr", log.LabelFilterEqual)),
   591  				},
   592  			),
   593  		},
   594  		{
   595  			in: `{ foo = "bar" }|logfmt|ip="2.3.4.5"|ip="abc"|ipaddr=ip("4.5.6.7")|ip=ip("6.7.8.9")`, // `ip` should work as both label name and filter in same query with same name.
   596  			exp: newPipelineExpr(
   597  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   598  				MultiStageExpr{
   599  					newLabelParserExpr(OpParserTypeLogfmt, ""),
   600  					newLabelFilterExpr(log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "ip", "2.3.4.5"))),
   601  					newLabelFilterExpr(log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "ip", "abc"))),
   602  					newLabelFilterExpr(log.NewIPLabelFilter("4.5.6.7", "ipaddr", log.LabelFilterEqual)),
   603  					newLabelFilterExpr(log.NewIPLabelFilter("6.7.8.9", "ip", log.LabelFilterEqual)),
   604  				},
   605  			),
   606  		},
   607  		{
   608  			in:  `sum(3 ,count_over_time({ foo = "bar" }[5h]))`,
   609  			err: logqlmodel.NewParseError("unsupported parameter for operation sum(3,", 0, 0),
   610  		},
   611  		{
   612  			in:  `topk(count_over_time({ foo = "bar" }[5h]))`,
   613  			err: logqlmodel.NewParseError("parameter required for operation topk", 0, 0),
   614  		},
   615  		{
   616  			in:  `bottomk(he,count_over_time({ foo = "bar" }[5h]))`,
   617  			err: logqlmodel.NewParseError("syntax error: unexpected IDENTIFIER", 1, 9),
   618  		},
   619  		{
   620  			in:  `bottomk(1.2,count_over_time({ foo = "bar" }[5h]))`,
   621  			err: logqlmodel.NewParseError("invalid parameter bottomk(1.2,", 0, 0),
   622  		},
   623  		{
   624  			in:  `stddev({ foo = "bar" })`,
   625  			err: logqlmodel.NewParseError("syntax error: unexpected )", 1, 23),
   626  		},
   627  		{
   628  			in: `{ foo = "bar", bar != "baz" }`,
   629  			exp: &MatchersExpr{Mts: []*labels.Matcher{
   630  				mustNewMatcher(labels.MatchEqual, "foo", "bar"),
   631  				mustNewMatcher(labels.MatchNotEqual, "bar", "baz"),
   632  			}},
   633  		},
   634  		{
   635  			in: `{foo="bar"} |= "baz"`,
   636  			exp: newPipelineExpr(
   637  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   638  				MultiStageExpr{newLineFilterExpr(labels.MatchEqual, "", "baz")},
   639  			),
   640  		},
   641  		{
   642  			in: `{foo="bar"} |= "baz" |~ "blip" != "flip" !~ "flap"`,
   643  			exp: newPipelineExpr(
   644  				newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   645  				MultiStageExpr{
   646  					newNestedLineFilterExpr(
   647  						newNestedLineFilterExpr(
   648  							newNestedLineFilterExpr(
   649  								newLineFilterExpr(labels.MatchEqual, "", "baz"),
   650  								newLineFilterExpr(labels.MatchRegexp, "", "blip"),
   651  							),
   652  							newLineFilterExpr(labels.MatchNotEqual, "", "flip"),
   653  						),
   654  						newLineFilterExpr(labels.MatchNotRegexp, "", "flap"),
   655  					),
   656  				},
   657  			),
   658  		},
   659  		{
   660  			in: `count_over_time(({foo="bar"} |= "baz" |~ "blip" != "flip" !~ "flap")[5m])`,
   661  			exp: newRangeAggregationExpr(
   662  				&LogRange{
   663  					Left: newPipelineExpr(
   664  						newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   665  						MultiStageExpr{
   666  							newNestedLineFilterExpr(
   667  								newNestedLineFilterExpr(
   668  									newNestedLineFilterExpr(
   669  										newLineFilterExpr(labels.MatchEqual, "", "baz"),
   670  										newLineFilterExpr(labels.MatchRegexp, "", "blip"),
   671  									),
   672  									newLineFilterExpr(labels.MatchNotEqual, "", "flip"),
   673  								),
   674  								newLineFilterExpr(labels.MatchNotRegexp, "", "flap"),
   675  							),
   676  						},
   677  					),
   678  					Interval: 5 * time.Minute,
   679  				}, OpRangeTypeCount, nil, nil),
   680  		},
   681  		{
   682  			in: `bytes_over_time(({foo="bar"} |= "baz" |~ "blip" != "flip" !~ "flap")[5m])`,
   683  			exp: newRangeAggregationExpr(
   684  				&LogRange{
   685  					Left: newPipelineExpr(
   686  						newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   687  						MultiStageExpr{
   688  							newNestedLineFilterExpr(
   689  								newNestedLineFilterExpr(
   690  									newNestedLineFilterExpr(
   691  										newLineFilterExpr(labels.MatchEqual, "", "baz"),
   692  										newLineFilterExpr(labels.MatchRegexp, "", "blip"),
   693  									),
   694  									newLineFilterExpr(labels.MatchNotEqual, "", "flip"),
   695  								),
   696  								newLineFilterExpr(labels.MatchNotRegexp, "", "flap"),
   697  							),
   698  						},
   699  					),
   700  					Interval: 5 * time.Minute,
   701  				}, OpRangeTypeBytes, nil, nil),
   702  		},
   703  		{
   704  			in: `bytes_over_time(({foo="bar"} |= "baz" |~ "blip" != "flip" !~ "flap" | unpack)[5m])`,
   705  			exp: newRangeAggregationExpr(
   706  				&LogRange{
   707  					Left: newPipelineExpr(
   708  						newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   709  						MultiStageExpr{
   710  							newNestedLineFilterExpr(
   711  								newNestedLineFilterExpr(
   712  									newNestedLineFilterExpr(
   713  										newLineFilterExpr(labels.MatchEqual, "", "baz"),
   714  										newLineFilterExpr(labels.MatchRegexp, "", "blip"),
   715  									),
   716  									newLineFilterExpr(labels.MatchNotEqual, "", "flip"),
   717  								),
   718  								newLineFilterExpr(labels.MatchNotRegexp, "", "flap"),
   719  							),
   720  							newLabelParserExpr(OpParserTypeUnpack, ""),
   721  						},
   722  					),
   723  					Interval: 5 * time.Minute,
   724  				}, OpRangeTypeBytes, nil, nil),
   725  		},
   726  		{
   727  			in: `
   728  			label_replace(
   729  				bytes_over_time(({foo="bar"} |= "baz" |~ "blip" != "flip" !~ "flap")[5m]),
   730  				"buzz",
   731  				"$2",
   732  				"bar",
   733  				"(.*):(.*)"
   734  			)
   735  			`,
   736  			exp: mustNewLabelReplaceExpr(
   737  				newRangeAggregationExpr(
   738  					&LogRange{
   739  						Left: newPipelineExpr(
   740  							newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   741  							MultiStageExpr{
   742  								newNestedLineFilterExpr(
   743  									newNestedLineFilterExpr(
   744  										newNestedLineFilterExpr(
   745  											newLineFilterExpr(labels.MatchEqual, "", "baz"),
   746  											newLineFilterExpr(labels.MatchRegexp, "", "blip"),
   747  										),
   748  										newLineFilterExpr(labels.MatchNotEqual, "", "flip"),
   749  									),
   750  									newLineFilterExpr(labels.MatchNotRegexp, "", "flap"),
   751  								),
   752  							},
   753  						),
   754  						Interval: 5 * time.Minute,
   755  					}, OpRangeTypeBytes, nil, nil),
   756  				"buzz",
   757  				"$2",
   758  				"bar",
   759  				"(.*):(.*)",
   760  			),
   761  		},
   762  		{
   763  			in: `sum(count_over_time(({foo="bar"} |= "baz" |~ "blip" != "flip" !~ "flap")[5m])) by (foo)`,
   764  			exp: mustNewVectorAggregationExpr(newRangeAggregationExpr(
   765  				&LogRange{
   766  					Left: newPipelineExpr(
   767  						newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   768  						MultiStageExpr{
   769  							newNestedLineFilterExpr(
   770  								newNestedLineFilterExpr(
   771  									newNestedLineFilterExpr(
   772  										newLineFilterExpr(labels.MatchEqual, "", "baz"),
   773  										newLineFilterExpr(labels.MatchRegexp, "", "blip"),
   774  									),
   775  									newLineFilterExpr(labels.MatchNotEqual, "", "flip"),
   776  								),
   777  								newLineFilterExpr(labels.MatchNotRegexp, "", "flap"),
   778  							),
   779  						},
   780  					),
   781  					Interval: 5 * time.Minute,
   782  				}, OpRangeTypeCount, nil, nil),
   783  				"sum",
   784  				&Grouping{
   785  					Without: false,
   786  					Groups:  []string{"foo"},
   787  				},
   788  				nil),
   789  		},
   790  		{
   791  			in: `sum(bytes_rate(({foo="bar"} |= "baz" |~ "blip" != "flip" !~ "flap")[5m])) by (foo)`,
   792  			exp: mustNewVectorAggregationExpr(newRangeAggregationExpr(
   793  				&LogRange{
   794  					Left: newPipelineExpr(
   795  						newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   796  						MultiStageExpr{
   797  							newNestedLineFilterExpr(
   798  								newNestedLineFilterExpr(
   799  									newNestedLineFilterExpr(
   800  										newLineFilterExpr(labels.MatchEqual, "", "baz"),
   801  										newLineFilterExpr(labels.MatchRegexp, "", "blip"),
   802  									),
   803  									newLineFilterExpr(labels.MatchNotEqual, "", "flip"),
   804  								),
   805  								newLineFilterExpr(labels.MatchNotRegexp, "", "flap"),
   806  							),
   807  						},
   808  					),
   809  					Interval: 5 * time.Minute,
   810  				}, OpRangeTypeBytesRate, nil, nil),
   811  				"sum",
   812  				&Grouping{
   813  					Without: false,
   814  					Groups:  []string{"foo"},
   815  				},
   816  				nil),
   817  		},
   818  		{
   819  			in: `topk(5,count_over_time(({foo="bar"} |= "baz" |~ "blip" != "flip" !~ "flap")[5m])) without (foo)`,
   820  			exp: mustNewVectorAggregationExpr(newRangeAggregationExpr(
   821  				&LogRange{
   822  					Left: newPipelineExpr(
   823  						newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   824  						MultiStageExpr{
   825  							newNestedLineFilterExpr(
   826  								newNestedLineFilterExpr(
   827  									newNestedLineFilterExpr(
   828  										newLineFilterExpr(labels.MatchEqual, "", "baz"),
   829  										newLineFilterExpr(labels.MatchRegexp, "", "blip"),
   830  									),
   831  									newLineFilterExpr(labels.MatchNotEqual, "", "flip"),
   832  								),
   833  								newLineFilterExpr(labels.MatchNotRegexp, "", "flap"),
   834  							),
   835  						},
   836  					),
   837  					Interval: 5 * time.Minute,
   838  				}, OpRangeTypeCount, nil, nil),
   839  				"topk",
   840  				&Grouping{
   841  					Without: true,
   842  					Groups:  []string{"foo"},
   843  				},
   844  				NewStringLabelFilter("5")),
   845  		},
   846  		{
   847  			in: `topk(5,sum(rate(({foo="bar"} |= "baz" |~ "blip" != "flip" !~ "flap")[5m])) by (app))`,
   848  			exp: mustNewVectorAggregationExpr(
   849  				mustNewVectorAggregationExpr(
   850  					newRangeAggregationExpr(
   851  						&LogRange{
   852  							Left: newPipelineExpr(
   853  								newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   854  								MultiStageExpr{
   855  									newNestedLineFilterExpr(
   856  										newNestedLineFilterExpr(
   857  											newNestedLineFilterExpr(
   858  												newLineFilterExpr(labels.MatchEqual, "", "baz"),
   859  												newLineFilterExpr(labels.MatchRegexp, "", "blip"),
   860  											),
   861  											newLineFilterExpr(labels.MatchNotEqual, "", "flip"),
   862  										),
   863  										newLineFilterExpr(labels.MatchNotRegexp, "", "flap"),
   864  									),
   865  								},
   866  							),
   867  							Interval: 5 * time.Minute,
   868  						}, OpRangeTypeRate, nil, nil),
   869  					"sum",
   870  					&Grouping{
   871  						Without: false,
   872  						Groups:  []string{"app"},
   873  					},
   874  					nil),
   875  				"topk",
   876  				nil,
   877  				NewStringLabelFilter("5")),
   878  		},
   879  		{
   880  			in: `count_over_time({foo="bar"}[5m] |= "baz" |~ "blip" != "flip" !~ "flap")`,
   881  			exp: newRangeAggregationExpr(
   882  				&LogRange{
   883  					Left: newPipelineExpr(
   884  						newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   885  						MultiStageExpr{
   886  							newNestedLineFilterExpr(
   887  								newNestedLineFilterExpr(
   888  									newNestedLineFilterExpr(
   889  										newLineFilterExpr(labels.MatchEqual, "", "baz"),
   890  										newLineFilterExpr(labels.MatchRegexp, "", "blip"),
   891  									),
   892  									newLineFilterExpr(labels.MatchNotEqual, "", "flip"),
   893  								),
   894  								newLineFilterExpr(labels.MatchNotRegexp, "", "flap"),
   895  							),
   896  						},
   897  					),
   898  					Interval: 5 * time.Minute,
   899  				}, OpRangeTypeCount, nil, nil),
   900  		},
   901  		{
   902  			in: `sum(count_over_time({foo="bar"}[5m] |= "baz" |~ "blip" != "flip" !~ "flap")) by (foo)`,
   903  			exp: mustNewVectorAggregationExpr(newRangeAggregationExpr(
   904  				&LogRange{
   905  					Left: newPipelineExpr(
   906  						newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   907  						MultiStageExpr{
   908  							newNestedLineFilterExpr(
   909  								newNestedLineFilterExpr(
   910  									newNestedLineFilterExpr(
   911  										newLineFilterExpr(labels.MatchEqual, "", "baz"),
   912  										newLineFilterExpr(labels.MatchRegexp, "", "blip"),
   913  									),
   914  									newLineFilterExpr(labels.MatchNotEqual, "", "flip"),
   915  								),
   916  								newLineFilterExpr(labels.MatchNotRegexp, "", "flap"),
   917  							),
   918  						},
   919  					),
   920  					Interval: 5 * time.Minute,
   921  				}, OpRangeTypeCount, nil, nil),
   922  				"sum",
   923  				&Grouping{
   924  					Without: false,
   925  					Groups:  []string{"foo"},
   926  				},
   927  				nil),
   928  		},
   929  		{
   930  			in: `topk(5,count_over_time({foo="bar"}[5m] |= "baz" |~ "blip" != "flip" !~ "flap")) without (foo)`,
   931  			exp: mustNewVectorAggregationExpr(newRangeAggregationExpr(
   932  				&LogRange{
   933  					Left: newPipelineExpr(
   934  						newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   935  						MultiStageExpr{
   936  							newNestedLineFilterExpr(
   937  								newNestedLineFilterExpr(
   938  									newNestedLineFilterExpr(
   939  										newLineFilterExpr(labels.MatchEqual, "", "baz"),
   940  										newLineFilterExpr(labels.MatchRegexp, "", "blip"),
   941  									),
   942  									newLineFilterExpr(labels.MatchNotEqual, "", "flip"),
   943  								),
   944  								newLineFilterExpr(labels.MatchNotRegexp, "", "flap"),
   945  							),
   946  						},
   947  					),
   948  					Interval: 5 * time.Minute,
   949  				}, OpRangeTypeCount, nil, nil),
   950  				"topk",
   951  				&Grouping{
   952  					Without: true,
   953  					Groups:  []string{"foo"},
   954  				},
   955  				NewStringLabelFilter("5")),
   956  		},
   957  		{
   958  			in: `topk(5,sum(rate({foo="bar"}[5m] |= "baz" |~ "blip" != "flip" !~ "flap")) by (app))`,
   959  			exp: mustNewVectorAggregationExpr(
   960  				mustNewVectorAggregationExpr(
   961  					newRangeAggregationExpr(
   962  						&LogRange{
   963  							Left: newPipelineExpr(
   964  								newMatcherExpr([]*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}),
   965  								MultiStageExpr{
   966  									newNestedLineFilterExpr(
   967  										newNestedLineFilterExpr(
   968  											newNestedLineFilterExpr(
   969  												newLineFilterExpr(labels.MatchEqual, "", "baz"),
   970  												newLineFilterExpr(labels.MatchRegexp, "", "blip"),
   971  											),
   972  											newLineFilterExpr(labels.MatchNotEqual, "", "flip"),
   973  										),
   974  										newLineFilterExpr(labels.MatchNotRegexp, "", "flap"),
   975  									),
   976  								},
   977  							),
   978  							Interval: 5 * time.Minute,
   979  						}, OpRangeTypeRate, nil, nil),
   980  					"sum",
   981  					&Grouping{
   982  						Without: false,
   983  						Groups:  []string{"app"},
   984  					},
   985  					nil),
   986  				"topk",
   987  				nil,
   988  				NewStringLabelFilter("5")),
   989  		},
   990  		{
   991  			in:  `{foo="bar}`,
   992  			err: logqlmodel.NewParseError("literal not terminated", 1, 6),
   993  		},
   994  		{
   995  			in:  `{foo="bar"`,
   996  			err: logqlmodel.NewParseError("syntax error: unexpected $end, expecting } or ,", 1, 11),
   997  		},
   998  
   999  		{
  1000  			in:  `{foo="bar"} |~`,
  1001  			err: logqlmodel.NewParseError("syntax error: unexpected $end, expecting STRING or ip", 1, 15),
  1002  		},
  1003  
  1004  		{
  1005  			in:  `{foo="bar"} "foo"`,
  1006  			err: logqlmodel.NewParseError("syntax error: unexpected STRING", 1, 13),
  1007  		},
  1008  		{
  1009  			in:  `{foo="bar"} foo`,
  1010  			err: logqlmodel.NewParseError("syntax error: unexpected IDENTIFIER", 1, 13),
  1011  		},
  1012  		{
  1013  			// require left associativity
  1014  			in: `
  1015  			sum(count_over_time({foo="bar"}[5m])) by (foo) /
  1016  			sum(count_over_time({foo="bar"}[5m])) by (foo) /
  1017  			sum(count_over_time({foo="bar"}[5m])) by (foo)
  1018  			`,
  1019  			exp: mustNewBinOpExpr(
  1020  				OpTypeDiv,
  1021  				&BinOpOptions{
  1022  					VectorMatching: &VectorMatching{Card: CardOneToOne},
  1023  				},
  1024  				mustNewBinOpExpr(
  1025  					OpTypeDiv,
  1026  					&BinOpOptions{
  1027  						VectorMatching: &VectorMatching{Card: CardOneToOne},
  1028  					},
  1029  					mustNewVectorAggregationExpr(newRangeAggregationExpr(
  1030  						&LogRange{
  1031  							Left: &MatchersExpr{
  1032  								Mts: []*labels.Matcher{
  1033  									mustNewMatcher(labels.MatchEqual, "foo", "bar"),
  1034  								},
  1035  							},
  1036  							Interval: 5 * time.Minute,
  1037  						}, OpRangeTypeCount, nil, nil),
  1038  						"sum",
  1039  						&Grouping{
  1040  							Without: false,
  1041  							Groups:  []string{"foo"},
  1042  						},
  1043  						nil,
  1044  					),
  1045  					mustNewVectorAggregationExpr(newRangeAggregationExpr(
  1046  						&LogRange{
  1047  							Left: &MatchersExpr{
  1048  								Mts: []*labels.Matcher{
  1049  									mustNewMatcher(labels.MatchEqual, "foo", "bar"),
  1050  								},
  1051  							},
  1052  							Interval: 5 * time.Minute,
  1053  						}, OpRangeTypeCount, nil, nil),
  1054  						"sum",
  1055  						&Grouping{
  1056  							Without: false,
  1057  							Groups:  []string{"foo"},
  1058  						},
  1059  						nil,
  1060  					),
  1061  				),
  1062  				mustNewVectorAggregationExpr(newRangeAggregationExpr(
  1063  					&LogRange{
  1064  						Left: &MatchersExpr{
  1065  							Mts: []*labels.Matcher{
  1066  								mustNewMatcher(labels.MatchEqual, "foo", "bar"),
  1067  							},
  1068  						},
  1069  						Interval: 5 * time.Minute,
  1070  					}, OpRangeTypeCount, nil, nil),
  1071  					"sum",
  1072  					&Grouping{
  1073  						Without: false,
  1074  						Groups:  []string{"foo"},
  1075  					},
  1076  					nil,
  1077  				),
  1078  			),
  1079  		},
  1080  		{
  1081  			in: `
  1082  					sum(count_over_time({foo="bar"}[5m])) by (foo) ^
  1083  					sum(count_over_time({foo="bar"}[5m])) by (foo) /
  1084  					sum(count_over_time({foo="bar"}[5m])) by (foo)
  1085  					`,
  1086  			exp: mustNewBinOpExpr(
  1087  				OpTypeDiv,
  1088  				&BinOpOptions{
  1089  					VectorMatching: &VectorMatching{Card: CardOneToOne},
  1090  				},
  1091  				mustNewBinOpExpr(
  1092  					OpTypePow,
  1093  					&BinOpOptions{
  1094  						VectorMatching: &VectorMatching{Card: CardOneToOne},
  1095  					},
  1096  					mustNewVectorAggregationExpr(newRangeAggregationExpr(
  1097  						&LogRange{
  1098  							Left: &MatchersExpr{
  1099  								Mts: []*labels.Matcher{
  1100  									mustNewMatcher(labels.MatchEqual, "foo", "bar"),
  1101  								},
  1102  							},
  1103  							Interval: 5 * time.Minute,
  1104  						}, OpRangeTypeCount, nil, nil),
  1105  						"sum",
  1106  						&Grouping{
  1107  							Without: false,
  1108  							Groups:  []string{"foo"},
  1109  						},
  1110  						nil,
  1111  					),
  1112  					mustNewVectorAggregationExpr(newRangeAggregationExpr(
  1113  						&LogRange{
  1114  							Left: &MatchersExpr{
  1115  								Mts: []*labels.Matcher{
  1116  									mustNewMatcher(labels.MatchEqual, "foo", "bar"),
  1117  								},
  1118  							},
  1119  							Interval: 5 * time.Minute,
  1120  						}, OpRangeTypeCount, nil, nil),
  1121  						"sum",
  1122  						&Grouping{
  1123  							Without: false,
  1124  							Groups:  []string{"foo"},
  1125  						},
  1126  						nil,
  1127  					),
  1128  				),
  1129  				mustNewVectorAggregationExpr(newRangeAggregationExpr(
  1130  					&LogRange{
  1131  						Left: &MatchersExpr{
  1132  							Mts: []*labels.Matcher{
  1133  								mustNewMatcher(labels.MatchEqual, "foo", "bar"),
  1134  							},
  1135  						},
  1136  						Interval: 5 * time.Minute,
  1137  					}, OpRangeTypeCount, nil, nil),
  1138  					"sum",
  1139  					&Grouping{
  1140  						Without: false,
  1141  						Groups:  []string{"foo"},
  1142  					},
  1143  					nil,
  1144  				),
  1145  			),
  1146  		},
  1147  		{
  1148  			// operator precedence before left associativity
  1149  			in: `
  1150  					sum(count_over_time({foo="bar"}[5m])) by (foo) +
  1151  					sum(count_over_time({foo="bar"}[5m])) by (foo) /
  1152  					sum(count_over_time({foo="bar"}[5m])) by (foo)
  1153  					`,
  1154  			exp: mustNewBinOpExpr(
  1155  				OpTypeAdd,
  1156  				&BinOpOptions{
  1157  					VectorMatching: &VectorMatching{Card: CardOneToOne},
  1158  				},
  1159  				mustNewVectorAggregationExpr(newRangeAggregationExpr(
  1160  					&LogRange{
  1161  						Left: &MatchersExpr{
  1162  							Mts: []*labels.Matcher{
  1163  								mustNewMatcher(labels.MatchEqual, "foo", "bar"),
  1164  							},
  1165  						},
  1166  						Interval: 5 * time.Minute,
  1167  					}, OpRangeTypeCount, nil, nil),
  1168  					"sum",
  1169  					&Grouping{
  1170  						Without: false,
  1171  						Groups:  []string{"foo"},
  1172  					},
  1173  					nil,
  1174  				),
  1175  				mustNewBinOpExpr(
  1176  					OpTypeDiv,
  1177  					&BinOpOptions{
  1178  						VectorMatching: &VectorMatching{Card: CardOneToOne},
  1179  					},
  1180  					mustNewVectorAggregationExpr(newRangeAggregationExpr(
  1181  						&LogRange{
  1182  							Left: &MatchersExpr{
  1183  								Mts: []*labels.Matcher{
  1184  									mustNewMatcher(labels.MatchEqual, "foo", "bar"),
  1185  								},
  1186  							},
  1187  							Interval: 5 * time.Minute,
  1188  						}, OpRangeTypeCount, nil, nil),
  1189  						"sum",
  1190  						&Grouping{
  1191  							Without: false,
  1192  							Groups:  []string{"foo"},
  1193  						},
  1194  						nil,
  1195  					),
  1196  					mustNewVectorAggregationExpr(newRangeAggregationExpr(
  1197  						&LogRange{
  1198  							Left: &MatchersExpr{
  1199  								Mts: []*labels.Matcher{
  1200  									mustNewMatcher(labels.MatchEqual, "foo", "bar"),
  1201  								},
  1202  							},
  1203  							Interval: 5 * time.Minute,
  1204  						}, OpRangeTypeCount, nil, nil),
  1205  						"sum",
  1206  						&Grouping{
  1207  							Without: false,
  1208  							Groups:  []string{"foo"},
  1209  						},
  1210  						nil,
  1211  					),
  1212  				),
  1213  			),
  1214  		},
  1215  		{
  1216  			in: `sum by (job) (
  1217  							count_over_time({namespace="tns"} |= "level=error"[5m])
  1218  						/
  1219  							count_over_time({namespace="tns"}[5m])
  1220  						)`,
  1221  			exp: mustNewVectorAggregationExpr(
  1222  				mustNewBinOpExpr(OpTypeDiv,
  1223  					&BinOpOptions{
  1224  						VectorMatching: &VectorMatching{Card: CardOneToOne},
  1225  					},
  1226  					newRangeAggregationExpr(
  1227  						&LogRange{
  1228  							Left: newPipelineExpr(
  1229  								newMatcherExpr([]*labels.Matcher{
  1230  									mustNewMatcher(labels.MatchEqual, "namespace", "tns"),
  1231  								}),
  1232  								MultiStageExpr{
  1233  									newLineFilterExpr(labels.MatchEqual, "", "level=error"),
  1234  								}),
  1235  							Interval: 5 * time.Minute,
  1236  						}, OpRangeTypeCount, nil, nil),
  1237  					newRangeAggregationExpr(
  1238  						&LogRange{
  1239  							Left: &MatchersExpr{
  1240  								Mts: []*labels.Matcher{
  1241  									mustNewMatcher(labels.MatchEqual, "namespace", "tns"),
  1242  								},
  1243  							},
  1244  							Interval: 5 * time.Minute,
  1245  						}, OpRangeTypeCount, nil, nil)), OpTypeSum, &Grouping{Groups: []string{"job"}}, nil),
  1246  		},
  1247  		{
  1248  			in: `sum by (job) (
  1249  							count_over_time({namespace="tns"} |= "level=error"[5m])
  1250  						/
  1251  							count_over_time({namespace="tns"}[5m])
  1252  						) * 100`,
  1253  			exp: mustNewBinOpExpr(OpTypeMul, &BinOpOptions{
  1254  				VectorMatching: &VectorMatching{Card: CardOneToOne},
  1255  			}, mustNewVectorAggregationExpr(
  1256  				mustNewBinOpExpr(OpTypeDiv,
  1257  					&BinOpOptions{
  1258  						VectorMatching: &VectorMatching{Card: CardOneToOne},
  1259  					},
  1260  					newRangeAggregationExpr(
  1261  						&LogRange{
  1262  							Left: newPipelineExpr(
  1263  								newMatcherExpr([]*labels.Matcher{
  1264  									mustNewMatcher(labels.MatchEqual, "namespace", "tns"),
  1265  								}),
  1266  								MultiStageExpr{
  1267  									newLineFilterExpr(labels.MatchEqual, "", "level=error"),
  1268  								}),
  1269  							Interval: 5 * time.Minute,
  1270  						}, OpRangeTypeCount, nil, nil),
  1271  					newRangeAggregationExpr(
  1272  						&LogRange{
  1273  							Left: &MatchersExpr{
  1274  								Mts: []*labels.Matcher{
  1275  									mustNewMatcher(labels.MatchEqual, "namespace", "tns"),
  1276  								},
  1277  							},
  1278  							Interval: 5 * time.Minute,
  1279  						}, OpRangeTypeCount, nil, nil)), OpTypeSum, &Grouping{Groups: []string{"job"}}, nil),
  1280  				mustNewLiteralExpr("100", false),
  1281  			),
  1282  		},
  1283  		{
  1284  			// reduces binop with two literalExprs
  1285  			in: `sum(count_over_time({foo="bar"}[5m])) by (foo) + 1 / 2`,
  1286  			exp: mustNewBinOpExpr(
  1287  				OpTypeAdd,
  1288  				&BinOpOptions{
  1289  					VectorMatching: &VectorMatching{Card: CardOneToOne},
  1290  				},
  1291  				mustNewVectorAggregationExpr(
  1292  					newRangeAggregationExpr(
  1293  						&LogRange{
  1294  							Left: &MatchersExpr{
  1295  								Mts: []*labels.Matcher{
  1296  									mustNewMatcher(labels.MatchEqual, "foo", "bar"),
  1297  								},
  1298  							},
  1299  							Interval: 5 * time.Minute,
  1300  						}, OpRangeTypeCount, nil, nil),
  1301  					"sum",
  1302  					&Grouping{
  1303  						Without: false,
  1304  						Groups:  []string{"foo"},
  1305  					},
  1306  					nil,
  1307  				),
  1308  				&LiteralExpr{Val: 0.5},
  1309  			),
  1310  		},
  1311  		{
  1312  			// test signs
  1313  			in: `1 + -2 / 1`,
  1314  			exp: mustNewBinOpExpr(
  1315  				OpTypeAdd,
  1316  				&BinOpOptions{
  1317  					VectorMatching: &VectorMatching{Card: CardOneToOne},
  1318  				},
  1319  				&LiteralExpr{Val: 1},
  1320  				mustNewBinOpExpr(OpTypeDiv, &BinOpOptions{
  1321  					VectorMatching: &VectorMatching{Card: CardOneToOne},
  1322  				}, &LiteralExpr{Val: -2}, &LiteralExpr{Val: 1}),
  1323  			),
  1324  		},
  1325  		{
  1326  			// test signs/ops with equal associativity
  1327  			in: `1 + 1 - -1`,
  1328  			exp: mustNewBinOpExpr(
  1329  				OpTypeSub,
  1330  				&BinOpOptions{
  1331  					VectorMatching: &VectorMatching{Card: CardOneToOne},
  1332  				},
  1333  				mustNewBinOpExpr(OpTypeAdd, &BinOpOptions{
  1334  					VectorMatching: &VectorMatching{Card: CardOneToOne},
  1335  				}, &LiteralExpr{Val: 1}, &LiteralExpr{Val: 1}),
  1336  				&LiteralExpr{Val: -1},
  1337  			),
  1338  		},
  1339  		{
  1340  			in: `{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)`,
  1341  			exp: &PipelineExpr{
  1342  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1343  				MultiStages: MultiStageExpr{
  1344  					newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1345  					newLabelParserExpr(OpParserTypeJSON, ""),
  1346  					&LabelFilterExpr{
  1347  						LabelFilterer: log.NewOrLabelFilter(
  1348  							log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1349  							log.NewAndLabelFilter(
  1350  								log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1351  								log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1352  							),
  1353  						),
  1354  					},
  1355  				},
  1356  			},
  1357  		},
  1358  		{
  1359  			in: `{app="foo"} |= "bar" | unpack | json | latency >= 250ms or ( status_code < 500 and status_code > 200)`,
  1360  			exp: &PipelineExpr{
  1361  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1362  				MultiStages: MultiStageExpr{
  1363  					newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1364  					newLabelParserExpr(OpParserTypeUnpack, ""),
  1365  					newLabelParserExpr(OpParserTypeJSON, ""),
  1366  					&LabelFilterExpr{
  1367  						LabelFilterer: log.NewOrLabelFilter(
  1368  							log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1369  							log.NewAndLabelFilter(
  1370  								log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1371  								log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1372  							),
  1373  						),
  1374  					},
  1375  				},
  1376  			},
  1377  		},
  1378  		{
  1379  			in: `{app="foo"} |= "bar" | json | (duration > 1s or status!= 200) and method!="POST"`,
  1380  			exp: &PipelineExpr{
  1381  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1382  				MultiStages: MultiStageExpr{
  1383  					newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1384  					newLabelParserExpr(OpParserTypeJSON, ""),
  1385  					&LabelFilterExpr{
  1386  						LabelFilterer: log.NewAndLabelFilter(
  1387  							log.NewOrLabelFilter(
  1388  								log.NewDurationLabelFilter(log.LabelFilterGreaterThan, "duration", 1*time.Second),
  1389  								log.NewNumericLabelFilter(log.LabelFilterNotEqual, "status", 200.0),
  1390  							),
  1391  							log.NewStringLabelFilter(mustNewMatcher(labels.MatchNotEqual, "method", "POST")),
  1392  						),
  1393  					},
  1394  				},
  1395  			},
  1396  		},
  1397  		{
  1398  			in: `{app="foo"} |= "bar" | pattern "<foo> bar <buzz>" | (duration > 1s or status!= 200) and method!="POST"`,
  1399  			exp: &PipelineExpr{
  1400  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1401  				MultiStages: MultiStageExpr{
  1402  					newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1403  					newLabelParserExpr(OpParserTypePattern, "<foo> bar <buzz>"),
  1404  					&LabelFilterExpr{
  1405  						LabelFilterer: log.NewAndLabelFilter(
  1406  							log.NewOrLabelFilter(
  1407  								log.NewDurationLabelFilter(log.LabelFilterGreaterThan, "duration", 1*time.Second),
  1408  								log.NewNumericLabelFilter(log.LabelFilterNotEqual, "status", 200.0),
  1409  							),
  1410  							log.NewStringLabelFilter(mustNewMatcher(labels.MatchNotEqual, "method", "POST")),
  1411  						),
  1412  					},
  1413  				},
  1414  			},
  1415  		},
  1416  		{
  1417  			in: `{app="foo"} |= "bar" | json | ( status_code < 500 and status_code > 200) or latency >= 250ms `,
  1418  			exp: &PipelineExpr{
  1419  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1420  				MultiStages: MultiStageExpr{
  1421  					newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1422  					newLabelParserExpr(OpParserTypeJSON, ""),
  1423  					&LabelFilterExpr{
  1424  						LabelFilterer: log.NewOrLabelFilter(
  1425  							log.NewAndLabelFilter(
  1426  								log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1427  								log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1428  							),
  1429  							log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1430  						),
  1431  					},
  1432  				},
  1433  			},
  1434  		},
  1435  		{
  1436  			in: `{app="foo"} |= "bar" | json | ( status_code < 500 or status_code > 200) and latency >= 250ms `,
  1437  			exp: &PipelineExpr{
  1438  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1439  				MultiStages: MultiStageExpr{
  1440  					newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1441  					newLabelParserExpr(OpParserTypeJSON, ""),
  1442  					&LabelFilterExpr{
  1443  						LabelFilterer: log.NewAndLabelFilter(
  1444  							log.NewOrLabelFilter(
  1445  								log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1446  								log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1447  							),
  1448  							log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1449  						),
  1450  					},
  1451  				},
  1452  			},
  1453  		},
  1454  		{
  1455  			in: `{app="foo"} |= "bar" | json |  status_code < 500 or status_code > 200 and latency >= 250ms `,
  1456  			exp: &PipelineExpr{
  1457  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1458  				MultiStages: MultiStageExpr{
  1459  					newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1460  					newLabelParserExpr(OpParserTypeJSON, ""),
  1461  					&LabelFilterExpr{
  1462  						LabelFilterer: log.NewOrLabelFilter(
  1463  							log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1464  							log.NewAndLabelFilter(
  1465  								log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1466  								log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1467  							),
  1468  						),
  1469  					},
  1470  				},
  1471  			},
  1472  		},
  1473  		{
  1474  			in: `{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  1475  				| foo="bar" buzz!="blip", blop=~"boop" or fuzz==5`,
  1476  			exp: &PipelineExpr{
  1477  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1478  				MultiStages: MultiStageExpr{
  1479  					newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1480  					newLabelParserExpr(OpParserTypeJSON, ""),
  1481  					&LabelFilterExpr{
  1482  						LabelFilterer: log.NewOrLabelFilter(
  1483  							log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1484  							log.NewAndLabelFilter(
  1485  								log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1486  								log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1487  							),
  1488  						),
  1489  					},
  1490  					&LabelFilterExpr{
  1491  						LabelFilterer: log.NewAndLabelFilter(
  1492  							log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "foo", "bar")),
  1493  							log.NewAndLabelFilter(
  1494  								log.NewStringLabelFilter(mustNewMatcher(labels.MatchNotEqual, "buzz", "blip")),
  1495  								log.NewOrLabelFilter(
  1496  									log.NewStringLabelFilter(mustNewMatcher(labels.MatchRegexp, "blop", "boop")),
  1497  									log.NewNumericLabelFilter(log.LabelFilterEqual, "fuzz", 5),
  1498  								),
  1499  							),
  1500  						),
  1501  					},
  1502  				},
  1503  			},
  1504  		},
  1505  		{
  1506  			in: `{app="foo"} |= "bar" | line_format "blip{{ .foo }}blop"`,
  1507  			exp: &PipelineExpr{
  1508  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1509  				MultiStages: MultiStageExpr{
  1510  					newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1511  					newLineFmtExpr("blip{{ .foo }}blop"),
  1512  				},
  1513  			},
  1514  		},
  1515  		{
  1516  			in: `{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  1517  			| line_format "blip{{ .foo }}blop {{.status_code}}"`,
  1518  			exp: &PipelineExpr{
  1519  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1520  				MultiStages: MultiStageExpr{
  1521  					newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1522  					newLabelParserExpr(OpParserTypeJSON, ""),
  1523  					&LabelFilterExpr{
  1524  						LabelFilterer: log.NewOrLabelFilter(
  1525  							log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1526  							log.NewAndLabelFilter(
  1527  								log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1528  								log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1529  							),
  1530  						),
  1531  					},
  1532  					newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  1533  				},
  1534  			},
  1535  		},
  1536  		{
  1537  			in: `{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  1538  			| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}"`,
  1539  			exp: &PipelineExpr{
  1540  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1541  				MultiStages: MultiStageExpr{
  1542  					newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1543  					newLabelParserExpr(OpParserTypeJSON, ""),
  1544  					&LabelFilterExpr{
  1545  						LabelFilterer: log.NewOrLabelFilter(
  1546  							log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1547  							log.NewAndLabelFilter(
  1548  								log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1549  								log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1550  							),
  1551  						),
  1552  					},
  1553  					newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  1554  					newLabelFmtExpr([]log.LabelFmt{
  1555  						log.NewRenameLabelFmt("foo", "bar"),
  1556  						log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  1557  					}),
  1558  				},
  1559  			},
  1560  		},
  1561  		{
  1562  			in: `count_over_time({app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  1563  			| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}"[5m])`,
  1564  			exp: newRangeAggregationExpr(
  1565  				newLogRange(&PipelineExpr{
  1566  					Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1567  					MultiStages: MultiStageExpr{
  1568  						newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1569  						newLabelParserExpr(OpParserTypeJSON, ""),
  1570  						&LabelFilterExpr{
  1571  							LabelFilterer: log.NewOrLabelFilter(
  1572  								log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1573  								log.NewAndLabelFilter(
  1574  									log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1575  									log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1576  								),
  1577  							),
  1578  						},
  1579  						newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  1580  						newLabelFmtExpr([]log.LabelFmt{
  1581  							log.NewRenameLabelFmt("foo", "bar"),
  1582  							log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  1583  						}),
  1584  					},
  1585  				},
  1586  					5*time.Minute,
  1587  					nil, nil),
  1588  				OpRangeTypeCount,
  1589  				nil,
  1590  				nil,
  1591  			),
  1592  		},
  1593  		{
  1594  			in:  "{app=~\"\xa0\xa1\"}",
  1595  			exp: nil,
  1596  			err: logqlmodel.NewParseError("invalid UTF-8 encoding", 1, 7),
  1597  		},
  1598  		{
  1599  			in: `sum_over_time({app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  1600  			| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}"[5m])`,
  1601  			exp: nil,
  1602  			err: logqlmodel.NewParseError("invalid aggregation sum_over_time without unwrap", 0, 0),
  1603  		},
  1604  		{
  1605  			in:  `count_over_time({app="foo"} |= "foo" | json | unwrap foo [5m])`,
  1606  			exp: nil,
  1607  			err: logqlmodel.NewParseError("invalid aggregation count_over_time with unwrap", 0, 0),
  1608  		},
  1609  		{
  1610  			in: `{app="foo"} |= "bar" | json |  status_code < 500 or status_code > 200 and size >= 2.5KiB `,
  1611  			exp: &PipelineExpr{
  1612  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1613  				MultiStages: MultiStageExpr{
  1614  					newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1615  					newLabelParserExpr(OpParserTypeJSON, ""),
  1616  					&LabelFilterExpr{
  1617  						LabelFilterer: log.NewOrLabelFilter(
  1618  							log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1619  							log.NewAndLabelFilter(
  1620  								log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1621  								log.NewBytesLabelFilter(log.LabelFilterGreaterThanOrEqual, "size", 2560),
  1622  							),
  1623  						),
  1624  					},
  1625  				},
  1626  			},
  1627  		},
  1628  		{
  1629  			in: `stdvar_over_time({app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  1630  			| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m])`,
  1631  			exp: newRangeAggregationExpr(
  1632  				newLogRange(&PipelineExpr{
  1633  					Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1634  					MultiStages: MultiStageExpr{
  1635  						newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1636  						newLabelParserExpr(OpParserTypeJSON, ""),
  1637  						&LabelFilterExpr{
  1638  							LabelFilterer: log.NewOrLabelFilter(
  1639  								log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1640  								log.NewAndLabelFilter(
  1641  									log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1642  									log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1643  								),
  1644  							),
  1645  						},
  1646  						newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  1647  						newLabelFmtExpr([]log.LabelFmt{
  1648  							log.NewRenameLabelFmt("foo", "bar"),
  1649  							log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  1650  						}),
  1651  					},
  1652  				},
  1653  					5*time.Minute,
  1654  					newUnwrapExpr("foo", ""),
  1655  					nil),
  1656  				OpRangeTypeStdvar, nil, nil,
  1657  			),
  1658  		},
  1659  		{
  1660  			in: `stdvar_over_time({app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  1661  			| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap duration(foo) [5m])`,
  1662  			exp: newRangeAggregationExpr(
  1663  				newLogRange(&PipelineExpr{
  1664  					Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1665  					MultiStages: MultiStageExpr{
  1666  						newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1667  						newLabelParserExpr(OpParserTypeJSON, ""),
  1668  						&LabelFilterExpr{
  1669  							LabelFilterer: log.NewOrLabelFilter(
  1670  								log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1671  								log.NewAndLabelFilter(
  1672  									log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1673  									log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1674  								),
  1675  							),
  1676  						},
  1677  						newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  1678  						newLabelFmtExpr([]log.LabelFmt{
  1679  							log.NewRenameLabelFmt("foo", "bar"),
  1680  							log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  1681  						}),
  1682  					},
  1683  				},
  1684  					5*time.Minute,
  1685  					newUnwrapExpr("foo", OpConvDuration),
  1686  					nil),
  1687  				OpRangeTypeStdvar, nil, nil,
  1688  			),
  1689  		},
  1690  		{
  1691  			in: `sum_over_time({namespace="tns"} |= "level=error" | json |foo>=5,bar<25ms| unwrap bytes(foo) [5m])`,
  1692  			exp: newRangeAggregationExpr(
  1693  				newLogRange(&PipelineExpr{
  1694  					Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "namespace", Value: "tns"}}),
  1695  					MultiStages: MultiStageExpr{
  1696  						newLineFilterExpr(labels.MatchEqual, "", "level=error"),
  1697  						newLabelParserExpr(OpParserTypeJSON, ""),
  1698  						&LabelFilterExpr{
  1699  							LabelFilterer: log.NewAndLabelFilter(
  1700  								log.NewNumericLabelFilter(log.LabelFilterGreaterThanOrEqual, "foo", 5),
  1701  								log.NewDurationLabelFilter(log.LabelFilterLesserThan, "bar", 25*time.Millisecond),
  1702  							),
  1703  						},
  1704  					},
  1705  				},
  1706  					5*time.Minute,
  1707  					newUnwrapExpr("foo", OpConvBytes),
  1708  					nil),
  1709  				OpRangeTypeSum, nil, nil,
  1710  			),
  1711  		},
  1712  		{
  1713  			in: `sum_over_time({namespace="tns"} |= "level=error" | json |foo>=5,bar<25ms| unwrap bytes(foo) [5m] offset 5m)`,
  1714  			exp: newRangeAggregationExpr(
  1715  				newLogRange(&PipelineExpr{
  1716  					Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "namespace", Value: "tns"}}),
  1717  					MultiStages: MultiStageExpr{
  1718  						newLineFilterExpr(labels.MatchEqual, "", "level=error"),
  1719  						newLabelParserExpr(OpParserTypeJSON, ""),
  1720  						&LabelFilterExpr{
  1721  							LabelFilterer: log.NewAndLabelFilter(
  1722  								log.NewNumericLabelFilter(log.LabelFilterGreaterThanOrEqual, "foo", 5),
  1723  								log.NewDurationLabelFilter(log.LabelFilterLesserThan, "bar", 25*time.Millisecond),
  1724  							),
  1725  						},
  1726  					},
  1727  				},
  1728  					5*time.Minute,
  1729  					newUnwrapExpr("foo", OpConvBytes),
  1730  					newOffsetExpr(5*time.Minute)),
  1731  				OpRangeTypeSum, nil, nil,
  1732  			),
  1733  		},
  1734  		{
  1735  			in: `sum_over_time({namespace="tns"} |= "level=error" | json |foo>=5,bar<25ms| unwrap latency [5m])`,
  1736  			exp: newRangeAggregationExpr(
  1737  				newLogRange(&PipelineExpr{
  1738  					Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "namespace", Value: "tns"}}),
  1739  					MultiStages: MultiStageExpr{
  1740  						newLineFilterExpr(labels.MatchEqual, "", "level=error"),
  1741  						newLabelParserExpr(OpParserTypeJSON, ""),
  1742  						&LabelFilterExpr{
  1743  							LabelFilterer: log.NewAndLabelFilter(
  1744  								log.NewNumericLabelFilter(log.LabelFilterGreaterThanOrEqual, "foo", 5),
  1745  								log.NewDurationLabelFilter(log.LabelFilterLesserThan, "bar", 25*time.Millisecond),
  1746  							),
  1747  						},
  1748  					},
  1749  				},
  1750  					5*time.Minute,
  1751  					newUnwrapExpr("latency", ""),
  1752  					nil),
  1753  				OpRangeTypeSum, nil, nil,
  1754  			),
  1755  		},
  1756  		{
  1757  			in: `sum_over_time({namespace="tns"} |= "level=error" | json |foo==5,bar<25ms| unwrap latency [5m])`,
  1758  			exp: newRangeAggregationExpr(
  1759  				newLogRange(&PipelineExpr{
  1760  					Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "namespace", Value: "tns"}}),
  1761  					MultiStages: MultiStageExpr{
  1762  						newLineFilterExpr(labels.MatchEqual, "", "level=error"),
  1763  						newLabelParserExpr(OpParserTypeJSON, ""),
  1764  						&LabelFilterExpr{
  1765  							LabelFilterer: log.NewAndLabelFilter(
  1766  								log.NewNumericLabelFilter(log.LabelFilterEqual, "foo", 5),
  1767  								log.NewDurationLabelFilter(log.LabelFilterLesserThan, "bar", 25*time.Millisecond),
  1768  							),
  1769  						},
  1770  					},
  1771  				},
  1772  					5*time.Minute,
  1773  					newUnwrapExpr("latency", ""),
  1774  					nil),
  1775  				OpRangeTypeSum, nil, nil,
  1776  			),
  1777  		},
  1778  		{
  1779  			in: `stddev_over_time({app="foo"} |= "bar" | unwrap bar [5m])`,
  1780  			exp: newRangeAggregationExpr(
  1781  				newLogRange(&PipelineExpr{
  1782  					Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1783  					MultiStages: MultiStageExpr{
  1784  						newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1785  					},
  1786  				},
  1787  					5*time.Minute,
  1788  					newUnwrapExpr("bar", ""),
  1789  					nil),
  1790  				OpRangeTypeStddev, nil, nil,
  1791  			),
  1792  		},
  1793  		{
  1794  			in: `min_over_time({app="foo"} | unwrap bar [5m])`,
  1795  			exp: newRangeAggregationExpr(
  1796  				newLogRange(
  1797  					newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1798  					5*time.Minute,
  1799  					newUnwrapExpr("bar", ""),
  1800  					nil),
  1801  				OpRangeTypeMin, nil, nil,
  1802  			),
  1803  		},
  1804  		{
  1805  			in: `min_over_time({app="foo"} | unwrap bar [5m]) by ()`,
  1806  			exp: newRangeAggregationExpr(
  1807  				newLogRange(
  1808  					newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1809  					5*time.Minute,
  1810  					newUnwrapExpr("bar", ""),
  1811  					nil),
  1812  				OpRangeTypeMin, &Grouping{}, nil,
  1813  			),
  1814  		},
  1815  		{
  1816  			in: `max_over_time({app="foo"} | unwrap bar [5m]) without ()`,
  1817  			exp: newRangeAggregationExpr(
  1818  				newLogRange(
  1819  					newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1820  					5*time.Minute,
  1821  					newUnwrapExpr("bar", ""),
  1822  					nil),
  1823  				OpRangeTypeMax, &Grouping{Without: true}, nil,
  1824  			),
  1825  		},
  1826  		{
  1827  			in: `max_over_time({app="foo"} | unwrap bar [5m]) without (foo,bar)`,
  1828  			exp: newRangeAggregationExpr(
  1829  				newLogRange(
  1830  					newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1831  					5*time.Minute,
  1832  					newUnwrapExpr("bar", ""),
  1833  					nil),
  1834  				OpRangeTypeMax, &Grouping{Without: true, Groups: []string{"foo", "bar"}}, nil,
  1835  			),
  1836  		},
  1837  		{
  1838  			in: `max_over_time({app="foo"} | unwrap bar [5m] offset 5m) without (foo,bar)`,
  1839  			exp: newRangeAggregationExpr(
  1840  				newLogRange(
  1841  					newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1842  					5*time.Minute,
  1843  					newUnwrapExpr("bar", ""),
  1844  					newOffsetExpr(5*time.Minute)),
  1845  				OpRangeTypeMax, &Grouping{Without: true, Groups: []string{"foo", "bar"}}, nil,
  1846  			),
  1847  		},
  1848  		{
  1849  			in: `max_over_time(({app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  1850  			| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo )[5m])`,
  1851  			exp: newRangeAggregationExpr(
  1852  				newLogRange(&PipelineExpr{
  1853  					Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1854  					MultiStages: MultiStageExpr{
  1855  						newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1856  						newLabelParserExpr(OpParserTypeJSON, ""),
  1857  						&LabelFilterExpr{
  1858  							LabelFilterer: log.NewOrLabelFilter(
  1859  								log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1860  								log.NewAndLabelFilter(
  1861  									log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1862  									log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1863  								),
  1864  							),
  1865  						},
  1866  						newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  1867  						newLabelFmtExpr([]log.LabelFmt{
  1868  							log.NewRenameLabelFmt("foo", "bar"),
  1869  							log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  1870  						}),
  1871  					},
  1872  				},
  1873  					5*time.Minute,
  1874  					newUnwrapExpr("foo", ""),
  1875  					nil),
  1876  				OpRangeTypeMax, nil, nil,
  1877  			),
  1878  		},
  1879  		{
  1880  			in: `quantile_over_time(0.99998,{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  1881  			| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m])`,
  1882  			exp: newRangeAggregationExpr(
  1883  				newLogRange(&PipelineExpr{
  1884  					Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1885  					MultiStages: MultiStageExpr{
  1886  						newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1887  						newLabelParserExpr(OpParserTypeJSON, ""),
  1888  						&LabelFilterExpr{
  1889  							LabelFilterer: log.NewOrLabelFilter(
  1890  								log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1891  								log.NewAndLabelFilter(
  1892  									log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1893  									log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1894  								),
  1895  							),
  1896  						},
  1897  						newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  1898  						newLabelFmtExpr([]log.LabelFmt{
  1899  							log.NewRenameLabelFmt("foo", "bar"),
  1900  							log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  1901  						}),
  1902  					},
  1903  				},
  1904  					5*time.Minute,
  1905  					newUnwrapExpr("foo", ""),
  1906  					nil),
  1907  				OpRangeTypeQuantile, nil, NewStringLabelFilter("0.99998"),
  1908  			),
  1909  		},
  1910  		{
  1911  			in: `quantile_over_time(0.99998,{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  1912  			| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m]) by (namespace,instance)`,
  1913  			exp: newRangeAggregationExpr(
  1914  				newLogRange(&PipelineExpr{
  1915  					Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1916  					MultiStages: MultiStageExpr{
  1917  						newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1918  						newLabelParserExpr(OpParserTypeJSON, ""),
  1919  						&LabelFilterExpr{
  1920  							LabelFilterer: log.NewOrLabelFilter(
  1921  								log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1922  								log.NewAndLabelFilter(
  1923  									log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1924  									log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1925  								),
  1926  							),
  1927  						},
  1928  						newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  1929  						newLabelFmtExpr([]log.LabelFmt{
  1930  							log.NewRenameLabelFmt("foo", "bar"),
  1931  							log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  1932  						}),
  1933  					},
  1934  				},
  1935  					5*time.Minute,
  1936  					newUnwrapExpr("foo", ""),
  1937  					nil),
  1938  				OpRangeTypeQuantile, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, NewStringLabelFilter("0.99998"),
  1939  			),
  1940  		},
  1941  		{
  1942  			in: `quantile_over_time(0.99998,{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  1943  			| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo | __error__ !~".+"[5m]) by (namespace,instance)`,
  1944  			exp: newRangeAggregationExpr(
  1945  				newLogRange(&PipelineExpr{
  1946  					Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1947  					MultiStages: MultiStageExpr{
  1948  						newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1949  						newLabelParserExpr(OpParserTypeJSON, ""),
  1950  						&LabelFilterExpr{
  1951  							LabelFilterer: log.NewOrLabelFilter(
  1952  								log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1953  								log.NewAndLabelFilter(
  1954  									log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1955  									log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1956  								),
  1957  							),
  1958  						},
  1959  						newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  1960  						newLabelFmtExpr([]log.LabelFmt{
  1961  							log.NewRenameLabelFmt("foo", "bar"),
  1962  							log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  1963  						}),
  1964  					},
  1965  				},
  1966  					5*time.Minute,
  1967  					newUnwrapExpr("foo", "").addPostFilter(log.NewStringLabelFilter(mustNewMatcher(labels.MatchNotRegexp, logqlmodel.ErrorLabel, ".+"))),
  1968  					nil),
  1969  				OpRangeTypeQuantile, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, NewStringLabelFilter("0.99998"),
  1970  			),
  1971  		},
  1972  		{
  1973  			in: `sum without (foo) (
  1974  				quantile_over_time(0.99998,{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  1975  					| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m]
  1976  								) by (namespace,instance)
  1977  					)`,
  1978  			exp: mustNewVectorAggregationExpr(
  1979  				newRangeAggregationExpr(
  1980  					newLogRange(&PipelineExpr{
  1981  						Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  1982  						MultiStages: MultiStageExpr{
  1983  							newLineFilterExpr(labels.MatchEqual, "", "bar"),
  1984  							newLabelParserExpr(OpParserTypeJSON, ""),
  1985  							&LabelFilterExpr{
  1986  								LabelFilterer: log.NewOrLabelFilter(
  1987  									log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  1988  									log.NewAndLabelFilter(
  1989  										log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  1990  										log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  1991  									),
  1992  								),
  1993  							},
  1994  							newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  1995  							newLabelFmtExpr([]log.LabelFmt{
  1996  								log.NewRenameLabelFmt("foo", "bar"),
  1997  								log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  1998  							}),
  1999  						},
  2000  					},
  2001  						5*time.Minute,
  2002  						newUnwrapExpr("foo", ""),
  2003  						nil),
  2004  					OpRangeTypeQuantile, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, NewStringLabelFilter("0.99998"),
  2005  				),
  2006  				OpTypeSum,
  2007  				&Grouping{Without: true, Groups: []string{"foo"}},
  2008  				nil,
  2009  			),
  2010  		},
  2011  		{
  2012  			in: `sum without (foo) (
  2013  				quantile_over_time(0.99998,{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  2014  					| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m] offset 5m
  2015  								) by (namespace,instance)
  2016  					)`,
  2017  			exp: mustNewVectorAggregationExpr(
  2018  				newRangeAggregationExpr(
  2019  					newLogRange(&PipelineExpr{
  2020  						Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2021  						MultiStages: MultiStageExpr{
  2022  							newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2023  							newLabelParserExpr(OpParserTypeJSON, ""),
  2024  							&LabelFilterExpr{
  2025  								LabelFilterer: log.NewOrLabelFilter(
  2026  									log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  2027  									log.NewAndLabelFilter(
  2028  										log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  2029  										log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  2030  									),
  2031  								),
  2032  							},
  2033  							newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  2034  							newLabelFmtExpr([]log.LabelFmt{
  2035  								log.NewRenameLabelFmt("foo", "bar"),
  2036  								log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  2037  							}),
  2038  						},
  2039  					},
  2040  						5*time.Minute,
  2041  						newUnwrapExpr("foo", ""),
  2042  						newOffsetExpr(5*time.Minute)),
  2043  					OpRangeTypeQuantile, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, NewStringLabelFilter("0.99998"),
  2044  				),
  2045  				OpTypeSum,
  2046  				&Grouping{Without: true, Groups: []string{"foo"}},
  2047  				nil,
  2048  			),
  2049  		},
  2050  		{
  2051  			in: `sum without (foo) (
  2052  			quantile_over_time(0.99998,{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  2053  				| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap duration(foo) [5m]
  2054  							) by (namespace,instance)
  2055  				)`,
  2056  			exp: mustNewVectorAggregationExpr(
  2057  				newRangeAggregationExpr(
  2058  					newLogRange(&PipelineExpr{
  2059  						Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2060  						MultiStages: MultiStageExpr{
  2061  							newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2062  							newLabelParserExpr(OpParserTypeJSON, ""),
  2063  							&LabelFilterExpr{
  2064  								LabelFilterer: log.NewOrLabelFilter(
  2065  									log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  2066  									log.NewAndLabelFilter(
  2067  										log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  2068  										log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  2069  									),
  2070  								),
  2071  							},
  2072  							newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  2073  							newLabelFmtExpr([]log.LabelFmt{
  2074  								log.NewRenameLabelFmt("foo", "bar"),
  2075  								log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  2076  							}),
  2077  						},
  2078  					},
  2079  						5*time.Minute,
  2080  						newUnwrapExpr("foo", OpConvDuration),
  2081  						nil),
  2082  					OpRangeTypeQuantile, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, NewStringLabelFilter("0.99998"),
  2083  				),
  2084  				OpTypeSum,
  2085  				&Grouping{Without: true, Groups: []string{"foo"}},
  2086  				nil,
  2087  			),
  2088  		},
  2089  		{
  2090  			in: `sum without (foo) (
  2091  			quantile_over_time(.99998,{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  2092  				| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap duration(foo) [5m]
  2093  							) by (namespace,instance)
  2094  				)`,
  2095  			exp: mustNewVectorAggregationExpr(
  2096  				newRangeAggregationExpr(
  2097  					newLogRange(&PipelineExpr{
  2098  						Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2099  						MultiStages: MultiStageExpr{
  2100  							newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2101  							newLabelParserExpr(OpParserTypeJSON, ""),
  2102  							&LabelFilterExpr{
  2103  								LabelFilterer: log.NewOrLabelFilter(
  2104  									log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  2105  									log.NewAndLabelFilter(
  2106  										log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  2107  										log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  2108  									),
  2109  								),
  2110  							},
  2111  							newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  2112  							newLabelFmtExpr([]log.LabelFmt{
  2113  								log.NewRenameLabelFmt("foo", "bar"),
  2114  								log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  2115  							}),
  2116  						},
  2117  					},
  2118  						5*time.Minute,
  2119  						newUnwrapExpr("foo", OpConvDuration),
  2120  						nil),
  2121  					OpRangeTypeQuantile, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, NewStringLabelFilter(".99998"),
  2122  				),
  2123  				OpTypeSum,
  2124  				&Grouping{Without: true, Groups: []string{"foo"}},
  2125  				nil,
  2126  			),
  2127  		},
  2128  		{
  2129  			in: `sum without (foo) (
  2130  			quantile_over_time(.99998,{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  2131  				| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap duration_seconds(foo) [5m]
  2132  							) by (namespace,instance)
  2133  				)`,
  2134  			exp: mustNewVectorAggregationExpr(
  2135  				newRangeAggregationExpr(
  2136  					newLogRange(&PipelineExpr{
  2137  						Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2138  						MultiStages: MultiStageExpr{
  2139  							newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2140  							newLabelParserExpr(OpParserTypeJSON, ""),
  2141  							&LabelFilterExpr{
  2142  								LabelFilterer: log.NewOrLabelFilter(
  2143  									log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  2144  									log.NewAndLabelFilter(
  2145  										log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  2146  										log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  2147  									),
  2148  								),
  2149  							},
  2150  							newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  2151  							newLabelFmtExpr([]log.LabelFmt{
  2152  								log.NewRenameLabelFmt("foo", "bar"),
  2153  								log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  2154  							}),
  2155  						},
  2156  					},
  2157  						5*time.Minute,
  2158  						newUnwrapExpr("foo", OpConvDurationSeconds),
  2159  						nil),
  2160  					OpRangeTypeQuantile, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, NewStringLabelFilter(".99998"),
  2161  				),
  2162  				OpTypeSum,
  2163  				&Grouping{Without: true, Groups: []string{"foo"}},
  2164  				nil,
  2165  			),
  2166  		},
  2167  		{
  2168  			in: `topk(10,
  2169  				quantile_over_time(0.99998,{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  2170  					| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m]
  2171  								) by (namespace,instance)
  2172  					)`,
  2173  			exp: mustNewVectorAggregationExpr(
  2174  				newRangeAggregationExpr(
  2175  					newLogRange(&PipelineExpr{
  2176  						Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2177  						MultiStages: MultiStageExpr{
  2178  							newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2179  							newLabelParserExpr(OpParserTypeJSON, ""),
  2180  							&LabelFilterExpr{
  2181  								LabelFilterer: log.NewOrLabelFilter(
  2182  									log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  2183  									log.NewAndLabelFilter(
  2184  										log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  2185  										log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  2186  									),
  2187  								),
  2188  							},
  2189  							newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  2190  							newLabelFmtExpr([]log.LabelFmt{
  2191  								log.NewRenameLabelFmt("foo", "bar"),
  2192  								log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  2193  							}),
  2194  						},
  2195  					},
  2196  						5*time.Minute,
  2197  						newUnwrapExpr("foo", ""),
  2198  						nil),
  2199  					OpRangeTypeQuantile, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, NewStringLabelFilter("0.99998"),
  2200  				),
  2201  				OpTypeTopK,
  2202  				nil,
  2203  				NewStringLabelFilter("10"),
  2204  			),
  2205  		},
  2206  		{
  2207  			in: `
  2208  			sum by (foo,bar) (
  2209  				quantile_over_time(0.99998,{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  2210  					| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m]
  2211  								) by (namespace,instance)
  2212  					)
  2213  					+
  2214  					avg(
  2215  						avg_over_time({app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  2216  							| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m]
  2217  										) by (namespace,instance)
  2218  							) by (foo,bar)
  2219  					`,
  2220  			exp: mustNewBinOpExpr(OpTypeAdd, &BinOpOptions{
  2221  				VectorMatching: &VectorMatching{Card: CardOneToOne}, ReturnBool: false,
  2222  			},
  2223  				mustNewVectorAggregationExpr(
  2224  					newRangeAggregationExpr(
  2225  						newLogRange(&PipelineExpr{
  2226  							Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2227  							MultiStages: MultiStageExpr{
  2228  								newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2229  								newLabelParserExpr(OpParserTypeJSON, ""),
  2230  								&LabelFilterExpr{
  2231  									LabelFilterer: log.NewOrLabelFilter(
  2232  										log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  2233  										log.NewAndLabelFilter(
  2234  											log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  2235  											log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  2236  										),
  2237  									),
  2238  								},
  2239  								newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  2240  								newLabelFmtExpr([]log.LabelFmt{
  2241  									log.NewRenameLabelFmt("foo", "bar"),
  2242  									log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  2243  								}),
  2244  							},
  2245  						},
  2246  							5*time.Minute,
  2247  							newUnwrapExpr("foo", ""),
  2248  							nil),
  2249  						OpRangeTypeQuantile, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, NewStringLabelFilter("0.99998"),
  2250  					),
  2251  					OpTypeSum,
  2252  					&Grouping{Groups: []string{"foo", "bar"}},
  2253  					nil,
  2254  				),
  2255  				mustNewVectorAggregationExpr(
  2256  					newRangeAggregationExpr(
  2257  						newLogRange(&PipelineExpr{
  2258  							Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2259  							MultiStages: MultiStageExpr{
  2260  								newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2261  								newLabelParserExpr(OpParserTypeJSON, ""),
  2262  								&LabelFilterExpr{
  2263  									LabelFilterer: log.NewOrLabelFilter(
  2264  										log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  2265  										log.NewAndLabelFilter(
  2266  											log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  2267  											log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  2268  										),
  2269  									),
  2270  								},
  2271  								newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  2272  								newLabelFmtExpr([]log.LabelFmt{
  2273  									log.NewRenameLabelFmt("foo", "bar"),
  2274  									log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  2275  								}),
  2276  							},
  2277  						},
  2278  							5*time.Minute,
  2279  							newUnwrapExpr("foo", ""),
  2280  							nil),
  2281  						OpRangeTypeAvg, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, nil,
  2282  					),
  2283  					OpTypeAvg,
  2284  					&Grouping{Groups: []string{"foo", "bar"}},
  2285  					nil,
  2286  				),
  2287  			),
  2288  		},
  2289  		{
  2290  			in: `
  2291  			sum by (foo,bar) (
  2292  				quantile_over_time(0.99998,{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  2293  					| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m]
  2294  								) by (namespace,instance)
  2295  					)
  2296  					+ ignoring (bar)
  2297  					avg(
  2298  						avg_over_time({app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  2299  							| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m]
  2300  										) by (namespace,instance)
  2301  							) by (foo)
  2302  					`,
  2303  			exp: mustNewBinOpExpr(OpTypeAdd, &BinOpOptions{ReturnBool: false, VectorMatching: &VectorMatching{Card: CardOneToOne, On: false, MatchingLabels: []string{"bar"}}},
  2304  				mustNewVectorAggregationExpr(
  2305  					newRangeAggregationExpr(
  2306  						newLogRange(&PipelineExpr{
  2307  							Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2308  							MultiStages: MultiStageExpr{
  2309  								newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2310  								newLabelParserExpr(OpParserTypeJSON, ""),
  2311  								&LabelFilterExpr{
  2312  									LabelFilterer: log.NewOrLabelFilter(
  2313  										log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  2314  										log.NewAndLabelFilter(
  2315  											log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  2316  											log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  2317  										),
  2318  									),
  2319  								},
  2320  								newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  2321  								newLabelFmtExpr([]log.LabelFmt{
  2322  									log.NewRenameLabelFmt("foo", "bar"),
  2323  									log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  2324  								}),
  2325  							},
  2326  						},
  2327  							5*time.Minute,
  2328  							newUnwrapExpr("foo", ""),
  2329  							nil),
  2330  						OpRangeTypeQuantile, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, NewStringLabelFilter("0.99998"),
  2331  					),
  2332  					OpTypeSum,
  2333  					&Grouping{Groups: []string{"foo", "bar"}},
  2334  					nil,
  2335  				),
  2336  				mustNewVectorAggregationExpr(
  2337  					newRangeAggregationExpr(
  2338  						newLogRange(&PipelineExpr{
  2339  							Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2340  							MultiStages: MultiStageExpr{
  2341  								newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2342  								newLabelParserExpr(OpParserTypeJSON, ""),
  2343  								&LabelFilterExpr{
  2344  									LabelFilterer: log.NewOrLabelFilter(
  2345  										log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  2346  										log.NewAndLabelFilter(
  2347  											log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  2348  											log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  2349  										),
  2350  									),
  2351  								},
  2352  								newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  2353  								newLabelFmtExpr([]log.LabelFmt{
  2354  									log.NewRenameLabelFmt("foo", "bar"),
  2355  									log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  2356  								}),
  2357  							},
  2358  						},
  2359  							5*time.Minute,
  2360  							newUnwrapExpr("foo", ""),
  2361  							nil),
  2362  						OpRangeTypeAvg, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, nil,
  2363  					),
  2364  					OpTypeAvg,
  2365  					&Grouping{Groups: []string{"foo"}},
  2366  					nil,
  2367  				),
  2368  			),
  2369  		},
  2370  		{
  2371  			in: `
  2372  			sum by (foo,bar) (
  2373  				quantile_over_time(0.99998,{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  2374  					| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m]
  2375  								) by (namespace,instance)
  2376  					)
  2377  					+ on (foo)
  2378  					avg(
  2379  						avg_over_time({app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  2380  							| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m]
  2381  										) by (namespace,instance)
  2382  							) by (foo)
  2383  					`,
  2384  			exp: mustNewBinOpExpr(OpTypeAdd, &BinOpOptions{ReturnBool: false, VectorMatching: &VectorMatching{Card: CardOneToOne, On: true, MatchingLabels: []string{"foo"}}},
  2385  				mustNewVectorAggregationExpr(
  2386  					newRangeAggregationExpr(
  2387  						newLogRange(&PipelineExpr{
  2388  							Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2389  							MultiStages: MultiStageExpr{
  2390  								newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2391  								newLabelParserExpr(OpParserTypeJSON, ""),
  2392  								&LabelFilterExpr{
  2393  									LabelFilterer: log.NewOrLabelFilter(
  2394  										log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  2395  										log.NewAndLabelFilter(
  2396  											log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  2397  											log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  2398  										),
  2399  									),
  2400  								},
  2401  								newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  2402  								newLabelFmtExpr([]log.LabelFmt{
  2403  									log.NewRenameLabelFmt("foo", "bar"),
  2404  									log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  2405  								}),
  2406  							},
  2407  						},
  2408  							5*time.Minute,
  2409  							newUnwrapExpr("foo", ""),
  2410  							nil),
  2411  						OpRangeTypeQuantile, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, NewStringLabelFilter("0.99998"),
  2412  					),
  2413  					OpTypeSum,
  2414  					&Grouping{Groups: []string{"foo", "bar"}},
  2415  					nil,
  2416  				),
  2417  				mustNewVectorAggregationExpr(
  2418  					newRangeAggregationExpr(
  2419  						newLogRange(&PipelineExpr{
  2420  							Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2421  							MultiStages: MultiStageExpr{
  2422  								newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2423  								newLabelParserExpr(OpParserTypeJSON, ""),
  2424  								&LabelFilterExpr{
  2425  									LabelFilterer: log.NewOrLabelFilter(
  2426  										log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  2427  										log.NewAndLabelFilter(
  2428  											log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  2429  											log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  2430  										),
  2431  									),
  2432  								},
  2433  								newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  2434  								newLabelFmtExpr([]log.LabelFmt{
  2435  									log.NewRenameLabelFmt("foo", "bar"),
  2436  									log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  2437  								}),
  2438  							},
  2439  						},
  2440  							5*time.Minute,
  2441  							newUnwrapExpr("foo", ""),
  2442  							nil),
  2443  						OpRangeTypeAvg, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, nil,
  2444  					),
  2445  					OpTypeAvg,
  2446  					&Grouping{Groups: []string{"foo"}},
  2447  					nil,
  2448  				),
  2449  			),
  2450  		},
  2451  		{
  2452  			in: `
  2453  			sum by (foo,bar) (
  2454  				quantile_over_time(0.99998,{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  2455  					| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m]
  2456  								) by (namespace,instance)
  2457  					)
  2458  					+ ignoring (bar) group_left (foo)
  2459  					avg(
  2460  						avg_over_time({app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  2461  							| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m]
  2462  										) by (namespace,instance)
  2463  							) by (foo)
  2464  					`,
  2465  			exp: mustNewBinOpExpr(OpTypeAdd, &BinOpOptions{ReturnBool: false, VectorMatching: &VectorMatching{Card: CardManyToOne, Include: []string{"foo"}, On: false, MatchingLabels: []string{"bar"}}},
  2466  				mustNewVectorAggregationExpr(
  2467  					newRangeAggregationExpr(
  2468  						newLogRange(&PipelineExpr{
  2469  							Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2470  							MultiStages: MultiStageExpr{
  2471  								newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2472  								newLabelParserExpr(OpParserTypeJSON, ""),
  2473  								&LabelFilterExpr{
  2474  									LabelFilterer: log.NewOrLabelFilter(
  2475  										log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  2476  										log.NewAndLabelFilter(
  2477  											log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  2478  											log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  2479  										),
  2480  									),
  2481  								},
  2482  								newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  2483  								newLabelFmtExpr([]log.LabelFmt{
  2484  									log.NewRenameLabelFmt("foo", "bar"),
  2485  									log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  2486  								}),
  2487  							},
  2488  						},
  2489  							5*time.Minute,
  2490  							newUnwrapExpr("foo", ""),
  2491  							nil),
  2492  						OpRangeTypeQuantile, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, NewStringLabelFilter("0.99998"),
  2493  					),
  2494  					OpTypeSum,
  2495  					&Grouping{Groups: []string{"foo", "bar"}},
  2496  					nil,
  2497  				),
  2498  				mustNewVectorAggregationExpr(
  2499  					newRangeAggregationExpr(
  2500  						newLogRange(&PipelineExpr{
  2501  							Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2502  							MultiStages: MultiStageExpr{
  2503  								newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2504  								newLabelParserExpr(OpParserTypeJSON, ""),
  2505  								&LabelFilterExpr{
  2506  									LabelFilterer: log.NewOrLabelFilter(
  2507  										log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  2508  										log.NewAndLabelFilter(
  2509  											log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  2510  											log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  2511  										),
  2512  									),
  2513  								},
  2514  								newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  2515  								newLabelFmtExpr([]log.LabelFmt{
  2516  									log.NewRenameLabelFmt("foo", "bar"),
  2517  									log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  2518  								}),
  2519  							},
  2520  						},
  2521  							5*time.Minute,
  2522  							newUnwrapExpr("foo", ""),
  2523  							nil),
  2524  						OpRangeTypeAvg, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, nil,
  2525  					),
  2526  					OpTypeAvg,
  2527  					&Grouping{Groups: []string{"foo"}},
  2528  					nil,
  2529  				),
  2530  			),
  2531  		},
  2532  		{
  2533  			in: `
  2534  			sum by (app,machine) (count_over_time({app="foo"}[1m])) > bool on () group_right (app) sum by (app) (count_over_time({app="foo"}[1m]))
  2535  					`,
  2536  			exp: mustNewBinOpExpr(OpTypeGT, &BinOpOptions{ReturnBool: true, VectorMatching: &VectorMatching{Card: CardOneToMany, Include: []string{"app"}, On: true, MatchingLabels: nil}},
  2537  				mustNewVectorAggregationExpr(
  2538  					newRangeAggregationExpr(
  2539  						&LogRange{
  2540  							Left:     newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2541  							Interval: 1 * time.Minute,
  2542  						},
  2543  						OpRangeTypeCount, nil, nil,
  2544  					),
  2545  					OpTypeSum,
  2546  					&Grouping{Groups: []string{"app", "machine"}},
  2547  					nil,
  2548  				),
  2549  				mustNewVectorAggregationExpr(
  2550  					newRangeAggregationExpr(
  2551  						&LogRange{
  2552  							Left:     newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2553  							Interval: 1 * time.Minute,
  2554  						},
  2555  						OpRangeTypeCount, nil, nil,
  2556  					),
  2557  					OpTypeSum,
  2558  					&Grouping{Groups: []string{"app"}},
  2559  					nil,
  2560  				),
  2561  			),
  2562  		},
  2563  		{
  2564  			in: `
  2565  			sum by (app,machine) (count_over_time({app="foo"}[1m])) > bool on () group_right sum by (app) (count_over_time({app="foo"}[1m]))
  2566  					`,
  2567  			exp: mustNewBinOpExpr(OpTypeGT, &BinOpOptions{ReturnBool: true, VectorMatching: &VectorMatching{Card: CardOneToMany, Include: nil, On: true, MatchingLabels: nil}},
  2568  				mustNewVectorAggregationExpr(
  2569  					newRangeAggregationExpr(
  2570  						&LogRange{
  2571  							Left:     newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2572  							Interval: 1 * time.Minute,
  2573  						},
  2574  						OpRangeTypeCount, nil, nil,
  2575  					),
  2576  					OpTypeSum,
  2577  					&Grouping{Groups: []string{"app", "machine"}},
  2578  					nil,
  2579  				),
  2580  				mustNewVectorAggregationExpr(
  2581  					newRangeAggregationExpr(
  2582  						&LogRange{
  2583  							Left:     newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2584  							Interval: 1 * time.Minute,
  2585  						},
  2586  						OpRangeTypeCount, nil, nil,
  2587  					),
  2588  					OpTypeSum,
  2589  					&Grouping{Groups: []string{"app"}},
  2590  					nil,
  2591  				),
  2592  			),
  2593  		},
  2594  		{
  2595  			in: `
  2596  			label_replace(
  2597  				sum by (foo,bar) (
  2598  					quantile_over_time(0.99998,{app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  2599  						| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m]
  2600  									) by (namespace,instance)
  2601  						)
  2602  						+
  2603  						avg(
  2604  							avg_over_time({app="foo"} |= "bar" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)
  2605  								| line_format "blip{{ .foo }}blop {{.status_code}}" | label_format foo=bar,status_code="buzz{{.bar}}" | unwrap foo [5m]
  2606  											) by (namespace,instance)
  2607  								) by (foo,bar),
  2608  				"foo",
  2609  				"$1",
  2610  				"svc",
  2611  				"(.*)"
  2612  				)`,
  2613  			exp: mustNewLabelReplaceExpr(
  2614  				mustNewBinOpExpr(OpTypeAdd, &BinOpOptions{VectorMatching: &VectorMatching{Card: CardOneToOne}, ReturnBool: false},
  2615  					mustNewVectorAggregationExpr(
  2616  						newRangeAggregationExpr(
  2617  							newLogRange(&PipelineExpr{
  2618  								Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2619  								MultiStages: MultiStageExpr{
  2620  									newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2621  									newLabelParserExpr(OpParserTypeJSON, ""),
  2622  									&LabelFilterExpr{
  2623  										LabelFilterer: log.NewOrLabelFilter(
  2624  											log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  2625  											log.NewAndLabelFilter(
  2626  												log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  2627  												log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  2628  											),
  2629  										),
  2630  									},
  2631  									newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  2632  									newLabelFmtExpr([]log.LabelFmt{
  2633  										log.NewRenameLabelFmt("foo", "bar"),
  2634  										log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  2635  									}),
  2636  								},
  2637  							},
  2638  								5*time.Minute,
  2639  								newUnwrapExpr("foo", ""),
  2640  								nil),
  2641  							OpRangeTypeQuantile, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, NewStringLabelFilter("0.99998"),
  2642  						),
  2643  						OpTypeSum,
  2644  						&Grouping{Groups: []string{"foo", "bar"}},
  2645  						nil,
  2646  					),
  2647  					mustNewVectorAggregationExpr(
  2648  						newRangeAggregationExpr(
  2649  							newLogRange(&PipelineExpr{
  2650  								Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2651  								MultiStages: MultiStageExpr{
  2652  									newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2653  									newLabelParserExpr(OpParserTypeJSON, ""),
  2654  									&LabelFilterExpr{
  2655  										LabelFilterer: log.NewOrLabelFilter(
  2656  											log.NewDurationLabelFilter(log.LabelFilterGreaterThanOrEqual, "latency", 250*time.Millisecond),
  2657  											log.NewAndLabelFilter(
  2658  												log.NewNumericLabelFilter(log.LabelFilterLesserThan, "status_code", 500.0),
  2659  												log.NewNumericLabelFilter(log.LabelFilterGreaterThan, "status_code", 200.0),
  2660  											),
  2661  										),
  2662  									},
  2663  									newLineFmtExpr("blip{{ .foo }}blop {{.status_code}}"),
  2664  									newLabelFmtExpr([]log.LabelFmt{
  2665  										log.NewRenameLabelFmt("foo", "bar"),
  2666  										log.NewTemplateLabelFmt("status_code", "buzz{{.bar}}"),
  2667  									}),
  2668  								},
  2669  							},
  2670  								5*time.Minute,
  2671  								newUnwrapExpr("foo", ""),
  2672  								nil),
  2673  							OpRangeTypeAvg, &Grouping{Without: false, Groups: []string{"namespace", "instance"}}, nil,
  2674  						),
  2675  						OpTypeAvg,
  2676  						&Grouping{Groups: []string{"foo", "bar"}},
  2677  						nil,
  2678  					),
  2679  				),
  2680  				"foo", "$1", "svc", "(.*)",
  2681  			),
  2682  		},
  2683  		{
  2684  			// ensure binary ops with two literals are reduced recursively
  2685  			in:  `1 + 1 + 1`,
  2686  			exp: &LiteralExpr{Val: 3},
  2687  		},
  2688  		{
  2689  			// ensure binary ops with two literals are reduced when comparisons are used
  2690  			in:  `1 == 1`,
  2691  			exp: &LiteralExpr{Val: 1},
  2692  		},
  2693  		{
  2694  			// ensure binary ops with two literals are reduced when comparisons are used
  2695  			in:  `1 != 1`,
  2696  			exp: &LiteralExpr{Val: 0},
  2697  		},
  2698  		{
  2699  			// ensure binary ops with two literals are reduced when comparisons are used
  2700  			in:  `1 > 1`,
  2701  			exp: &LiteralExpr{Val: 0},
  2702  		},
  2703  		{
  2704  			// ensure binary ops with two literals are reduced when comparisons are used
  2705  			in:  `1 >= 1`,
  2706  			exp: &LiteralExpr{Val: 1},
  2707  		},
  2708  		{
  2709  			// ensure binary ops with two literals are reduced when comparisons are used
  2710  			in:  `1 < 1`,
  2711  			exp: &LiteralExpr{Val: 0},
  2712  		},
  2713  		{
  2714  			// ensure binary ops with two literals are reduced when comparisons are used
  2715  			in:  `1 <= 1`,
  2716  			exp: &LiteralExpr{Val: 1},
  2717  		},
  2718  		{
  2719  			// ensure binary ops with two literals are reduced recursively when comparisons are used
  2720  			in:  `1 >= 1 > 1`,
  2721  			exp: &LiteralExpr{Val: 0},
  2722  		},
  2723  		{
  2724  			in:  `{foo="bar"} + {foo="bar"}`,
  2725  			err: logqlmodel.NewParseError(`unexpected type for left leg of binary operation (+): *syntax.MatchersExpr`, 0, 0),
  2726  		},
  2727  		{
  2728  			in:  `sum(count_over_time({foo="bar"}[5m])) by (foo) - {foo="bar"}`,
  2729  			err: logqlmodel.NewParseError(`unexpected type for right leg of binary operation (-): *syntax.MatchersExpr`, 0, 0),
  2730  		},
  2731  		{
  2732  			in:  `{foo="bar"} / sum(count_over_time({foo="bar"}[5m])) by (foo)`,
  2733  			err: logqlmodel.NewParseError(`unexpected type for left leg of binary operation (/): *syntax.MatchersExpr`, 0, 0),
  2734  		},
  2735  		{
  2736  			in:  `sum(count_over_time({foo="bar"}[5m])) by (foo) or 1`,
  2737  			err: logqlmodel.NewParseError(`unexpected literal for right leg of logical/set binary operation (or): 1.000000`, 0, 0),
  2738  		},
  2739  		{
  2740  			in:  `1 unless sum(count_over_time({foo="bar"}[5m])) by (foo)`,
  2741  			err: logqlmodel.NewParseError(`unexpected literal for left leg of logical/set binary operation (unless): 1.000000`, 0, 0),
  2742  		},
  2743  		{
  2744  			in:  `sum(count_over_time({foo="bar"}[5m])) by (foo) + 1 or 1`,
  2745  			err: logqlmodel.NewParseError(`unexpected literal for right leg of logical/set binary operation (or): 1.000000`, 0, 0),
  2746  		},
  2747  		{
  2748  			in: `count_over_time({ foo ="bar" }[12m]) > count_over_time({ foo = "bar" }[12m])`,
  2749  			exp: &BinOpExpr{
  2750  				Op: OpTypeGT,
  2751  				Opts: &BinOpOptions{
  2752  					ReturnBool:     false,
  2753  					VectorMatching: &VectorMatching{Card: CardOneToOne},
  2754  				},
  2755  				SampleExpr: &RangeAggregationExpr{
  2756  					Left: &LogRange{
  2757  						Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
  2758  						Interval: 12 * time.Minute,
  2759  					},
  2760  					Operation: "count_over_time",
  2761  				},
  2762  				RHS: &RangeAggregationExpr{
  2763  					Left: &LogRange{
  2764  						Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
  2765  						Interval: 12 * time.Minute,
  2766  					},
  2767  					Operation: "count_over_time",
  2768  				},
  2769  			},
  2770  		},
  2771  		{
  2772  			in: `count_over_time({ foo = "bar" }[12m]) > 1`,
  2773  			exp: &BinOpExpr{
  2774  				Op: OpTypeGT,
  2775  				Opts: &BinOpOptions{
  2776  					ReturnBool:     false,
  2777  					VectorMatching: &VectorMatching{Card: CardOneToOne},
  2778  				},
  2779  				SampleExpr: &RangeAggregationExpr{
  2780  					Left: &LogRange{
  2781  						Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
  2782  						Interval: 12 * time.Minute,
  2783  					},
  2784  					Operation: "count_over_time",
  2785  				},
  2786  				RHS: &LiteralExpr{Val: 1},
  2787  			},
  2788  		},
  2789  		{
  2790  			// cannot compare metric & log queries
  2791  			in:  `count_over_time({ foo = "bar" }[12m]) > { foo = "bar" }`,
  2792  			err: logqlmodel.NewParseError("unexpected type for right leg of binary operation (>): *syntax.MatchersExpr", 0, 0),
  2793  		},
  2794  		{
  2795  			in: `count_over_time({ foo = "bar" }[12m]) or count_over_time({ foo = "bar" }[12m]) > 1`,
  2796  			exp: &BinOpExpr{
  2797  				Op: OpTypeOr,
  2798  				Opts: &BinOpOptions{
  2799  					ReturnBool:     false,
  2800  					VectorMatching: &VectorMatching{},
  2801  				},
  2802  				SampleExpr: &RangeAggregationExpr{
  2803  					Left: &LogRange{
  2804  						Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
  2805  						Interval: 12 * time.Minute,
  2806  					},
  2807  					Operation: "count_over_time",
  2808  				},
  2809  				RHS: &BinOpExpr{
  2810  					Op: OpTypeGT,
  2811  					Opts: &BinOpOptions{
  2812  						ReturnBool:     false,
  2813  						VectorMatching: &VectorMatching{Card: CardOneToOne},
  2814  					},
  2815  					SampleExpr: &RangeAggregationExpr{
  2816  						Left: &LogRange{
  2817  							Left:     &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
  2818  							Interval: 12 * time.Minute,
  2819  						},
  2820  						Operation: "count_over_time",
  2821  					},
  2822  					RHS: &LiteralExpr{Val: 1},
  2823  				},
  2824  			},
  2825  		},
  2826  		{
  2827  			// test associativity
  2828  			in:  `1 > 1 < 1`,
  2829  			exp: &LiteralExpr{Val: 1},
  2830  		},
  2831  		{
  2832  			// bool modifiers are reduced-away between two literal legs
  2833  			in:  `1 > 1 > bool 1`,
  2834  			exp: &LiteralExpr{Val: 0},
  2835  		},
  2836  		{
  2837  			// cannot lead with bool modifier
  2838  			in:  `bool 1 > 1 > bool 1`,
  2839  			err: logqlmodel.NewParseError("syntax error: unexpected bool", 1, 1),
  2840  		},
  2841  		{
  2842  			in:  `sum_over_time({namespace="tns"} |= "level=error" | json |foo>=5,bar<25ms| unwrap latency [5m]) by (foo)`,
  2843  			err: logqlmodel.NewParseError("grouping not allowed for sum_over_time aggregation", 0, 0),
  2844  		},
  2845  		{
  2846  			in:  `sum_over_time(50,{namespace="tns"} |= "level=error" | json |foo>=5,bar<25ms| unwrap latency [5m])`,
  2847  			err: logqlmodel.NewParseError("parameter 50 not supported for operation sum_over_time", 0, 0),
  2848  		},
  2849  		{
  2850  			in:  `quantile_over_time({namespace="tns"} |= "level=error" | json |foo>=5,bar<25ms| unwrap latency [5m])`,
  2851  			err: logqlmodel.NewParseError("parameter required for operation quantile_over_time", 0, 0),
  2852  		},
  2853  		{
  2854  			in:  `quantile_over_time(foo,{namespace="tns"} |= "level=error" | json |foo>=5,bar<25ms| unwrap latency [5m])`,
  2855  			err: logqlmodel.NewParseError("syntax error: unexpected IDENTIFIER, expecting NUMBER or { or (", 1, 20),
  2856  		},
  2857  		{
  2858  			in: `{app="foo"}
  2859  					# |= "bar"
  2860  					| json`,
  2861  			exp: &PipelineExpr{
  2862  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2863  				MultiStages: MultiStageExpr{
  2864  					newLabelParserExpr(OpParserTypeJSON, ""),
  2865  				},
  2866  			},
  2867  		},
  2868  		{
  2869  			in: `{app="foo"}
  2870  					#
  2871  					|= "bar"
  2872  					| json`,
  2873  			exp: &PipelineExpr{
  2874  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2875  				MultiStages: MultiStageExpr{
  2876  					newLineFilterExpr(labels.MatchEqual, "", "bar"),
  2877  					newLabelParserExpr(OpParserTypeJSON, ""),
  2878  				},
  2879  			},
  2880  		},
  2881  		{
  2882  			in:  `{app="foo"} # |= "bar" | json`,
  2883  			exp: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2884  		},
  2885  		{
  2886  			in: `{app="foo"} | json #`,
  2887  			exp: &PipelineExpr{
  2888  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2889  				MultiStages: MultiStageExpr{
  2890  					newLabelParserExpr(OpParserTypeJSON, ""),
  2891  				},
  2892  			},
  2893  		},
  2894  		{
  2895  			in:  `#{app="foo"} | json`,
  2896  			err: logqlmodel.NewParseError("syntax error: unexpected $end", 1, 20),
  2897  		},
  2898  		{
  2899  			in:  `{app="#"}`,
  2900  			exp: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "#"}}),
  2901  		},
  2902  		{
  2903  			in: `{app="foo"} |= "#"`,
  2904  			exp: &PipelineExpr{
  2905  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2906  				MultiStages: MultiStageExpr{
  2907  					newLineFilterExpr(labels.MatchEqual, "", "#"),
  2908  				},
  2909  			},
  2910  		},
  2911  		{
  2912  			in: `{app="foo"} | bar="#"`,
  2913  			exp: &PipelineExpr{
  2914  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2915  				MultiStages: MultiStageExpr{
  2916  					&LabelFilterExpr{
  2917  						LabelFilterer: log.NewStringLabelFilter(mustNewMatcher(labels.MatchEqual, "bar", "#")),
  2918  					},
  2919  				},
  2920  			},
  2921  		},
  2922  		{
  2923  			in: `{app="foo"} | json bob="top.sub[\"index\"]"`,
  2924  			exp: &PipelineExpr{
  2925  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2926  				MultiStages: MultiStageExpr{
  2927  					newJSONExpressionParser([]log.JSONExpression{
  2928  						log.NewJSONExpr("bob", `top.sub["index"]`),
  2929  					}),
  2930  				},
  2931  			},
  2932  		},
  2933  		{
  2934  			in: `{app="foo"} | json bob="top.params[0]"`,
  2935  			exp: &PipelineExpr{
  2936  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2937  				MultiStages: MultiStageExpr{
  2938  					newJSONExpressionParser([]log.JSONExpression{
  2939  						log.NewJSONExpr("bob", `top.params[0]`),
  2940  					}),
  2941  				},
  2942  			},
  2943  		},
  2944  		{
  2945  			in: `{app="foo"} | json response_code="response.code", api_key="request.headers[\"X-API-KEY\"]"`,
  2946  			exp: &PipelineExpr{
  2947  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2948  				MultiStages: MultiStageExpr{
  2949  					newJSONExpressionParser([]log.JSONExpression{
  2950  						log.NewJSONExpr("response_code", `response.code`),
  2951  						log.NewJSONExpr("api_key", `request.headers["X-API-KEY"]`),
  2952  					}),
  2953  				},
  2954  			},
  2955  		},
  2956  		{
  2957  			in: `{app="foo"} | json response_code, api_key="request.headers[\"X-API-KEY\"]", layer7_something_specific="layer7_something_specific"`,
  2958  			exp: &PipelineExpr{
  2959  				Left: newMatcherExpr([]*labels.Matcher{{Type: labels.MatchEqual, Name: "app", Value: "foo"}}),
  2960  				MultiStages: MultiStageExpr{
  2961  					newJSONExpressionParser([]log.JSONExpression{
  2962  						log.NewJSONExpr("response_code", `response_code`),
  2963  						log.NewJSONExpr("api_key", `request.headers["X-API-KEY"]`),
  2964  						log.NewJSONExpr("layer7_something_specific", `layer7_something_specific`),
  2965  					}),
  2966  				},
  2967  			},
  2968  		},
  2969  		{
  2970  			in: `count_over_time({ foo ="bar" } | json layer7_something_specific="layer7_something_specific" [12m])`,
  2971  			exp: &RangeAggregationExpr{
  2972  				Left: &LogRange{
  2973  					Left: &PipelineExpr{
  2974  						MultiStages: MultiStageExpr{
  2975  							newJSONExpressionParser([]log.JSONExpression{
  2976  								log.NewJSONExpr("layer7_something_specific", `layer7_something_specific`),
  2977  							}),
  2978  						},
  2979  						Left: &MatchersExpr{Mts: []*labels.Matcher{mustNewMatcher(labels.MatchEqual, "foo", "bar")}},
  2980  					},
  2981  					Interval: 12 * time.Minute,
  2982  				},
  2983  				Operation: "count_over_time",
  2984  			},
  2985  		},
  2986  	} {
  2987  		t.Run(tc.in, func(t *testing.T) {
  2988  			ast, err := ParseExpr(tc.in)
  2989  			require.Equal(t, tc.err, err)
  2990  			require.Equal(t, tc.exp, ast)
  2991  		})
  2992  	}
  2993  }
  2994  
  2995  func TestParseMatchers(t *testing.T) {
  2996  	tests := []struct {
  2997  		input   string
  2998  		want    []*labels.Matcher
  2999  		wantErr bool
  3000  	}{
  3001  		{
  3002  			`{app="foo",cluster=~".+bar"}`,
  3003  			[]*labels.Matcher{
  3004  				mustNewMatcher(labels.MatchEqual, "app", "foo"),
  3005  				mustNewMatcher(labels.MatchRegexp, "cluster", ".+bar"),
  3006  			},
  3007  			false,
  3008  		},
  3009  		{
  3010  			`{app!="foo",cluster=~".+bar",bar!~".?boo"}`,
  3011  			[]*labels.Matcher{
  3012  				mustNewMatcher(labels.MatchNotEqual, "app", "foo"),
  3013  				mustNewMatcher(labels.MatchRegexp, "cluster", ".+bar"),
  3014  				mustNewMatcher(labels.MatchNotRegexp, "bar", ".?boo"),
  3015  			},
  3016  			false,
  3017  		},
  3018  		{
  3019  			`{app!="foo",cluster=~".+bar",bar!~".?boo"`,
  3020  			nil,
  3021  			true,
  3022  		},
  3023  		{
  3024  			`{app!="foo",cluster=~".+bar",bar!~".?boo"} |= "test"`,
  3025  			nil,
  3026  			true,
  3027  		},
  3028  	}
  3029  	for _, tt := range tests {
  3030  		t.Run(tt.input, func(t *testing.T) {
  3031  			got, err := ParseMatchers(tt.input)
  3032  			if (err != nil) != tt.wantErr {
  3033  				t.Errorf("ParseMatchers() error = %v, wantErr %v", err, tt.wantErr)
  3034  				return
  3035  			}
  3036  			if !reflect.DeepEqual(got, tt.want) {
  3037  				t.Errorf("ParseMatchers() = %v, want %v", got, tt.want)
  3038  			}
  3039  		})
  3040  	}
  3041  }
  3042  
  3043  func TestIsParseError(t *testing.T) {
  3044  	tests := []struct {
  3045  		name  string
  3046  		errFn func() error
  3047  		want  bool
  3048  	}{
  3049  		{
  3050  			"bad query",
  3051  			func() error {
  3052  				_, err := ParseExpr(`{foo`)
  3053  				return err
  3054  			},
  3055  			true,
  3056  		},
  3057  		{
  3058  			"other error",
  3059  			func() error {
  3060  				return errors.New("")
  3061  			},
  3062  			false,
  3063  		},
  3064  	}
  3065  	for _, tt := range tests {
  3066  		t.Run(tt.name, func(t *testing.T) {
  3067  			if got := errors.Is(tt.errFn(), logqlmodel.ErrParse); got != tt.want {
  3068  				t.Errorf("IsParseError() = %v, want %v", got, tt.want)
  3069  			}
  3070  		})
  3071  	}
  3072  }
  3073  
  3074  func Test_PipelineCombined(t *testing.T) {
  3075  	query := `{job="cortex-ops/query-frontend"} |= "logging.go" | logfmt | line_format "{{.msg}}" | regexp "(?P<method>\\w+) (?P<path>[\\w|/]+) \\((?P<status>\\d+?)\\) (?P<duration>.*)" | (duration > 1s or status==200) and method="POST" | line_format "{{.duration}}|{{.method}}|{{.status}}"`
  3076  
  3077  	expr, err := ParseLogSelector(query, true)
  3078  	require.Nil(t, err)
  3079  
  3080  	p, err := expr.Pipeline()
  3081  	require.Nil(t, err)
  3082  	sp := p.ForStream(labels.Labels{})
  3083  	line, lbs, matches := sp.Process(0, []byte(`level=debug ts=2020-10-02T10:10:42.092268913Z caller=logging.go:66 traceID=a9d4d8a928d8db1 msg="POST /api/prom/api/v1/query_range (200) 1.5s"`))
  3084  	require.True(t, matches)
  3085  	require.Equal(
  3086  		t,
  3087  		labels.Labels{labels.Label{Name: "caller", Value: "logging.go:66"}, labels.Label{Name: "duration", Value: "1.5s"}, labels.Label{Name: "level", Value: "debug"}, labels.Label{Name: "method", Value: "POST"}, labels.Label{Name: "msg", Value: "POST /api/prom/api/v1/query_range (200) 1.5s"}, labels.Label{Name: "path", Value: "/api/prom/api/v1/query_range"}, labels.Label{Name: "status", Value: "200"}, labels.Label{Name: "traceID", Value: "a9d4d8a928d8db1"}, labels.Label{Name: "ts", Value: "2020-10-02T10:10:42.092268913Z"}},
  3088  		lbs.Labels(),
  3089  	)
  3090  	require.Equal(t, string([]byte(`1.5s|POST|200`)), string(line))
  3091  }
  3092  
  3093  func Benchmark_PipelineCombined(b *testing.B) {
  3094  	query := `{job="cortex-ops/query-frontend"} |= "logging.go" | logfmt | line_format "{{.msg}}" | regexp "(?P<method>\\w+) (?P<path>[\\w|/]+) \\((?P<status>\\d+?)\\) (?P<duration>.*)" | (duration > 1s or status==200) and method="POST" | line_format "{{.duration}}|{{.method}}|{{.status}}"`
  3095  
  3096  	expr, err := ParseLogSelector(query, true)
  3097  	require.Nil(b, err)
  3098  
  3099  	p, err := expr.Pipeline()
  3100  	require.Nil(b, err)
  3101  	sp := p.ForStream(labels.Labels{})
  3102  	var (
  3103  		line    []byte
  3104  		lbs     log.LabelsResult
  3105  		matches bool
  3106  	)
  3107  	in := []byte(`level=debug ts=2020-10-02T10:10:42.092268913Z caller=logging.go:66 traceID=a9d4d8a928d8db1 msg="POST /api/prom/api/v1/query_range (200) 1.5s"`)
  3108  
  3109  	b.ReportAllocs()
  3110  	b.ResetTimer()
  3111  	for i := 0; i < b.N; i++ {
  3112  		line, lbs, matches = sp.Process(0, in)
  3113  	}
  3114  	require.True(b, matches)
  3115  	require.Equal(
  3116  		b,
  3117  		labels.Labels{labels.Label{Name: "caller", Value: "logging.go:66"}, labels.Label{Name: "duration", Value: "1.5s"}, labels.Label{Name: "level", Value: "debug"}, labels.Label{Name: "method", Value: "POST"}, labels.Label{Name: "msg", Value: "POST /api/prom/api/v1/query_range (200) 1.5s"}, labels.Label{Name: "path", Value: "/api/prom/api/v1/query_range"}, labels.Label{Name: "status", Value: "200"}, labels.Label{Name: "traceID", Value: "a9d4d8a928d8db1"}, labels.Label{Name: "ts", Value: "2020-10-02T10:10:42.092268913Z"}},
  3118  		lbs.Labels(),
  3119  	)
  3120  	require.Equal(b, string([]byte(`1.5s|POST|200`)), string(line))
  3121  }
  3122  
  3123  func Benchmark_MetricPipelineCombined(b *testing.B) {
  3124  	query := `count_over_time({job="cortex-ops/query-frontend"} |= "logging.go" | logfmt | line_format "{{.msg}}" | regexp "(?P<method>\\w+) (?P<path>[\\w|/]+) \\((?P<status>\\d+?)\\) (?P<duration>.*)" | (duration > 1s or status==200) and method="POST" | line_format "{{.duration}}|{{.method}}|{{.status}}"[1m])`
  3125  
  3126  	expr, err := ParseSampleExpr(query)
  3127  	require.Nil(b, err)
  3128  
  3129  	p, err := expr.Extractor()
  3130  	require.Nil(b, err)
  3131  	sp := p.ForStream(labels.Labels{})
  3132  	var (
  3133  		v       float64
  3134  		lbs     log.LabelsResult
  3135  		matches bool
  3136  	)
  3137  	in := []byte(`level=debug ts=2020-10-02T10:10:42.092268913Z caller=logging.go:66 traceID=a9d4d8a928d8db1 msg="POST /api/prom/api/v1/query_range (200) 1.5s"`)
  3138  	b.ReportAllocs()
  3139  	b.ResetTimer()
  3140  	for i := 0; i < b.N; i++ {
  3141  		v, lbs, matches = sp.Process(0, in)
  3142  	}
  3143  	require.True(b, matches)
  3144  	require.Equal(
  3145  		b,
  3146  		labels.Labels{labels.Label{Name: "caller", Value: "logging.go:66"}, labels.Label{Name: "duration", Value: "1.5s"}, labels.Label{Name: "level", Value: "debug"}, labels.Label{Name: "method", Value: "POST"}, labels.Label{Name: "msg", Value: "POST /api/prom/api/v1/query_range (200) 1.5s"}, labels.Label{Name: "path", Value: "/api/prom/api/v1/query_range"}, labels.Label{Name: "status", Value: "200"}, labels.Label{Name: "traceID", Value: "a9d4d8a928d8db1"}, labels.Label{Name: "ts", Value: "2020-10-02T10:10:42.092268913Z"}},
  3147  		lbs.Labels(),
  3148  	)
  3149  	require.Equal(b, 1.0, v)
  3150  }
  3151  
  3152  var c []*labels.Matcher
  3153  
  3154  func Benchmark_ParseMatchers(b *testing.B) {
  3155  	s := `{cpu="10",endpoint="https",instance="10.253.57.87:9100",job="node-exporter",mode="idle",namespace="observability",pod="node-exporter-l454v",service="node-exporter"}`
  3156  	var err error
  3157  	for n := 0; n < b.N; n++ {
  3158  		c, err = ParseMatchers(s)
  3159  		require.NoError(b, err)
  3160  	}
  3161  }
  3162  
  3163  var lbs labels.Labels
  3164  
  3165  func Benchmark_CompareParseLabels(b *testing.B) {
  3166  	s := `{cpu="10",endpoint="https",instance="10.253.57.87:9100",job="node-exporter",mode="idle",namespace="observability",pod="node-exporter-l454v",service="node-exporter"}`
  3167  	var err error
  3168  	b.Run("logql", func(b *testing.B) {
  3169  		for n := 0; n < b.N; n++ {
  3170  			c, err = ParseMatchers(s)
  3171  			require.NoError(b, err)
  3172  		}
  3173  	})
  3174  	b.Run("promql", func(b *testing.B) {
  3175  		for n := 0; n < b.N; n++ {
  3176  			lbs, err = ParseLabels(s)
  3177  			require.NoError(b, err)
  3178  		}
  3179  	})
  3180  }
  3181  
  3182  func TestParseSampleExpr_equalityMatcher(t *testing.T) {
  3183  	for _, tc := range []struct {
  3184  		in  string
  3185  		err error
  3186  	}{
  3187  		{
  3188  			in: `count_over_time({foo="bar"}[5m])`,
  3189  		},
  3190  		{
  3191  			in:  `count_over_time({foo!="bar"}[5m])`,
  3192  			err: logqlmodel.NewParseError(errAtleastOneEqualityMatcherRequired, 0, 0),
  3193  		},
  3194  		{
  3195  			in: `count_over_time({app="baz", foo!="bar"}[5m])`,
  3196  		},
  3197  		{
  3198  			in: `count_over_time({app=~".+"}[5m])`,
  3199  		},
  3200  		{
  3201  			in:  `count_over_time({app=~".*"}[5m])`,
  3202  			err: logqlmodel.NewParseError(errAtleastOneEqualityMatcherRequired, 0, 0),
  3203  		},
  3204  		{
  3205  			in: `count_over_time({app=~"bar|baz"}[5m])`,
  3206  		},
  3207  		{
  3208  			in:  `count_over_time({app!~"bar|baz"}[5m])`,
  3209  			err: logqlmodel.NewParseError(errAtleastOneEqualityMatcherRequired, 0, 0),
  3210  		},
  3211  		{
  3212  			in:  `1 + count_over_time({app=~".*"}[5m])`,
  3213  			err: logqlmodel.NewParseError(errAtleastOneEqualityMatcherRequired, 0, 0),
  3214  		},
  3215  		{
  3216  			in:  `1 + count_over_time({app=~".+"}[5m]) + count_over_time({app=~".*"}[5m])`,
  3217  			err: logqlmodel.NewParseError(errAtleastOneEqualityMatcherRequired, 0, 0),
  3218  		},
  3219  		{
  3220  			in: `1 + count_over_time({app=~".+"}[5m]) + count_over_time({app=~".+"}[5m])`,
  3221  		},
  3222  		{
  3223  			in:  `1 + count_over_time({app=~".+"}[5m]) + count_over_time({app=~".*"}[5m]) + 1`,
  3224  			err: logqlmodel.NewParseError(errAtleastOneEqualityMatcherRequired, 0, 0),
  3225  		},
  3226  		{
  3227  			in: `1 + count_over_time({app=~".+"}[5m]) + count_over_time({app=~".+"}[5m]) + 1`,
  3228  		},
  3229  	} {
  3230  		t.Run(tc.in, func(t *testing.T) {
  3231  			_, err := ParseSampleExpr(tc.in)
  3232  			require.Equal(t, tc.err, err)
  3233  		})
  3234  	}
  3235  }
  3236  
  3237  func TestParseLogSelectorExpr_equalityMatcher(t *testing.T) {
  3238  	for _, tc := range []struct {
  3239  		in  string
  3240  		err error
  3241  	}{
  3242  		{
  3243  			in: `{foo="bar"}`,
  3244  		},
  3245  		{
  3246  			in:  `{foo!="bar"}`,
  3247  			err: logqlmodel.NewParseError(errAtleastOneEqualityMatcherRequired, 0, 0),
  3248  		},
  3249  		{
  3250  			in: `{app="baz", foo!="bar"}`,
  3251  		},
  3252  		{
  3253  			in: `{app=~".+"}`,
  3254  		},
  3255  		{
  3256  			in:  `{app=~".*"}`,
  3257  			err: logqlmodel.NewParseError(errAtleastOneEqualityMatcherRequired, 0, 0),
  3258  		},
  3259  		{
  3260  			in: `{foo=~"bar|baz"}`,
  3261  		},
  3262  		{
  3263  			in:  `{foo!~"bar|baz"}`,
  3264  			err: logqlmodel.NewParseError(errAtleastOneEqualityMatcherRequired, 0, 0),
  3265  		},
  3266  	} {
  3267  		t.Run(tc.in, func(t *testing.T) {
  3268  			_, err := ParseLogSelector(tc.in, true)
  3269  			require.Equal(t, tc.err, err)
  3270  		})
  3271  	}
  3272  }