You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ha...@apache.org on 2026/04/15 06:42:53 UTC

(skywalking-banyandb) branch test/query created (now d53c651e4)

This is an automated email from the ASF dual-hosted git repository.

hanahmily pushed a change to branch test/query
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git


      at d53c651e4 refactor(query): extract validation helpers to reduce cyclomatic complexity

This branch includes the following new commits:

     new 02c720f0f fix(query): validate MATCH/IN conditions and handle nil OR branch
     new 61645759f chore(query): clarify OR nil-branch comment and add test-case generation CI check
     new 5d963b470 chore(test): add Apache license headers to generated test case files
     new d53c651e4 refactor(query): extract validation helpers to reduce cyclomatic complexity

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(skywalking-banyandb) 04/04: refactor(query): extract validation helpers to reduce cyclomatic complexity

Posted by ha...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hanahmily pushed a commit to branch test/query
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git

commit d53c651e40bacdcf3e658108108af6b62dea6fbf
Author: Hongtao Gao <ha...@gmail.com>
AuthorDate: Wed Apr 15 05:53:10 2026 +0000

    refactor(query): extract validation helpers to reduce cyclomatic complexity
    
    Extract allEntitiesSpecific, validateMatchCondition, and validateINCondition
    helpers from BuildQuery and parseConditionToQuery, bringing both below the
    gocyclo threshold of 30. Also fix remaining lint issues in the test-case
    generator: gosec G306 (0o600 perms), goconst (true/false/none constants),
    gocritic (exitAfterDefer, appendCombine), line-length wrapping, and package
    comments.
    
    via [HAPI](https://hapi.run)
---
 pkg/index/inverted/query.go                        | 75 +++++++++++++---------
 test/cases/measure/cmd/capture/capture_test.go     |  2 +-
 test/cases/measure/cmd/generate/capture.go         | 12 ++--
 test/cases/measure/cmd/generate/entry.go           | 17 +++--
 test/cases/measure/cmd/generate/gap.go             | 22 +++----
 test/cases/measure/cmd/generate/layer1_leaf.go     | 44 +++++++------
 test/cases/measure/cmd/generate/layer2_tree.go     | 14 ++--
 test/cases/measure/cmd/generate/layer3_features.go | 34 +++++-----
 test/cases/measure/cmd/generate/main.go            |  6 +-
 test/cases/measure/cmd/generate/ql_renderer.go     | 15 ++---
 test/cases/measure/cmd/generate/seed.go            | 34 +++++++---
 test/cases/measure/cmd/generate/types.go           | 11 ++--
 test/cases/measure/measure.go                      | 63 ++++++++++++------
 13 files changed, 208 insertions(+), 141 deletions(-)

diff --git a/pkg/index/inverted/query.go b/pkg/index/inverted/query.go
index 1221e4643..8018ec6f4 100644
--- a/pkg/index/inverted/query.go
+++ b/pkg/index/inverted/query.go
@@ -150,25 +150,11 @@ func BuildQuery(criteria *modelv1.Criteria, schema logical.Schema, entityDict ma
 			// When one OR branch is entity-only (nil query), entity-level series lookup already
 			// constrains the scope. If all entities are specific (no AnyTagValue wildcards),
 			// return match-all — no index query is needed.
-			if left == nil || right == nil {
-				allSpecific := true
-				for _, entity := range entities {
-					for _, entry := range entity {
-						if entry == pbv1.AnyTagValue {
-							allSpecific = false
-							break
-						}
-					}
-					if !allSpecific {
-						break
-					}
-				}
-				if allSpecific {
-					return &queryNode{
-						query: bluge.NewMatchAllQuery(),
-						node:  newMatchAllNode(),
-					}, entities, true, nil
-				}
+			if (left == nil || right == nil) && allEntitiesSpecific(entities) {
+				return &queryNode{
+					query: bluge.NewMatchAllQuery(),
+					node:  newMatchAllNode(),
+				}, entities, true, nil
 			}
 			query, node := bluge.NewBooleanQuery(), newShouldNode()
 			query.SetMinShould(1)
@@ -187,6 +173,18 @@ func BuildQuery(criteria *modelv1.Criteria, schema logical.Schema, entityDict ma
 	return nil, nil, false, logical.ErrInvalidCriteriaType
 }
 
+// allEntitiesSpecific reports whether every entry in all entity lists is a concrete value (no AnyTagValue wildcard).
+func allEntitiesSpecific(entities [][]*modelv1.TagValue) bool {
+	for _, entity := range entities {
+		for _, entry := range entity {
+			if entry == pbv1.AnyTagValue {
+				return false
+			}
+		}
+	}
+	return true
+}
+
 // BuildIndexModeQuery returns blugeQuery for index mode.
 func BuildIndexModeQuery(measureName string, criteria *modelv1.Criteria, schema logical.Schema) (index.Query, error) {
 	var subjectQuery bluge.Query
@@ -290,6 +288,28 @@ func buildIndexModeCriteria(criteria *modelv1.Criteria, schema logical.Schema, e
 	return nil, logical.ErrInvalidCriteriaType
 }
 
+func validateMatchCondition(cond *modelv1.Condition) error {
+	if cond.Value == nil {
+		return errors.WithMessagef(logical.ErrUnsupportedConditionValue, "MATCH condition requires non-nil value")
+	}
+	if _, ok := cond.Value.Value.(*modelv1.TagValue_Str); !ok {
+		return errors.WithMessagef(logical.ErrUnsupportedConditionValue, "MATCH condition requires string value type: %s", cond)
+	}
+	return nil
+}
+
+func validateINCondition(cond *modelv1.Condition) error {
+	if cond.Value == nil {
+		return errors.WithMessagef(logical.ErrUnsupportedConditionValue, "IN condition requires non-nil value")
+	}
+	switch cond.Value.Value.(type) {
+	case *modelv1.TagValue_StrArray, *modelv1.TagValue_IntArray:
+		return nil
+	default:
+		return errors.WithMessagef(logical.ErrUnsupportedConditionValue, "IN condition requires array value type: %s", cond)
+	}
+}
+
 func parseConditionToQuery(cond *modelv1.Condition, indexRule *databasev1.IndexRule,
 	expr logical.LiteralExpr, fieldKey string,
 ) (*queryNode, error) {
@@ -339,11 +359,8 @@ func parseConditionToQuery(cond *modelv1.Condition, indexRule *databasev1.IndexR
 		if indexRule == nil {
 			return nil, errors.WithMessagef(logical.ErrUnsupportedConditionOp, "index rule is mandatory for match operation: %s", cond)
 		}
-		if cond.Value == nil {
-			return nil, errors.WithMessagef(logical.ErrUnsupportedConditionValue, "MATCH condition requires non-nil value")
-		}
-		if _, ok := cond.Value.Value.(*modelv1.TagValue_Str); !ok {
-			return nil, errors.WithMessagef(logical.ErrUnsupportedConditionValue, "MATCH condition requires string value type: %s", cond)
+		if matchErr := validateMatchCondition(cond); matchErr != nil {
+			return nil, matchErr
 		}
 		bb := expr.Bytes()
 		if len(bb) != 1 {
@@ -386,14 +403,8 @@ func parseConditionToQuery(cond *modelv1.Condition, indexRule *databasev1.IndexR
 		node.SetSubNode(node)
 		return &queryNode{query, node}, nil
 	case modelv1.Condition_BINARY_OP_IN:
-		if cond.Value == nil {
-			return nil, errors.WithMessagef(logical.ErrUnsupportedConditionValue, "IN condition requires non-nil value")
-		}
-		switch cond.Value.Value.(type) {
-		case *modelv1.TagValue_StrArray, *modelv1.TagValue_IntArray:
-			// valid
-		default:
-			return nil, errors.WithMessagef(logical.ErrUnsupportedConditionValue, "IN condition requires array value type: %s", cond)
+		if inErr := validateINCondition(cond); inErr != nil {
+			return nil, inErr
 		}
 		bb, elements := expr.Bytes(), expr.Elements()
 		query, node := bluge.NewBooleanQuery(), newShouldNode()
diff --git a/test/cases/measure/cmd/capture/capture_test.go b/test/cases/measure/cmd/capture/capture_test.go
index 113d479f4..d5d80a85d 100644
--- a/test/cases/measure/cmd/capture/capture_test.go
+++ b/test/cases/measure/cmd/capture/capture_test.go
@@ -169,7 +169,7 @@ func TestCapture(t *testing.T) {
 		}
 
 		wantPath := filepath.Join(wantDirPath, testName+".yaml")
-		if writeErr := os.WriteFile(wantPath, respYAML, 0o644); writeErr != nil {
+		if writeErr := os.WriteFile(wantPath, respYAML, 0o600); writeErr != nil {
 			t.Errorf("  [ERROR] %s: write failed: %v", testName, writeErr)
 			continue
 		}
diff --git a/test/cases/measure/cmd/generate/capture.go b/test/cases/measure/cmd/generate/capture.go
index 91bd4c2de..f4b79dc0a 100644
--- a/test/cases/measure/cmd/generate/capture.go
+++ b/test/cases/measure/cmd/generate/capture.go
@@ -15,6 +15,9 @@
 // specific language governing permissions and limitations
 // under the License.
 
+// Package main implements the capture subcommand for the test-case generator.
+// It connects to a live BanyanDB server, executes generated queries, and
+// writes the responses as want/*.yaml files.
 package main
 
 import (
@@ -55,16 +58,12 @@ func runCapture(outputDir, serverAddr string) {
 		fmt.Fprintf(os.Stderr, "Error connecting to server: %v\n", connErr)
 		os.Exit(1)
 	}
-	defer func() {
-		if closeErr := conn.Close(); closeErr != nil {
-			fmt.Fprintf(os.Stderr, "Warning: error closing connection: %v\n", closeErr)
-		}
-	}()
 
 	// Discover all generated input YAML files
 	entries, readErr := os.ReadDir(inputDirPath)
 	if readErr != nil {
 		fmt.Fprintf(os.Stderr, "Error reading input directory: %v\n", readErr)
+		_ = conn.Close()
 		os.Exit(1)
 	}
 
@@ -166,7 +165,7 @@ func runCapture(outputDir, serverAddr string) {
 		}
 
 		wantPath := filepath.Join(wantDirPath, testName+".yaml")
-		if writeErr := os.WriteFile(wantPath, respYAML, 0o644); writeErr != nil {
+		if writeErr := os.WriteFile(wantPath, respYAML, 0o600); writeErr != nil {
 			fmt.Fprintf(os.Stderr, "  [ERROR] %s: write failed: %v\n", testName, writeErr)
 			errorCount++
 			continue
@@ -177,6 +176,7 @@ func runCapture(outputDir, serverAddr string) {
 		capturedCount++
 	}
 
+	_ = conn.Close()
 	fmt.Printf("\nCapture complete: %d captured, %d skipped (error cases), %d errors\n",
 		capturedCount, skippedCount, errorCount)
 }
diff --git a/test/cases/measure/cmd/generate/entry.go b/test/cases/measure/cmd/generate/entry.go
index ac77b7f48..3058b63d9 100644
--- a/test/cases/measure/cmd/generate/entry.go
+++ b/test/cases/measure/cmd/generate/entry.go
@@ -22,6 +22,11 @@ import (
 	"strings"
 )
 
+const (
+	strTrue  = "true"
+	strFalse = "false"
+)
+
 // GenerateEntryCode produces g.Entry lines for measure.go.
 func GenerateEntryCode(cases []*TestCase) string {
 	var lines []string
@@ -38,20 +43,20 @@ func GenerateEntryCode(cases []*TestCase) string {
 func renderEntryLine(tc *TestCase) string {
 	name := tc.Name
 	want := name
-	wantEmpty := "false"
-	wantErr := "false"
-	disOrder := "false"
+	wantEmpty := strFalse
+	wantErr := strFalse
+	disOrder := strFalse
 	duration := "25 * time.Minute"
 	offset := "-20 * time.Minute"
 
 	if tc.WantErr {
-		wantErr = "true"
+		wantErr = strTrue
 	}
 	if tc.WantEmpty {
-		wantEmpty = "true"
+		wantEmpty = strTrue
 	}
 	if tc.DisOrder {
-		disOrder = "true"
+		disOrder = strTrue
 	}
 	if tc.Duration != "" {
 		duration = tc.Duration
diff --git a/test/cases/measure/cmd/generate/gap.go b/test/cases/measure/cmd/generate/gap.go
index 1aa5e78ef..8e0be9475 100644
--- a/test/cases/measure/cmd/generate/gap.go
+++ b/test/cases/measure/cmd/generate/gap.go
@@ -26,17 +26,17 @@ import (
 
 // CoverageMap tracks which combinations are already tested by existing cases.
 type CoverageMap struct {
-	BinaryOps   map[string]bool // "EQ:str", "NE:int", etc.
-	AggFunctions map[string]bool // "MEAN", "MAX", etc.
-	TreeDepths  map[int]bool    // 1, 2, 3, 5
-	LogicalOps  map[string]bool // "AND", "OR"
-	SortOrders  map[string]bool // "ASC", "DESC"
-	TopN        bool
-	GroupBy     bool
-	Limit       bool
-	Offset      bool
-	Criteria    map[string]bool // "EQ", "NE", "IN", etc. (any use of criteria)
-	TestNames   map[string]bool // existing test names
+	BinaryOps    map[string]bool
+	AggFunctions map[string]bool
+	TreeDepths   map[int]bool
+	LogicalOps   map[string]bool
+	SortOrders   map[string]bool
+	Criteria     map[string]bool
+	TestNames    map[string]bool
+	TopN         bool
+	GroupBy      bool
+	Limit        bool
+	Offset       bool
 }
 
 // AnalyzeGaps reads existing test files and builds a coverage map.
diff --git a/test/cases/measure/cmd/generate/layer1_leaf.go b/test/cases/measure/cmd/generate/layer1_leaf.go
index 51b0c3e33..5c155df1f 100644
--- a/test/cases/measure/cmd/generate/layer1_leaf.go
+++ b/test/cases/measure/cmd/generate/layer1_leaf.go
@@ -26,12 +26,12 @@ import (
 
 // LeafOp defines a valid BinaryOp + TagValue + measure + tag combination.
 type LeafOp struct {
-	Op         modelv1.Condition_BinaryOp
-	ValueType  string           // "str", "int", "null", "str_array", "int_array"
-	Value      *modelv1.TagValue // example value to use
-	Measure    string           // target measure name
-	TagName    string           // tag to filter on
-	QLValue    string           // QL representation of the value
+	Value     *modelv1.TagValue
+	ValueType string
+	Measure   string
+	TagName   string
+	QLValue   string
+	Op        modelv1.Condition_BinaryOp
 }
 
 // GenerateLayer1 produces test cases for the Criteria leaf layer.
@@ -50,10 +50,10 @@ func GenerateLayer1() []*TestCase {
 			continue
 		}
 		cases = append(cases, &TestCase{
-			Name:    fmt.Sprintf("gen_leaf_%s_%s", binaryOpShortName(op.Op), op.ValueType),
-			Measure: m,
-			Request: req,
-			QL:      ql,
+			Name:     fmt.Sprintf("gen_leaf_%s_%s", binaryOpShortName(op.Op), op.ValueType),
+			Measure:  m,
+			Request:  req,
+			QL:       ql,
 			WantErr:  op.ValueType == "null", // server rejects null value comparisons
 			Duration: "25 * time.Minute",
 		})
@@ -89,10 +89,16 @@ func defineLeafOps() []LeafOp {
 		{Op: modelv1.Condition_BINARY_OP_GE, ValueType: "str", Value: TagValueStr("svc2"), Measure: "service_cpm_minute", TagName: "id", QLValue: "'svc2'"},
 		{Op: modelv1.Condition_BINARY_OP_GE, ValueType: "int", Value: TagValueInt(2), Measure: "service_traffic", TagName: "layer", QLValue: "2"},
 		// IN with str_array, int_array
-		{Op: modelv1.Condition_BINARY_OP_IN, ValueType: "str_array", Value: TagValueStrArray([]string{"svc1", "svc2"}), Measure: "service_cpm_minute", TagName: "id", QLValue: "('svc1', 'svc2')"},
+		{
+			Op: modelv1.Condition_BINARY_OP_IN, ValueType: "str_array", Value: TagValueStrArray([]string{"svc1", "svc2"}),
+			Measure: "service_cpm_minute", TagName: "id", QLValue: "('svc1', 'svc2')",
+		},
 		{Op: modelv1.Condition_BINARY_OP_IN, ValueType: "int_array", Value: TagValueIntArray([]int64{1, 2}), Measure: "service_traffic", TagName: "layer", QLValue: "(1, 2)"},
 		// NOT_IN with str_array, int_array
-		{Op: modelv1.Condition_BINARY_OP_NOT_IN, ValueType: "str_array", Value: TagValueStrArray([]string{"svc3"}), Measure: "service_cpm_minute", TagName: "id", QLValue: "('svc3')"},
+		{
+			Op: modelv1.Condition_BINARY_OP_NOT_IN, ValueType: "str_array", Value: TagValueStrArray([]string{"svc3"}),
+			Measure: "service_cpm_minute", TagName: "id", QLValue: "('svc3')",
+		},
 		{Op: modelv1.Condition_BINARY_OP_NOT_IN, ValueType: "int_array", Value: TagValueIntArray([]int64{0}), Measure: "service_traffic", TagName: "layer", QLValue: "(0)"},
 		// MATCH with str
 		{Op: modelv1.Condition_BINARY_OP_MATCH, ValueType: "str", Value: TagValueStr("nodea"), Measure: "service_instance_traffic", TagName: "name", QLValue: "MATCH('nodea')"},
@@ -105,10 +111,10 @@ func buildLeafRequest(m *Measure, op LeafOp) *measurev1.QueryRequest {
 	tagNames := AllTagNames(m)
 	fieldNames := AllFieldNames(m)
 	return &measurev1.QueryRequest{
-		Name:           m.Name,
-		Groups:         []string{m.Group},
-		Criteria:       criteria,
-		TagProjection:  BuildTagProjection(m, tagNames),
+		Name:            m.Name,
+		Groups:          []string{m.Group},
+		Criteria:        criteria,
+		TagProjection:   BuildTagProjection(m, tagNames),
 		FieldProjection: BuildFieldProjection(fieldNames),
 	}
 }
@@ -117,7 +123,7 @@ func generateLeafErrorCases() []*TestCase {
 	return []*TestCase{
 		// IN with scalar (should use array)
 		{
-			Name: "gen_err_in_scalar",
+			Name:    "gen_err_in_scalar",
 			Measure: FindMeasure("service_cpm_minute"),
 			Request: &measurev1.QueryRequest{
 				Name:   "service_cpm_minute",
@@ -125,7 +131,7 @@ func generateLeafErrorCases() []*TestCase {
 				Criteria: BuildCriteriaFromCondition(
 					BuildCondition("id", modelv1.Condition_BINARY_OP_IN, TagValueStr("svc1")),
 				),
-				TagProjection:  BuildTagProjection(FindMeasure("service_cpm_minute"), []string{"id", "entity_id"}),
+				TagProjection:   BuildTagProjection(FindMeasure("service_cpm_minute"), []string{"id", "entity_id"}),
 				FieldProjection: BuildFieldProjection([]string{"total", "value"}),
 			},
 			QL:      "SELECT id, entity_id, total, value FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id IN ('svc1')",
@@ -133,7 +139,7 @@ func generateLeafErrorCases() []*TestCase {
 		},
 		// MATCH with int value
 		{
-			Name: "gen_err_match_int",
+			Name:    "gen_err_match_int",
 			Measure: FindMeasure("service_traffic"),
 			Request: &measurev1.QueryRequest{
 				Name:   "service_traffic",
diff --git a/test/cases/measure/cmd/generate/layer2_tree.go b/test/cases/measure/cmd/generate/layer2_tree.go
index 44a54adb3..240683e01 100644
--- a/test/cases/measure/cmd/generate/layer2_tree.go
+++ b/test/cases/measure/cmd/generate/layer2_tree.go
@@ -33,9 +33,9 @@ func GenerateLayer2() []*TestCase {
 	}
 
 	type depthConfig struct {
-		depth    int
-		rootOp   modelv1.LogicalExpression_LogicalOp
-		suffix   string
+		suffix string
+		depth  int
+		rootOp modelv1.LogicalExpression_LogicalOp
 	}
 
 	configs := []depthConfig{
@@ -55,10 +55,10 @@ func GenerateLayer2() []*TestCase {
 			continue
 		}
 		req := &measurev1.QueryRequest{
-			Name:           m.Name,
-			Groups:         []string{m.Group},
-			Criteria:       criteria,
-			TagProjection:  BuildTagProjection(m, []string{"id", "entity_id"}),
+			Name:            m.Name,
+			Groups:          []string{m.Group},
+			Criteria:        criteria,
+			TagProjection:   BuildTagProjection(m, []string{"id", "entity_id"}),
 			FieldProjection: BuildFieldProjection([]string{"total", "value"}),
 		}
 		ql, qlErr := RenderQL(req)
diff --git a/test/cases/measure/cmd/generate/layer3_features.go b/test/cases/measure/cmd/generate/layer3_features.go
index d92d89e51..4670347fb 100644
--- a/test/cases/measure/cmd/generate/layer3_features.go
+++ b/test/cases/measure/cmd/generate/layer3_features.go
@@ -25,6 +25,8 @@ import (
 	modelv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
 )
 
+const strNone = "none"
+
 // GenerateLayer3 produces test cases for query feature combinations.
 // Uses pairwise testing across AggFunction, TopN, GroupBy, OrderBy, and CriteriaPresence.
 func GenerateLayer3() []*TestCase {
@@ -34,31 +36,31 @@ func GenerateLayer3() []*TestCase {
 	}
 
 	params := map[string][]string{
-		"agg":    {"MEAN", "MAX", "MIN", "COUNT", "SUM", "none"},
-		"top":    {"desc", "asc", "none"},
+		"agg":    {"MEAN", "MAX", "MIN", "COUNT", "SUM", strNone},
+		"top":    {"desc", "asc", strNone},
 		"group":  {"true", "false"},
-		"order":  {"asc", "desc", "none"},
-		"filter": {"none", "eq"},
+		"order":  {"asc", "desc", strNone},
+		"filter": {strNone, "eq"},
 	}
 
 	constraints := []ConstraintFunc{
 		// TopN requires GroupBy
 		func(tv TestVector) bool {
-			if tv["top"] != "none" && tv["group"] == "false" {
+			if tv["top"] != strNone && tv["group"] == "false" {
 				return false
 			}
 			return true
 		},
 		// Agg (non-none) requires GroupBy
 		func(tv TestVector) bool {
-			if tv["agg"] != "none" && tv["group"] == "false" {
+			if tv["agg"] != strNone && tv["group"] == "false" {
 				return false
 			}
 			return true
 		},
 		// TopN requires Agg
 		func(tv TestVector) bool {
-			if tv["top"] != "none" && tv["agg"] == "none" {
+			if tv["top"] != strNone && tv["agg"] == strNone {
 				return false
 			}
 			return true
@@ -80,21 +82,21 @@ func GenerateLayer3() []*TestCase {
 			continue
 		}
 
-		disOrder := tv["order"] == "none" && tv["group"] == "true"
+		disOrder := tv["order"] == strNone && tv["group"] == "true"
 		nameParts := []string{}
-		if tv["agg"] != "none" {
+		if tv["agg"] != strNone {
 			nameParts = append(nameParts, strings.ToLower(tv["agg"]))
 		}
-		if tv["top"] != "none" {
+		if tv["top"] != strNone {
 			nameParts = append(nameParts, "top_"+tv["top"])
 		}
 		if tv["group"] == "true" {
 			nameParts = append(nameParts, "group")
 		}
-		if tv["order"] != "none" {
+		if tv["order"] != strNone {
 			nameParts = append(nameParts, "order_"+tv["order"])
 		}
-		if tv["filter"] != "none" {
+		if tv["filter"] != strNone {
 			nameParts = append(nameParts, "filter")
 		}
 		if len(nameParts) == 0 {
@@ -139,7 +141,7 @@ func buildLayer3Request(m *Measure, tv TestVector) *measurev1.QueryRequest {
 	}
 
 	// Aggregation
-	if tv["agg"] != "none" {
+	if tv["agg"] != strNone {
 		req.Agg = &measurev1.QueryRequest_Aggregation{
 			Function:  parseAggFunction(tv["agg"]),
 			FieldName: "value",
@@ -147,7 +149,7 @@ func buildLayer3Request(m *Measure, tv TestVector) *measurev1.QueryRequest {
 	}
 
 	// TopN
-	if tv["top"] != "none" {
+	if tv["top"] != strNone {
 		sortVal := modelv1.Sort_SORT_DESC
 		if tv["top"] == "asc" {
 			sortVal = modelv1.Sort_SORT_ASC
@@ -200,10 +202,10 @@ func ensureAggCoverage(vectors []TestVector) []TestVector {
 		// Add a vector with this agg function, group=true, varied other params
 		vectors = append(vectors, TestVector{
 			"agg":    aggFn,
-			"top":    "none",
+			"top":    strNone,
 			"group":  "true",
 			"order":  "desc",
-			"filter": "none",
+			"filter": strNone,
 		})
 	}
 	return vectors
diff --git a/test/cases/measure/cmd/generate/main.go b/test/cases/measure/cmd/generate/main.go
index 46140ac63..4efc19276 100644
--- a/test/cases/measure/cmd/generate/main.go
+++ b/test/cases/measure/cmd/generate/main.go
@@ -100,7 +100,7 @@ func runGenerate(outputDir string) {
 	for _, tc := range filtered {
 		// Write .ql file
 		qlPath := filepath.Join(inputDirPath, tc.Name+".ql")
-		if writeErr := os.WriteFile(qlPath, tc.QLFileContent(), 0o644); writeErr != nil {
+		if writeErr := os.WriteFile(qlPath, tc.QLFileContent(), 0o600); writeErr != nil {
 			fmt.Fprintf(os.Stderr, "Error writing %s: %v\n", qlPath, writeErr)
 			continue
 		}
@@ -112,7 +112,7 @@ func runGenerate(outputDir string) {
 			continue
 		}
 		yamlPath := filepath.Join(inputDirPath, tc.Name+".yaml")
-		if writeErr := os.WriteFile(yamlPath, yamlContent, 0o644); writeErr != nil {
+		if writeErr := os.WriteFile(yamlPath, yamlContent, 0o600); writeErr != nil {
 			fmt.Fprintf(os.Stderr, "Error writing %s: %v\n", yamlPath, writeErr)
 			continue
 		}
@@ -122,7 +122,7 @@ func runGenerate(outputDir string) {
 			// Write placeholder want file (to be filled by capture)
 			wantPath := filepath.Join(wantDirPath, tc.Name+".yaml")
 			placeholder := []byte("# Placeholder - run 'capture' subcommand to fill this file\n")
-			if writeErr := os.WriteFile(wantPath, placeholder, 0o644); writeErr != nil {
+			if writeErr := os.WriteFile(wantPath, placeholder, 0o600); writeErr != nil {
 				fmt.Fprintf(os.Stderr, "Error writing %s: %v\n", wantPath, writeErr)
 				continue
 			}
diff --git a/test/cases/measure/cmd/generate/ql_renderer.go b/test/cases/measure/cmd/generate/ql_renderer.go
index 15a8e55d6..c8dc26e76 100644
--- a/test/cases/measure/cmd/generate/ql_renderer.go
+++ b/test/cases/measure/cmd/generate/ql_renderer.go
@@ -31,10 +31,11 @@ func RenderQL(req *measurev1.QueryRequest) (string, error) {
 
 	// SELECT projection
 	projection := renderProjection(req)
-	parts = append(parts, fmt.Sprintf("SELECT %s FROM MEASURE %s IN %s", projection, req.GetName(), strings.Join(req.GetGroups(), ",")))
-
 	// TIME — always required by the BydbQL service; actual range is injected by the test framework
-	parts = append(parts, "TIME > '-15m'")
+	parts = append(parts,
+		fmt.Sprintf("SELECT %s FROM MEASURE %s IN %s", projection, req.GetName(), strings.Join(req.GetGroups(), ",")),
+		"TIME > '-15m'",
+	)
 
 	// WHERE
 	if req.GetCriteria() != nil {
@@ -50,9 +51,7 @@ func RenderQL(req *measurev1.QueryRequest) (string, error) {
 		var groupCols []string
 		if tp := req.GetGroupBy().GetTagProjection(); tp != nil {
 			for _, family := range tp.GetTagFamilies() {
-				for _, tag := range family.GetTags() {
-					groupCols = append(groupCols, tag)
-				}
+				groupCols = append(groupCols, family.GetTags()...)
 			}
 		}
 		if fn := req.GetGroupBy().GetFieldName(); fn != "" {
@@ -99,9 +98,7 @@ func renderProjection(req *measurev1.QueryRequest) string {
 	// Tag projection
 	if tp := req.GetTagProjection(); tp != nil {
 		for _, family := range tp.GetTagFamilies() {
-			for _, tag := range family.GetTags() {
-				cols = append(cols, tag)
-			}
+			cols = append(cols, family.GetTags()...)
 		}
 	}
 
diff --git a/test/cases/measure/cmd/generate/seed.go b/test/cases/measure/cmd/generate/seed.go
index 8bd72b965..2f76e2370 100644
--- a/test/cases/measure/cmd/generate/seed.go
+++ b/test/cases/measure/cmd/generate/seed.go
@@ -38,9 +38,9 @@ const (
 // TagDef defines a tag in a measure schema.
 type TagDef struct {
 	Name     string
+	Family   string
 	Type     TagType
-	Family   string // tag family name (e.g., "default")
-	IsEntity bool   // whether this tag is the entity
+	IsEntity bool
 }
 
 // FieldDef defines a field in a measure schema.
@@ -124,9 +124,18 @@ func serviceTraffic() Measure {
 		},
 		Fields: []FieldDef{},
 		DataPoints: []DataPoint{
-			{TagValues: map[string]interface{}{"id": "1", "service_id": "service_1", "name": "service_name_1", "short_name": "service_short_name_1", "service_group": "group1", "layer": int64(1)}},
-			{TagValues: map[string]interface{}{"id": "2", "service_id": "service_2", "name": "service_name_2", "short_name": "service_short_name_2", "service_group": "group1", "layer": int64(2)}},
-			{TagValues: map[string]interface{}{"id": "3", "service_id": "service_3", "name": "service_name_3", "short_name": "service_short_name_3", "service_group": "group1", "layer": int64(0)}},
+			{TagValues: map[string]interface{}{
+				"id": "1", "service_id": "service_1", "name": "service_name_1",
+				"short_name": "service_short_name_1", "service_group": "group1", "layer": int64(1),
+			}},
+			{TagValues: map[string]interface{}{
+				"id": "2", "service_id": "service_2", "name": "service_name_2",
+				"short_name": "service_short_name_2", "service_group": "group1", "layer": int64(2),
+			}},
+			{TagValues: map[string]interface{}{
+				"id": "3", "service_id": "service_3", "name": "service_name_3",
+				"short_name": "service_short_name_3", "service_group": "group1", "layer": int64(0),
+			}},
 		},
 	}
 }
@@ -165,9 +174,18 @@ func instanceCPUCPUMinute() Measure {
 			{Name: "value", Kind: FieldKindFloat},
 		},
 		DataPoints: []DataPoint{
-			{TagValues: map[string]interface{}{"service_id": "1", "entity_id": "entity_1"}, FieldValues: map[string]interface{}{"summation": 18.1, "count": int64(1), "value": 18.1}},
-			{TagValues: map[string]interface{}{"service_id": "4", "entity_id": "entity_2"}, FieldValues: map[string]interface{}{"summation": 100.4, "count": int64(2), "value": 50.2}},
-			{TagValues: map[string]interface{}{"service_id": "5", "entity_id": "entity_2"}, FieldValues: map[string]interface{}{"summation": 100.0, "count": int64(3), "value": 33.333}},
+			{
+				TagValues:   map[string]interface{}{"service_id": "1", "entity_id": "entity_1"},
+				FieldValues: map[string]interface{}{"summation": 18.1, "count": int64(1), "value": 18.1},
+			},
+			{
+				TagValues:   map[string]interface{}{"service_id": "4", "entity_id": "entity_2"},
+				FieldValues: map[string]interface{}{"summation": 100.4, "count": int64(2), "value": 50.2},
+			},
+			{
+				TagValues:   map[string]interface{}{"service_id": "5", "entity_id": "entity_2"},
+				FieldValues: map[string]interface{}{"summation": 100.0, "count": int64(3), "value": 33.333},
+			},
 		},
 	}
 }
diff --git a/test/cases/measure/cmd/generate/types.go b/test/cases/measure/cmd/generate/types.go
index b4f33f2ed..20f11fc1e 100644
--- a/test/cases/measure/cmd/generate/types.go
+++ b/test/cases/measure/cmd/generate/types.go
@@ -18,22 +18,23 @@
 package main
 
 import (
-	measurev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/measure/v1"
-	modelv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
 	"google.golang.org/protobuf/encoding/protojson"
 	"sigs.k8s.io/yaml"
+
+	measurev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/measure/v1"
+	modelv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
 )
 
 // TestCase represents a generated test case with all its artifacts.
 type TestCase struct {
-	Name      string
 	Measure   *Measure
 	Request   *measurev1.QueryRequest
-	QL        string // rendered QL query string
+	Name      string
+	QL        string
+	Duration  string
 	WantErr   bool
 	WantEmpty bool
 	DisOrder  bool
-	Duration  string // Go duration expression for helpers.Args
 }
 
 const licenseHeader = "# Licensed to Apache Software Foundation (ASF) under one or more contributor\n" +
diff --git a/test/cases/measure/measure.go b/test/cases/measure/measure.go
index 43ad0617c..c948ff5eb 100644
--- a/test/cases/measure/measure.go
+++ b/test/cases/measure/measure.go
@@ -111,37 +111,64 @@ var measureEntries = []any{
 	g.Entry("gen: leaf LE string", helpers.Args{Input: "gen_leaf_le_str", Want: "gen_leaf_le_str", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
 	g.Entry("gen: leaf GE string", helpers.Args{Input: "gen_leaf_ge_str", Want: "gen_leaf_ge_str", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
 	g.Entry("gen: leaf IN str array", helpers.Args{Input: "gen_leaf_in_str_array", Want: "gen_leaf_in_str_array", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
-	g.Entry("gen: leaf NOT IN str array", helpers.Args{Input: "gen_leaf_not_in_str_array", Want: "gen_leaf_not_in_str_array", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: leaf NOT IN str array",
+		helpers.Args{Input: "gen_leaf_not_in_str_array", Want: "gen_leaf_not_in_str_array", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
 	g.Entry("gen: leaf EQ int", helpers.Args{Input: "gen_leaf_eq_int", Want: "gen_leaf_eq_int", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
 	g.Entry("gen: leaf NE int", helpers.Args{Input: "gen_leaf_ne_int", Want: "gen_leaf_ne_int", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
 	g.Entry("gen: leaf LT int", helpers.Args{Input: "gen_leaf_lt_int", Want: "gen_leaf_lt_int", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
 	g.Entry("gen: leaf GT int", helpers.Args{Input: "gen_leaf_gt_int", Want: "gen_leaf_gt_int", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
 	g.Entry("gen: leaf LE int", helpers.Args{Input: "gen_leaf_le_int", Want: "gen_leaf_le_int", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
 	g.Entry("gen: leaf GE int", helpers.Args{Input: "gen_leaf_ge_int", Want: "gen_leaf_ge_int", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
-	g.Entry("gen: leaf IN int array", helpers.Args{Input: "gen_leaf_in_int_array", Want: "gen_leaf_in_int_array", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
-	g.Entry("gen: leaf NOT IN int array", helpers.Args{Input: "gen_leaf_not_in_int_array", Want: "gen_leaf_not_in_int_array", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("gen: leaf IN int array",
+		helpers.Args{Input: "gen_leaf_in_int_array", Want: "gen_leaf_in_int_array", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("gen: leaf NOT IN int array",
+		helpers.Args{Input: "gen_leaf_not_in_int_array", Want: "gen_leaf_not_in_int_array", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
 	g.Entry("gen: leaf MATCH string", helpers.Args{Input: "gen_leaf_match_str", Want: "gen_leaf_match_str", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
 	g.Entry("gen: err IN with scalar value", helpers.Args{Input: "gen_err_in_scalar", Duration: 25 * time.Minute, Offset: -20 * time.Minute, WantErr: true}),
 	g.Entry("gen: err MATCH on int field", helpers.Args{Input: "gen_err_match_int", Duration: 25 * time.Minute, Offset: -20 * time.Minute, WantErr: true}),
 	// Generated test cases (Layer 2: tree conditions)
 	g.Entry("gen: tree depth 1 leaf", helpers.Args{Input: "gen_tree_depth1_leaf", Want: "gen_tree_depth1_leaf", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
-	g.Entry("gen: tree depth 2 AND", helpers.Args{Input: "gen_tree_depth2_and", Want: "gen_tree_depth2_and", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
-	g.Entry("gen: tree depth 2 OR", helpers.Args{Input: "gen_tree_depth2_or", Want: "gen_tree_depth2_or", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
-	g.Entry("gen: tree depth 3 AND/OR", helpers.Args{Input: "gen_tree_depth3_and_or", Want: "gen_tree_depth3_and_or", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
-	g.Entry("gen: tree depth 3 OR/AND", helpers.Args{Input: "gen_tree_depth3_or_and", Want: "gen_tree_depth3_or_and", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
-	g.Entry("gen: tree depth 5 deep AND", helpers.Args{Input: "gen_tree_depth5_deep_and", Want: "gen_tree_depth5_deep_and", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: tree depth 2 AND",
+		helpers.Args{Input: "gen_tree_depth2_and", Want: "gen_tree_depth2_and", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: tree depth 2 OR",
+		helpers.Args{Input: "gen_tree_depth2_or", Want: "gen_tree_depth2_or", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: tree depth 3 AND/OR",
+		helpers.Args{Input: "gen_tree_depth3_and_or", Want: "gen_tree_depth3_and_or", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: tree depth 3 OR/AND",
+		helpers.Args{Input: "gen_tree_depth3_or_and", Want: "gen_tree_depth3_or_and", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: tree depth 5 deep AND",
+		helpers.Args{Input: "gen_tree_depth5_deep_and", Want: "gen_tree_depth5_deep_and", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
 	g.Entry("gen: tree depth 5 deep OR", helpers.Args{Input: "gen_tree_depth5_deep_or", WantErr: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
 	// Generated test cases (Layer 3: feature combinations)
-	g.Entry("gen: mean top desc group order asc", helpers.Args{Input: "gen_feat_mean_top_desc_group_order_asc_0", Want: "gen_feat_mean_top_desc_group_order_asc_0", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
-	g.Entry("gen: mean top asc group order desc filter", helpers.Args{Input: "gen_feat_mean_top_asc_group_order_desc_filter_1", Want: "gen_feat_mean_top_asc_group_order_desc_filter_1", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
-	g.Entry("gen: mean group", helpers.Args{Input: "gen_feat_mean_group_2", Want: "gen_feat_mean_group_2", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
-	g.Entry("gen: mean top desc group order desc", helpers.Args{Input: "gen_feat_mean_top_desc_group_order_desc_3", Want: "gen_feat_mean_top_desc_group_order_desc_3", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
-	g.Entry("gen: mean top asc group order asc", helpers.Args{Input: "gen_feat_mean_top_asc_group_order_asc_4", Want: "gen_feat_mean_top_asc_group_order_asc_4", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
-	g.Entry("gen: mean group order asc", helpers.Args{Input: "gen_feat_mean_group_order_asc_5", Want: "gen_feat_mean_group_order_asc_5", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
-	g.Entry("gen: max group order desc", helpers.Args{Input: "gen_feat_max_group_order_desc_6", Want: "gen_feat_max_group_order_desc_6", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
-	g.Entry("gen: min group order desc", helpers.Args{Input: "gen_feat_min_group_order_desc_7", Want: "gen_feat_min_group_order_desc_7", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
-	g.Entry("gen: count group order desc", helpers.Args{Input: "gen_feat_count_group_order_desc_8", Want: "gen_feat_count_group_order_desc_8", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
-	g.Entry("gen: sum group order desc", helpers.Args{Input: "gen_feat_sum_group_order_desc_9", Want: "gen_feat_sum_group_order_desc_9", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: mean top desc group order asc",
+		helpers.Args{
+			Input: "gen_feat_mean_top_desc_group_order_asc_0", Want: "gen_feat_mean_top_desc_group_order_asc_0",
+			Duration: 25 * time.Minute, Offset: -20 * time.Minute,
+		}),
+	g.Entry("gen: mean top asc group order desc filter",
+		helpers.Args{
+			Input: "gen_feat_mean_top_asc_group_order_desc_filter_1", Want: "gen_feat_mean_top_asc_group_order_desc_filter_1",
+			Duration: 25 * time.Minute, Offset: -20 * time.Minute,
+		}),
+	g.Entry("gen: mean group",
+		helpers.Args{Input: "gen_feat_mean_group_2", Want: "gen_feat_mean_group_2", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: mean top desc group order desc",
+		helpers.Args{
+			Input: "gen_feat_mean_top_desc_group_order_desc_3", Want: "gen_feat_mean_top_desc_group_order_desc_3",
+			Duration: 25 * time.Minute, Offset: -20 * time.Minute,
+		}),
+	g.Entry("gen: mean top asc group order asc",
+		helpers.Args{Input: "gen_feat_mean_top_asc_group_order_asc_4", Want: "gen_feat_mean_top_asc_group_order_asc_4", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: mean group order asc",
+		helpers.Args{Input: "gen_feat_mean_group_order_asc_5", Want: "gen_feat_mean_group_order_asc_5", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: max group order desc",
+		helpers.Args{Input: "gen_feat_max_group_order_desc_6", Want: "gen_feat_max_group_order_desc_6", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: min group order desc",
+		helpers.Args{Input: "gen_feat_min_group_order_desc_7", Want: "gen_feat_min_group_order_desc_7", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: count group order desc",
+		helpers.Args{Input: "gen_feat_count_group_order_desc_8", Want: "gen_feat_count_group_order_desc_8", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: sum group order desc",
+		helpers.Args{Input: "gen_feat_sum_group_order_desc_9", Want: "gen_feat_sum_group_order_desc_9", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
 }
 
 // RegisterTable registers the measure test table with the given description.


(skywalking-banyandb) 03/04: chore(test): add Apache license headers to generated test case files

Posted by ha...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hanahmily pushed a commit to branch test/query
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git

commit 5d963b4701a950b2ea62dccd18624cfab4191917
Author: Hongtao Gao <ha...@gmail.com>
AuthorDate: Wed Apr 15 05:06:31 2026 +0000

    chore(test): add Apache license headers to generated test case files
    
    Add missing license headers to all generated .ql, input/*.yaml, and
    want/*.yaml test case files. Fix the generator (types.go) to include
    the header in future QLFileContent() and InputYAMLContent() output.
    Also add // headers to cmd/generate and cmd/capture Go source files.
    
    via [HAPI](https://hapi.run)
    
    Co-Authored-By: HAPI <no...@hapi.run>
---
 test/cases/measure/cmd/capture/capture_test.go     | 17 ++++++++
 test/cases/measure/cmd/generate/capture.go         | 17 ++++++++
 test/cases/measure/cmd/generate/entry.go           | 17 ++++++++
 test/cases/measure/cmd/generate/gap.go             | 17 ++++++++
 test/cases/measure/cmd/generate/layer1_leaf.go     | 17 ++++++++
 test/cases/measure/cmd/generate/layer2_tree.go     | 17 ++++++++
 test/cases/measure/cmd/generate/layer3_features.go | 17 ++++++++
 test/cases/measure/cmd/generate/main.go            | 17 ++++++++
 test/cases/measure/cmd/generate/pairwise.go        | 17 ++++++++
 test/cases/measure/cmd/generate/ql_renderer.go     | 17 ++++++++
 test/cases/measure/cmd/generate/seed.go            | 17 ++++++++
 test/cases/measure/cmd/generate/types.go           | 46 ++++++++++++++++++++--
 test/cases/measure/data/input/gen_err_in_scalar.ql | 17 ++++++++
 .../measure/data/input/gen_err_in_scalar.yaml      | 17 ++++++++
 test/cases/measure/data/input/gen_err_match_int.ql | 17 ++++++++
 .../measure/data/input/gen_err_match_int.yaml      | 17 ++++++++
 .../input/gen_feat_count_group_order_desc_8.ql     | 17 ++++++++
 .../input/gen_feat_count_group_order_desc_8.yaml   | 17 ++++++++
 .../data/input/gen_feat_max_group_order_desc_6.ql  | 17 ++++++++
 .../input/gen_feat_max_group_order_desc_6.yaml     | 17 ++++++++
 .../measure/data/input/gen_feat_mean_group_2.ql    | 17 ++++++++
 .../measure/data/input/gen_feat_mean_group_2.yaml  | 17 ++++++++
 .../data/input/gen_feat_mean_group_order_asc_5.ql  | 17 ++++++++
 .../input/gen_feat_mean_group_order_asc_5.yaml     | 17 ++++++++
 .../gen_feat_mean_top_asc_group_order_asc_4.ql     | 17 ++++++++
 .../gen_feat_mean_top_asc_group_order_asc_4.yaml   | 17 ++++++++
 ..._feat_mean_top_asc_group_order_desc_filter_1.ql | 17 ++++++++
 ...eat_mean_top_asc_group_order_desc_filter_1.yaml | 17 ++++++++
 .../gen_feat_mean_top_desc_group_order_asc_0.ql    | 17 ++++++++
 .../gen_feat_mean_top_desc_group_order_asc_0.yaml  | 17 ++++++++
 .../gen_feat_mean_top_desc_group_order_desc_3.ql   | 17 ++++++++
 .../gen_feat_mean_top_desc_group_order_desc_3.yaml | 17 ++++++++
 .../data/input/gen_feat_min_group_order_desc_7.ql  | 17 ++++++++
 .../input/gen_feat_min_group_order_desc_7.yaml     | 17 ++++++++
 .../data/input/gen_feat_sum_group_order_desc_9.ql  | 17 ++++++++
 .../input/gen_feat_sum_group_order_desc_9.yaml     | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_eq_int.ql   | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_eq_int.yaml | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_eq_null.ql  | 17 ++++++++
 .../cases/measure/data/input/gen_leaf_eq_null.yaml | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_eq_str.ql   | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_eq_str.yaml | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_ge_int.ql   | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_ge_int.yaml | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_ge_str.ql   | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_ge_str.yaml | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_gt_int.ql   | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_gt_int.yaml | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_gt_str.ql   | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_gt_str.yaml | 17 ++++++++
 .../measure/data/input/gen_leaf_in_int_array.ql    | 17 ++++++++
 .../measure/data/input/gen_leaf_in_int_array.yaml  | 17 ++++++++
 .../measure/data/input/gen_leaf_in_str_array.ql    | 17 ++++++++
 .../measure/data/input/gen_leaf_in_str_array.yaml  | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_le_int.ql   | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_le_int.yaml | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_le_str.ql   | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_le_str.yaml | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_lt_int.ql   | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_lt_int.yaml | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_lt_str.ql   | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_lt_str.yaml | 17 ++++++++
 .../cases/measure/data/input/gen_leaf_match_str.ql | 17 ++++++++
 .../measure/data/input/gen_leaf_match_str.yaml     | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_ne_int.ql   | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_ne_int.yaml | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_ne_null.ql  | 17 ++++++++
 .../cases/measure/data/input/gen_leaf_ne_null.yaml | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_ne_str.ql   | 17 ++++++++
 test/cases/measure/data/input/gen_leaf_ne_str.yaml | 17 ++++++++
 .../data/input/gen_leaf_not_in_int_array.ql        | 17 ++++++++
 .../data/input/gen_leaf_not_in_int_array.yaml      | 17 ++++++++
 .../data/input/gen_leaf_not_in_str_array.ql        | 17 ++++++++
 .../data/input/gen_leaf_not_in_str_array.yaml      | 17 ++++++++
 .../measure/data/input/gen_tree_depth1_leaf.ql     | 17 ++++++++
 .../measure/data/input/gen_tree_depth1_leaf.yaml   | 17 ++++++++
 .../measure/data/input/gen_tree_depth2_and.ql      | 17 ++++++++
 .../measure/data/input/gen_tree_depth2_and.yaml    | 17 ++++++++
 .../cases/measure/data/input/gen_tree_depth2_or.ql | 17 ++++++++
 .../measure/data/input/gen_tree_depth2_or.yaml     | 17 ++++++++
 .../measure/data/input/gen_tree_depth3_and_or.ql   | 17 ++++++++
 .../measure/data/input/gen_tree_depth3_and_or.yaml | 17 ++++++++
 .../measure/data/input/gen_tree_depth3_or_and.ql   | 17 ++++++++
 .../measure/data/input/gen_tree_depth3_or_and.yaml | 17 ++++++++
 .../measure/data/input/gen_tree_depth5_deep_and.ql | 17 ++++++++
 .../data/input/gen_tree_depth5_deep_and.yaml       | 17 ++++++++
 .../measure/data/input/gen_tree_depth5_deep_or.ql  | 17 ++++++++
 .../data/input/gen_tree_depth5_deep_or.yaml        | 17 ++++++++
 .../want/gen_feat_count_group_order_desc_8.yaml    | 17 ++++++++
 .../data/want/gen_feat_max_group_order_desc_6.yaml | 17 ++++++++
 .../measure/data/want/gen_feat_mean_group_2.yaml   | 17 ++++++++
 .../data/want/gen_feat_mean_group_order_asc_5.yaml | 17 ++++++++
 .../gen_feat_mean_top_asc_group_order_asc_4.yaml   | 17 ++++++++
 ...eat_mean_top_asc_group_order_desc_filter_1.yaml | 17 ++++++++
 .../gen_feat_mean_top_desc_group_order_asc_0.yaml  | 17 ++++++++
 .../gen_feat_mean_top_desc_group_order_desc_3.yaml | 17 ++++++++
 .../data/want/gen_feat_min_group_order_desc_7.yaml | 17 ++++++++
 .../data/want/gen_feat_sum_group_order_desc_9.yaml | 17 ++++++++
 test/cases/measure/data/want/gen_leaf_eq_int.yaml  | 17 ++++++++
 test/cases/measure/data/want/gen_leaf_eq_str.yaml  | 17 ++++++++
 test/cases/measure/data/want/gen_leaf_ge_int.yaml  | 17 ++++++++
 test/cases/measure/data/want/gen_leaf_ge_str.yaml  | 17 ++++++++
 test/cases/measure/data/want/gen_leaf_gt_int.yaml  | 17 ++++++++
 test/cases/measure/data/want/gen_leaf_gt_str.yaml  | 17 ++++++++
 .../measure/data/want/gen_leaf_in_int_array.yaml   | 17 ++++++++
 .../measure/data/want/gen_leaf_in_str_array.yaml   | 17 ++++++++
 test/cases/measure/data/want/gen_leaf_le_int.yaml  | 17 ++++++++
 test/cases/measure/data/want/gen_leaf_le_str.yaml  | 17 ++++++++
 test/cases/measure/data/want/gen_leaf_lt_int.yaml  | 17 ++++++++
 test/cases/measure/data/want/gen_leaf_lt_str.yaml  | 17 ++++++++
 .../measure/data/want/gen_leaf_match_str.yaml      | 17 ++++++++
 test/cases/measure/data/want/gen_leaf_ne_int.yaml  | 17 ++++++++
 test/cases/measure/data/want/gen_leaf_ne_str.yaml  | 17 ++++++++
 .../data/want/gen_leaf_not_in_int_array.yaml       | 17 ++++++++
 .../data/want/gen_leaf_not_in_str_array.yaml       | 17 ++++++++
 .../measure/data/want/gen_tree_depth1_leaf.yaml    | 17 ++++++++
 .../measure/data/want/gen_tree_depth2_and.yaml     | 17 ++++++++
 .../measure/data/want/gen_tree_depth2_or.yaml      | 17 ++++++++
 .../measure/data/want/gen_tree_depth3_and_or.yaml  | 17 ++++++++
 .../measure/data/want/gen_tree_depth3_or_and.yaml  | 17 ++++++++
 .../data/want/gen_tree_depth5_deep_and.yaml        | 17 ++++++++
 121 files changed, 2082 insertions(+), 4 deletions(-)

diff --git a/test/cases/measure/cmd/capture/capture_test.go b/test/cases/measure/cmd/capture/capture_test.go
index 8774619dc..113d479f4 100644
--- a/test/cases/measure/cmd/capture/capture_test.go
+++ b/test/cases/measure/cmd/capture/capture_test.go
@@ -1,3 +1,20 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
 // Package capture_test runs a standalone server, loads seed data, executes
 // generated queries, and writes responses as want/*.yaml files.
 //
diff --git a/test/cases/measure/cmd/generate/capture.go b/test/cases/measure/cmd/generate/capture.go
index 0f0fc9029..91bd4c2de 100644
--- a/test/cases/measure/cmd/generate/capture.go
+++ b/test/cases/measure/cmd/generate/capture.go
@@ -1,3 +1,20 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
 package main
 
 import (
diff --git a/test/cases/measure/cmd/generate/entry.go b/test/cases/measure/cmd/generate/entry.go
index 1270dfd81..ac77b7f48 100644
--- a/test/cases/measure/cmd/generate/entry.go
+++ b/test/cases/measure/cmd/generate/entry.go
@@ -1,3 +1,20 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
 package main
 
 import (
diff --git a/test/cases/measure/cmd/generate/gap.go b/test/cases/measure/cmd/generate/gap.go
index bd4b77ce0..1aa5e78ef 100644
--- a/test/cases/measure/cmd/generate/gap.go
+++ b/test/cases/measure/cmd/generate/gap.go
@@ -1,3 +1,20 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
 package main
 
 import (
diff --git a/test/cases/measure/cmd/generate/layer1_leaf.go b/test/cases/measure/cmd/generate/layer1_leaf.go
index 76fc65c13..51b0c3e33 100644
--- a/test/cases/measure/cmd/generate/layer1_leaf.go
+++ b/test/cases/measure/cmd/generate/layer1_leaf.go
@@ -1,3 +1,20 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
 package main
 
 import (
diff --git a/test/cases/measure/cmd/generate/layer2_tree.go b/test/cases/measure/cmd/generate/layer2_tree.go
index 107b8eaf3..44a54adb3 100644
--- a/test/cases/measure/cmd/generate/layer2_tree.go
+++ b/test/cases/measure/cmd/generate/layer2_tree.go
@@ -1,3 +1,20 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
 package main
 
 import (
diff --git a/test/cases/measure/cmd/generate/layer3_features.go b/test/cases/measure/cmd/generate/layer3_features.go
index 12bc95365..d92d89e51 100644
--- a/test/cases/measure/cmd/generate/layer3_features.go
+++ b/test/cases/measure/cmd/generate/layer3_features.go
@@ -1,3 +1,20 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
 package main
 
 import (
diff --git a/test/cases/measure/cmd/generate/main.go b/test/cases/measure/cmd/generate/main.go
index d70ffd6fb..46140ac63 100644
--- a/test/cases/measure/cmd/generate/main.go
+++ b/test/cases/measure/cmd/generate/main.go
@@ -1,3 +1,20 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
 package main
 
 import (
diff --git a/test/cases/measure/cmd/generate/pairwise.go b/test/cases/measure/cmd/generate/pairwise.go
index fb4182a0e..2fc2da62b 100644
--- a/test/cases/measure/cmd/generate/pairwise.go
+++ b/test/cases/measure/cmd/generate/pairwise.go
@@ -1,3 +1,20 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
 package main
 
 // Pair holds a parameter-value pair for pairwise testing.
diff --git a/test/cases/measure/cmd/generate/ql_renderer.go b/test/cases/measure/cmd/generate/ql_renderer.go
index a019656ab..15a8e55d6 100644
--- a/test/cases/measure/cmd/generate/ql_renderer.go
+++ b/test/cases/measure/cmd/generate/ql_renderer.go
@@ -1,3 +1,20 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
 package main
 
 import (
diff --git a/test/cases/measure/cmd/generate/seed.go b/test/cases/measure/cmd/generate/seed.go
index e67816ee0..8bd72b965 100644
--- a/test/cases/measure/cmd/generate/seed.go
+++ b/test/cases/measure/cmd/generate/seed.go
@@ -1,3 +1,20 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
 package main
 
 // TagType represents the type of a tag in the measure schema.
diff --git a/test/cases/measure/cmd/generate/types.go b/test/cases/measure/cmd/generate/types.go
index c2d6cf4f5..b4f33f2ed 100644
--- a/test/cases/measure/cmd/generate/types.go
+++ b/test/cases/measure/cmd/generate/types.go
@@ -1,3 +1,20 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
 package main
 
 import (
@@ -19,19 +36,40 @@ type TestCase struct {
 	Duration  string // Go duration expression for helpers.Args
 }
 
-// WriteQLFile writes the .ql file content.
+const licenseHeader = "# Licensed to Apache Software Foundation (ASF) under one or more contributor\n" +
+	"# license agreements. See the NOTICE file distributed with\n" +
+	"# this work for additional information regarding copyright\n" +
+	"# ownership. Apache Software Foundation (ASF) licenses this file to you under\n" +
+	"# the Apache License, Version 2.0 (the \"License\"); you may\n" +
+	"# not use this file except in compliance with the License.\n" +
+	"# You may obtain a copy of the License at\n" +
+	"#\n" +
+	"#     http://www.apache.org/licenses/LICENSE-2.0\n" +
+	"#\n" +
+	"# Unless required by applicable law or agreed to in writing,\n" +
+	"# software distributed under the License is distributed on an\n" +
+	"# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n" +
+	"# KIND, either express or implied.  See the License for the\n" +
+	"# specific language governing permissions and limitations\n" +
+	"# under the License.\n\n"
+
+// QLFileContent returns the .ql file content with license header.
 func (tc *TestCase) QLFileContent() []byte {
-	return []byte(tc.QL + "\n")
+	return []byte(licenseHeader + tc.QL + "\n")
 }
 
-// WriteInputYAML writes the input YAML file content.
+// InputYAMLContent returns the input YAML file content with license header.
 func (tc *TestCase) InputYAMLContent() ([]byte, error) {
 	marshaler := protojson.MarshalOptions{Multiline: true}
 	jsonBytes, marshalErr := marshaler.Marshal(tc.Request)
 	if marshalErr != nil {
 		return nil, marshalErr
 	}
-	return yaml.JSONToYAML(jsonBytes)
+	yamlBytes, yamlErr := yaml.JSONToYAML(jsonBytes)
+	if yamlErr != nil {
+		return nil, yamlErr
+	}
+	return append([]byte(licenseHeader), yamlBytes...), nil
 }
 
 // BuildTagProjection creates a TagProjection for the given tag names.
diff --git a/test/cases/measure/data/input/gen_err_in_scalar.ql b/test/cases/measure/data/input/gen_err_in_scalar.ql
index ab1c3634d..347457ed7 100644
--- a/test/cases/measure/data/input/gen_err_in_scalar.ql
+++ b/test/cases/measure/data/input/gen_err_in_scalar.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total, value FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id IN ('svc1')
diff --git a/test/cases/measure/data/input/gen_err_in_scalar.yaml b/test/cases/measure/data/input/gen_err_in_scalar.yaml
index 4f4080f4b..005446a48 100644
--- a/test/cases/measure/data/input/gen_err_in_scalar.yaml
+++ b/test/cases/measure/data/input/gen_err_in_scalar.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: id
diff --git a/test/cases/measure/data/input/gen_err_match_int.ql b/test/cases/measure/data/input/gen_err_match_int.ql
index b735d670f..640f81329 100644
--- a/test/cases/measure/data/input/gen_err_match_int.ql
+++ b/test/cases/measure/data/input/gen_err_match_int.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer MATCH 1
diff --git a/test/cases/measure/data/input/gen_err_match_int.yaml b/test/cases/measure/data/input/gen_err_match_int.yaml
index 26a639ad5..edb7ff941 100644
--- a/test/cases/measure/data/input/gen_err_match_int.yaml
+++ b/test/cases/measure/data/input/gen_err_match_int.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: layer
diff --git a/test/cases/measure/data/input/gen_feat_count_group_order_desc_8.ql b/test/cases/measure/data/input/gen_feat_count_group_order_desc_8.ql
index 969ee0d8f..a09303f67 100644
--- a/test/cases/measure/data/input/gen_feat_count_group_order_desc_8.ql
+++ b/test/cases/measure/data/input/gen_feat_count_group_order_desc_8.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, COUNT(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY DESC
diff --git a/test/cases/measure/data/input/gen_feat_count_group_order_desc_8.yaml b/test/cases/measure/data/input/gen_feat_count_group_order_desc_8.yaml
index 655496b34..3536c029e 100644
--- a/test/cases/measure/data/input/gen_feat_count_group_order_desc_8.yaml
+++ b/test/cases/measure/data/input/gen_feat_count_group_order_desc_8.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 agg:
   fieldName: value
   function: AGGREGATION_FUNCTION_COUNT
diff --git a/test/cases/measure/data/input/gen_feat_max_group_order_desc_6.ql b/test/cases/measure/data/input/gen_feat_max_group_order_desc_6.ql
index 79fcdfc27..678dc1ef7 100644
--- a/test/cases/measure/data/input/gen_feat_max_group_order_desc_6.ql
+++ b/test/cases/measure/data/input/gen_feat_max_group_order_desc_6.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, MAX(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY DESC
diff --git a/test/cases/measure/data/input/gen_feat_max_group_order_desc_6.yaml b/test/cases/measure/data/input/gen_feat_max_group_order_desc_6.yaml
index 859253265..f06d36ab3 100644
--- a/test/cases/measure/data/input/gen_feat_max_group_order_desc_6.yaml
+++ b/test/cases/measure/data/input/gen_feat_max_group_order_desc_6.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 agg:
   fieldName: value
   function: AGGREGATION_FUNCTION_MAX
diff --git a/test/cases/measure/data/input/gen_feat_mean_group_2.ql b/test/cases/measure/data/input/gen_feat_mean_group_2.ql
index ceff68626..4fdc3f5c1 100644
--- a/test/cases/measure/data/input/gen_feat_mean_group_2.ql
+++ b/test/cases/measure/data/input/gen_feat_mean_group_2.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, MEAN(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field
diff --git a/test/cases/measure/data/input/gen_feat_mean_group_2.yaml b/test/cases/measure/data/input/gen_feat_mean_group_2.yaml
index 8594d4626..b1fb42b34 100644
--- a/test/cases/measure/data/input/gen_feat_mean_group_2.yaml
+++ b/test/cases/measure/data/input/gen_feat_mean_group_2.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 agg:
   fieldName: value
   function: AGGREGATION_FUNCTION_MEAN
diff --git a/test/cases/measure/data/input/gen_feat_mean_group_order_asc_5.ql b/test/cases/measure/data/input/gen_feat_mean_group_order_asc_5.ql
index 4605b2980..0edda415c 100644
--- a/test/cases/measure/data/input/gen_feat_mean_group_order_asc_5.ql
+++ b/test/cases/measure/data/input/gen_feat_mean_group_order_asc_5.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, MEAN(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY ASC
diff --git a/test/cases/measure/data/input/gen_feat_mean_group_order_asc_5.yaml b/test/cases/measure/data/input/gen_feat_mean_group_order_asc_5.yaml
index 22d5b1305..031357bc4 100644
--- a/test/cases/measure/data/input/gen_feat_mean_group_order_asc_5.yaml
+++ b/test/cases/measure/data/input/gen_feat_mean_group_order_asc_5.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 agg:
   fieldName: value
   function: AGGREGATION_FUNCTION_MEAN
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_asc_4.ql b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_asc_4.ql
index ae6b98612..d5a7011db 100644
--- a/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_asc_4.ql
+++ b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_asc_4.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT TOP 2 value ASC, id, entity_id, MEAN(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY ASC
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_asc_4.yaml b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_asc_4.yaml
index 327c6dfe4..050ac389c 100644
--- a/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_asc_4.yaml
+++ b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_asc_4.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 agg:
   fieldName: value
   function: AGGREGATION_FUNCTION_MEAN
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_desc_filter_1.ql b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_desc_filter_1.ql
index 57c3686e2..f03fbea17 100644
--- a/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_desc_filter_1.ql
+++ b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_desc_filter_1.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT TOP 2 value ASC, id, entity_id, MEAN(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id != 'svc3' GROUP BY id, value::field ORDER BY DESC
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_desc_filter_1.yaml b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_desc_filter_1.yaml
index 27120871a..c3ae0bc12 100644
--- a/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_desc_filter_1.yaml
+++ b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_desc_filter_1.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 agg:
   fieldName: value
   function: AGGREGATION_FUNCTION_MEAN
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_asc_0.ql b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_asc_0.ql
index 9577d9cc1..bbbf9419e 100644
--- a/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_asc_0.ql
+++ b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_asc_0.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT TOP 2 value DESC, id, entity_id, MEAN(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY ASC
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_asc_0.yaml b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_asc_0.yaml
index 054c90198..b2c82628a 100644
--- a/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_asc_0.yaml
+++ b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_asc_0.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 agg:
   fieldName: value
   function: AGGREGATION_FUNCTION_MEAN
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_desc_3.ql b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_desc_3.ql
index c131c3c38..b3c9534f2 100644
--- a/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_desc_3.ql
+++ b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_desc_3.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT TOP 2 value DESC, id, entity_id, MEAN(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY DESC
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_desc_3.yaml b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_desc_3.yaml
index ead5c3c4e..8533c93ef 100644
--- a/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_desc_3.yaml
+++ b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_desc_3.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 agg:
   fieldName: value
   function: AGGREGATION_FUNCTION_MEAN
diff --git a/test/cases/measure/data/input/gen_feat_min_group_order_desc_7.ql b/test/cases/measure/data/input/gen_feat_min_group_order_desc_7.ql
index 53afd1020..48b5ede40 100644
--- a/test/cases/measure/data/input/gen_feat_min_group_order_desc_7.ql
+++ b/test/cases/measure/data/input/gen_feat_min_group_order_desc_7.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, MIN(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY DESC
diff --git a/test/cases/measure/data/input/gen_feat_min_group_order_desc_7.yaml b/test/cases/measure/data/input/gen_feat_min_group_order_desc_7.yaml
index 2025a6474..02268ec5a 100644
--- a/test/cases/measure/data/input/gen_feat_min_group_order_desc_7.yaml
+++ b/test/cases/measure/data/input/gen_feat_min_group_order_desc_7.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 agg:
   fieldName: value
   function: AGGREGATION_FUNCTION_MIN
diff --git a/test/cases/measure/data/input/gen_feat_sum_group_order_desc_9.ql b/test/cases/measure/data/input/gen_feat_sum_group_order_desc_9.ql
index 7f8ad1269..65a7a3d9f 100644
--- a/test/cases/measure/data/input/gen_feat_sum_group_order_desc_9.ql
+++ b/test/cases/measure/data/input/gen_feat_sum_group_order_desc_9.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, SUM(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY DESC
diff --git a/test/cases/measure/data/input/gen_feat_sum_group_order_desc_9.yaml b/test/cases/measure/data/input/gen_feat_sum_group_order_desc_9.yaml
index 4aa15eed9..d35b936d8 100644
--- a/test/cases/measure/data/input/gen_feat_sum_group_order_desc_9.yaml
+++ b/test/cases/measure/data/input/gen_feat_sum_group_order_desc_9.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 agg:
   fieldName: value
   function: AGGREGATION_FUNCTION_SUM
diff --git a/test/cases/measure/data/input/gen_leaf_eq_int.ql b/test/cases/measure/data/input/gen_leaf_eq_int.ql
index 5c22c2b2f..04d6f0a3e 100644
--- a/test/cases/measure/data/input/gen_leaf_eq_int.ql
+++ b/test/cases/measure/data/input/gen_leaf_eq_int.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer = 1
diff --git a/test/cases/measure/data/input/gen_leaf_eq_int.yaml b/test/cases/measure/data/input/gen_leaf_eq_int.yaml
index 6c371d5a7..21e280c4f 100644
--- a/test/cases/measure/data/input/gen_leaf_eq_int.yaml
+++ b/test/cases/measure/data/input/gen_leaf_eq_int.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: layer
diff --git a/test/cases/measure/data/input/gen_leaf_eq_null.ql b/test/cases/measure/data/input/gen_leaf_eq_null.ql
index 4d02b619c..03599d93b 100644
--- a/test/cases/measure/data/input/gen_leaf_eq_null.ql
+++ b/test/cases/measure/data/input/gen_leaf_eq_null.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id = NULL
diff --git a/test/cases/measure/data/input/gen_leaf_eq_null.yaml b/test/cases/measure/data/input/gen_leaf_eq_null.yaml
index 7e2534a14..5b61d5b19 100644
--- a/test/cases/measure/data/input/gen_leaf_eq_null.yaml
+++ b/test/cases/measure/data/input/gen_leaf_eq_null.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: id
diff --git a/test/cases/measure/data/input/gen_leaf_eq_str.ql b/test/cases/measure/data/input/gen_leaf_eq_str.ql
index 10183bd55..badf45339 100644
--- a/test/cases/measure/data/input/gen_leaf_eq_str.ql
+++ b/test/cases/measure/data/input/gen_leaf_eq_str.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id = 'svc1'
diff --git a/test/cases/measure/data/input/gen_leaf_eq_str.yaml b/test/cases/measure/data/input/gen_leaf_eq_str.yaml
index 6f4af72dd..5df528954 100644
--- a/test/cases/measure/data/input/gen_leaf_eq_str.yaml
+++ b/test/cases/measure/data/input/gen_leaf_eq_str.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: id
diff --git a/test/cases/measure/data/input/gen_leaf_ge_int.ql b/test/cases/measure/data/input/gen_leaf_ge_int.ql
index 209aa666e..f627e8d34 100644
--- a/test/cases/measure/data/input/gen_leaf_ge_int.ql
+++ b/test/cases/measure/data/input/gen_leaf_ge_int.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer >= 2
diff --git a/test/cases/measure/data/input/gen_leaf_ge_int.yaml b/test/cases/measure/data/input/gen_leaf_ge_int.yaml
index ace313a31..6fef89901 100644
--- a/test/cases/measure/data/input/gen_leaf_ge_int.yaml
+++ b/test/cases/measure/data/input/gen_leaf_ge_int.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: layer
diff --git a/test/cases/measure/data/input/gen_leaf_ge_str.ql b/test/cases/measure/data/input/gen_leaf_ge_str.ql
index 6561e12f5..44cdcf648 100644
--- a/test/cases/measure/data/input/gen_leaf_ge_str.ql
+++ b/test/cases/measure/data/input/gen_leaf_ge_str.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id >= 'svc2'
diff --git a/test/cases/measure/data/input/gen_leaf_ge_str.yaml b/test/cases/measure/data/input/gen_leaf_ge_str.yaml
index 19011fe9b..29afbdd90 100644
--- a/test/cases/measure/data/input/gen_leaf_ge_str.yaml
+++ b/test/cases/measure/data/input/gen_leaf_ge_str.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: id
diff --git a/test/cases/measure/data/input/gen_leaf_gt_int.ql b/test/cases/measure/data/input/gen_leaf_gt_int.ql
index c50f50068..549f60087 100644
--- a/test/cases/measure/data/input/gen_leaf_gt_int.ql
+++ b/test/cases/measure/data/input/gen_leaf_gt_int.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer > 1
diff --git a/test/cases/measure/data/input/gen_leaf_gt_int.yaml b/test/cases/measure/data/input/gen_leaf_gt_int.yaml
index 5ca122c74..7560fbafb 100644
--- a/test/cases/measure/data/input/gen_leaf_gt_int.yaml
+++ b/test/cases/measure/data/input/gen_leaf_gt_int.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: layer
diff --git a/test/cases/measure/data/input/gen_leaf_gt_str.ql b/test/cases/measure/data/input/gen_leaf_gt_str.ql
index 2d2a1fa80..3673d8ec0 100644
--- a/test/cases/measure/data/input/gen_leaf_gt_str.ql
+++ b/test/cases/measure/data/input/gen_leaf_gt_str.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id > 'svc1'
diff --git a/test/cases/measure/data/input/gen_leaf_gt_str.yaml b/test/cases/measure/data/input/gen_leaf_gt_str.yaml
index a36b80916..a9eb9c110 100644
--- a/test/cases/measure/data/input/gen_leaf_gt_str.yaml
+++ b/test/cases/measure/data/input/gen_leaf_gt_str.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: id
diff --git a/test/cases/measure/data/input/gen_leaf_in_int_array.ql b/test/cases/measure/data/input/gen_leaf_in_int_array.ql
index 142b461be..6567b996b 100644
--- a/test/cases/measure/data/input/gen_leaf_in_int_array.ql
+++ b/test/cases/measure/data/input/gen_leaf_in_int_array.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer IN (1, 2)
diff --git a/test/cases/measure/data/input/gen_leaf_in_int_array.yaml b/test/cases/measure/data/input/gen_leaf_in_int_array.yaml
index 31d59776a..d1e9db6b3 100644
--- a/test/cases/measure/data/input/gen_leaf_in_int_array.yaml
+++ b/test/cases/measure/data/input/gen_leaf_in_int_array.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: layer
diff --git a/test/cases/measure/data/input/gen_leaf_in_str_array.ql b/test/cases/measure/data/input/gen_leaf_in_str_array.ql
index 06b9130f3..dc4791c8f 100644
--- a/test/cases/measure/data/input/gen_leaf_in_str_array.ql
+++ b/test/cases/measure/data/input/gen_leaf_in_str_array.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id IN ('svc1', 'svc2')
diff --git a/test/cases/measure/data/input/gen_leaf_in_str_array.yaml b/test/cases/measure/data/input/gen_leaf_in_str_array.yaml
index 675eda500..453a74198 100644
--- a/test/cases/measure/data/input/gen_leaf_in_str_array.yaml
+++ b/test/cases/measure/data/input/gen_leaf_in_str_array.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: id
diff --git a/test/cases/measure/data/input/gen_leaf_le_int.ql b/test/cases/measure/data/input/gen_leaf_le_int.ql
index 5b10971da..5f3238bbe 100644
--- a/test/cases/measure/data/input/gen_leaf_le_int.ql
+++ b/test/cases/measure/data/input/gen_leaf_le_int.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer <= 1
diff --git a/test/cases/measure/data/input/gen_leaf_le_int.yaml b/test/cases/measure/data/input/gen_leaf_le_int.yaml
index c963d2742..aaecd4608 100644
--- a/test/cases/measure/data/input/gen_leaf_le_int.yaml
+++ b/test/cases/measure/data/input/gen_leaf_le_int.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: layer
diff --git a/test/cases/measure/data/input/gen_leaf_le_str.ql b/test/cases/measure/data/input/gen_leaf_le_str.ql
index cbaf25f2c..e55660aeb 100644
--- a/test/cases/measure/data/input/gen_leaf_le_str.ql
+++ b/test/cases/measure/data/input/gen_leaf_le_str.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id <= 'svc2'
diff --git a/test/cases/measure/data/input/gen_leaf_le_str.yaml b/test/cases/measure/data/input/gen_leaf_le_str.yaml
index 89d11ff53..be63b4094 100644
--- a/test/cases/measure/data/input/gen_leaf_le_str.yaml
+++ b/test/cases/measure/data/input/gen_leaf_le_str.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: id
diff --git a/test/cases/measure/data/input/gen_leaf_lt_int.ql b/test/cases/measure/data/input/gen_leaf_lt_int.ql
index 29ba57c8f..0787674dd 100644
--- a/test/cases/measure/data/input/gen_leaf_lt_int.ql
+++ b/test/cases/measure/data/input/gen_leaf_lt_int.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer < 2
diff --git a/test/cases/measure/data/input/gen_leaf_lt_int.yaml b/test/cases/measure/data/input/gen_leaf_lt_int.yaml
index c83daddf2..5eb41d5ac 100644
--- a/test/cases/measure/data/input/gen_leaf_lt_int.yaml
+++ b/test/cases/measure/data/input/gen_leaf_lt_int.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: layer
diff --git a/test/cases/measure/data/input/gen_leaf_lt_str.ql b/test/cases/measure/data/input/gen_leaf_lt_str.ql
index d2d491a87..427db43ff 100644
--- a/test/cases/measure/data/input/gen_leaf_lt_str.ql
+++ b/test/cases/measure/data/input/gen_leaf_lt_str.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id < 'svc2'
diff --git a/test/cases/measure/data/input/gen_leaf_lt_str.yaml b/test/cases/measure/data/input/gen_leaf_lt_str.yaml
index dece5fe4f..9d48ea3c7 100644
--- a/test/cases/measure/data/input/gen_leaf_lt_str.yaml
+++ b/test/cases/measure/data/input/gen_leaf_lt_str.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: id
diff --git a/test/cases/measure/data/input/gen_leaf_match_str.ql b/test/cases/measure/data/input/gen_leaf_match_str.ql
index 2e0611f00..6c6b5953a 100644
--- a/test/cases/measure/data/input/gen_leaf_match_str.ql
+++ b/test/cases/measure/data/input/gen_leaf_match_str.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, service_id, name, last_ping, layer FROM MEASURE service_instance_traffic IN sw_metric TIME > '-15m' WHERE name MATCH ('nodea')
diff --git a/test/cases/measure/data/input/gen_leaf_match_str.yaml b/test/cases/measure/data/input/gen_leaf_match_str.yaml
index 787d942d6..6426ca44c 100644
--- a/test/cases/measure/data/input/gen_leaf_match_str.yaml
+++ b/test/cases/measure/data/input/gen_leaf_match_str.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: name
diff --git a/test/cases/measure/data/input/gen_leaf_ne_int.ql b/test/cases/measure/data/input/gen_leaf_ne_int.ql
index 0e7204fbf..bba0d202f 100644
--- a/test/cases/measure/data/input/gen_leaf_ne_int.ql
+++ b/test/cases/measure/data/input/gen_leaf_ne_int.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer != 1
diff --git a/test/cases/measure/data/input/gen_leaf_ne_int.yaml b/test/cases/measure/data/input/gen_leaf_ne_int.yaml
index 733d3c6e4..71e8124c2 100644
--- a/test/cases/measure/data/input/gen_leaf_ne_int.yaml
+++ b/test/cases/measure/data/input/gen_leaf_ne_int.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: layer
diff --git a/test/cases/measure/data/input/gen_leaf_ne_null.ql b/test/cases/measure/data/input/gen_leaf_ne_null.ql
index b54248c6e..19e0ea14e 100644
--- a/test/cases/measure/data/input/gen_leaf_ne_null.ql
+++ b/test/cases/measure/data/input/gen_leaf_ne_null.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id != NULL
diff --git a/test/cases/measure/data/input/gen_leaf_ne_null.yaml b/test/cases/measure/data/input/gen_leaf_ne_null.yaml
index 341604b05..f21779598 100644
--- a/test/cases/measure/data/input/gen_leaf_ne_null.yaml
+++ b/test/cases/measure/data/input/gen_leaf_ne_null.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: id
diff --git a/test/cases/measure/data/input/gen_leaf_ne_str.ql b/test/cases/measure/data/input/gen_leaf_ne_str.ql
index da3bbab79..821472968 100644
--- a/test/cases/measure/data/input/gen_leaf_ne_str.ql
+++ b/test/cases/measure/data/input/gen_leaf_ne_str.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id != 'svc1'
diff --git a/test/cases/measure/data/input/gen_leaf_ne_str.yaml b/test/cases/measure/data/input/gen_leaf_ne_str.yaml
index 578cd8eda..8616b3d67 100644
--- a/test/cases/measure/data/input/gen_leaf_ne_str.yaml
+++ b/test/cases/measure/data/input/gen_leaf_ne_str.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: id
diff --git a/test/cases/measure/data/input/gen_leaf_not_in_int_array.ql b/test/cases/measure/data/input/gen_leaf_not_in_int_array.ql
index 8a0c6c416..36563c41c 100644
--- a/test/cases/measure/data/input/gen_leaf_not_in_int_array.ql
+++ b/test/cases/measure/data/input/gen_leaf_not_in_int_array.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer NOT IN (0)
diff --git a/test/cases/measure/data/input/gen_leaf_not_in_int_array.yaml b/test/cases/measure/data/input/gen_leaf_not_in_int_array.yaml
index 9906692ee..ff3b5d065 100644
--- a/test/cases/measure/data/input/gen_leaf_not_in_int_array.yaml
+++ b/test/cases/measure/data/input/gen_leaf_not_in_int_array.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: layer
diff --git a/test/cases/measure/data/input/gen_leaf_not_in_str_array.ql b/test/cases/measure/data/input/gen_leaf_not_in_str_array.ql
index ed4a4fe2d..3b3421007 100644
--- a/test/cases/measure/data/input/gen_leaf_not_in_str_array.ql
+++ b/test/cases/measure/data/input/gen_leaf_not_in_str_array.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id NOT IN ('svc3')
diff --git a/test/cases/measure/data/input/gen_leaf_not_in_str_array.yaml b/test/cases/measure/data/input/gen_leaf_not_in_str_array.yaml
index 4b500a562..975a09109 100644
--- a/test/cases/measure/data/input/gen_leaf_not_in_str_array.yaml
+++ b/test/cases/measure/data/input/gen_leaf_not_in_str_array.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: id
diff --git a/test/cases/measure/data/input/gen_tree_depth1_leaf.ql b/test/cases/measure/data/input/gen_tree_depth1_leaf.ql
index 10183bd55..badf45339 100644
--- a/test/cases/measure/data/input/gen_tree_depth1_leaf.ql
+++ b/test/cases/measure/data/input/gen_tree_depth1_leaf.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id = 'svc1'
diff --git a/test/cases/measure/data/input/gen_tree_depth1_leaf.yaml b/test/cases/measure/data/input/gen_tree_depth1_leaf.yaml
index 6f4af72dd..5df528954 100644
--- a/test/cases/measure/data/input/gen_tree_depth1_leaf.yaml
+++ b/test/cases/measure/data/input/gen_tree_depth1_leaf.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   condition:
     name: id
diff --git a/test/cases/measure/data/input/gen_tree_depth2_and.ql b/test/cases/measure/data/input/gen_tree_depth2_and.ql
index 5e6351288..d345cd652 100644
--- a/test/cases/measure/data/input/gen_tree_depth2_and.ql
+++ b/test/cases/measure/data/input/gen_tree_depth2_and.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE (id = 'svc1' AND entity_id = 'entity_2')
diff --git a/test/cases/measure/data/input/gen_tree_depth2_and.yaml b/test/cases/measure/data/input/gen_tree_depth2_and.yaml
index eefdad86d..5316d8a55 100644
--- a/test/cases/measure/data/input/gen_tree_depth2_and.yaml
+++ b/test/cases/measure/data/input/gen_tree_depth2_and.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   le:
     left:
diff --git a/test/cases/measure/data/input/gen_tree_depth2_or.ql b/test/cases/measure/data/input/gen_tree_depth2_or.ql
index ba15d9e6e..d1e1ca3ee 100644
--- a/test/cases/measure/data/input/gen_tree_depth2_or.ql
+++ b/test/cases/measure/data/input/gen_tree_depth2_or.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE (id = 'svc1' OR entity_id = 'entity_2')
diff --git a/test/cases/measure/data/input/gen_tree_depth2_or.yaml b/test/cases/measure/data/input/gen_tree_depth2_or.yaml
index 3308529da..dfd5147f6 100644
--- a/test/cases/measure/data/input/gen_tree_depth2_or.yaml
+++ b/test/cases/measure/data/input/gen_tree_depth2_or.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   le:
     left:
diff --git a/test/cases/measure/data/input/gen_tree_depth3_and_or.ql b/test/cases/measure/data/input/gen_tree_depth3_and_or.ql
index e2ba77a27..69d78e2fc 100644
--- a/test/cases/measure/data/input/gen_tree_depth3_and_or.ql
+++ b/test/cases/measure/data/input/gen_tree_depth3_and_or.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE ((id = 'svc1' OR entity_id = 'entity_2') AND (id = 'svc3' OR entity_id = 'entity_4'))
diff --git a/test/cases/measure/data/input/gen_tree_depth3_and_or.yaml b/test/cases/measure/data/input/gen_tree_depth3_and_or.yaml
index 62ca0bf20..5ac50dd7f 100644
--- a/test/cases/measure/data/input/gen_tree_depth3_and_or.yaml
+++ b/test/cases/measure/data/input/gen_tree_depth3_and_or.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   le:
     left:
diff --git a/test/cases/measure/data/input/gen_tree_depth3_or_and.ql b/test/cases/measure/data/input/gen_tree_depth3_or_and.ql
index bb7bf6e4d..fcded4df0 100644
--- a/test/cases/measure/data/input/gen_tree_depth3_or_and.ql
+++ b/test/cases/measure/data/input/gen_tree_depth3_or_and.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE ((id = 'svc1' AND entity_id = 'entity_2') OR (id = 'svc3' AND entity_id = 'entity_4'))
diff --git a/test/cases/measure/data/input/gen_tree_depth3_or_and.yaml b/test/cases/measure/data/input/gen_tree_depth3_or_and.yaml
index 2ee0d2285..dc7094ade 100644
--- a/test/cases/measure/data/input/gen_tree_depth3_or_and.yaml
+++ b/test/cases/measure/data/input/gen_tree_depth3_or_and.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   le:
     left:
diff --git a/test/cases/measure/data/input/gen_tree_depth5_deep_and.ql b/test/cases/measure/data/input/gen_tree_depth5_deep_and.ql
index 53377cbbd..49c76fb59 100644
--- a/test/cases/measure/data/input/gen_tree_depth5_deep_and.ql
+++ b/test/cases/measure/data/input/gen_tree_depth5_deep_and.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE ((((id = 'svc1' OR entity_id = 'entity_2') AND (id = 'svc3' OR entity_id = 'entity_4')) OR ((id = 'svc2' OR entity_id = 'entity_6') AND (id = 'svc1' OR entity_id = 'entity_2'))) AND (((id = 'svc3' OR entity_id = 'entity_4') AND (id = 'svc2' OR entity_id = 'entity_6')) OR ((id = 'svc1' OR entity_id = 'entity_2') AND (id = 'svc3' OR entity_id = 'entity_4'))))
diff --git a/test/cases/measure/data/input/gen_tree_depth5_deep_and.yaml b/test/cases/measure/data/input/gen_tree_depth5_deep_and.yaml
index 4ae105904..a936c3fb3 100644
--- a/test/cases/measure/data/input/gen_tree_depth5_deep_and.yaml
+++ b/test/cases/measure/data/input/gen_tree_depth5_deep_and.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   le:
     left:
diff --git a/test/cases/measure/data/input/gen_tree_depth5_deep_or.ql b/test/cases/measure/data/input/gen_tree_depth5_deep_or.ql
index 05b838c29..d2763f428 100644
--- a/test/cases/measure/data/input/gen_tree_depth5_deep_or.ql
+++ b/test/cases/measure/data/input/gen_tree_depth5_deep_or.ql
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE ((((id = 'svc1' AND entity_id = 'entity_2') OR (id = 'svc3' AND entity_id = 'entity_4')) AND ((id = 'svc2' AND entity_id = 'entity_6') OR (id = 'svc1' AND entity_id = 'entity_2'))) OR (((id = 'svc3' AND entity_id = 'entity_4') OR (id = 'svc2' AND entity_id = 'entity_6')) AND ((id = 'svc1' AND entity_id = 'entity_2') OR (id = 'svc3' AND entity_id = 'entity_4'))))
diff --git a/test/cases/measure/data/input/gen_tree_depth5_deep_or.yaml b/test/cases/measure/data/input/gen_tree_depth5_deep_or.yaml
index fc4f81a29..5b6750ad2 100644
--- a/test/cases/measure/data/input/gen_tree_depth5_deep_or.yaml
+++ b/test/cases/measure/data/input/gen_tree_depth5_deep_or.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 criteria:
   le:
     left:
diff --git a/test/cases/measure/data/want/gen_feat_count_group_order_desc_8.yaml b/test/cases/measure/data/want/gen_feat_count_group_order_desc_8.yaml
index 2450bddf2..dc915c451 100644
--- a/test/cases/measure/data/want/gen_feat_count_group_order_desc_8.yaml
+++ b/test/cases/measure/data/want/gen_feat_count_group_order_desc_8.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: value
diff --git a/test/cases/measure/data/want/gen_feat_max_group_order_desc_6.yaml b/test/cases/measure/data/want/gen_feat_max_group_order_desc_6.yaml
index 44cdaa245..89338489c 100644
--- a/test/cases/measure/data/want/gen_feat_max_group_order_desc_6.yaml
+++ b/test/cases/measure/data/want/gen_feat_max_group_order_desc_6.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: value
diff --git a/test/cases/measure/data/want/gen_feat_mean_group_2.yaml b/test/cases/measure/data/want/gen_feat_mean_group_2.yaml
index f38fb2306..9b7ddc223 100644
--- a/test/cases/measure/data/want/gen_feat_mean_group_2.yaml
+++ b/test/cases/measure/data/want/gen_feat_mean_group_2.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: value
diff --git a/test/cases/measure/data/want/gen_feat_mean_group_order_asc_5.yaml b/test/cases/measure/data/want/gen_feat_mean_group_order_asc_5.yaml
index f38fb2306..9b7ddc223 100644
--- a/test/cases/measure/data/want/gen_feat_mean_group_order_asc_5.yaml
+++ b/test/cases/measure/data/want/gen_feat_mean_group_order_asc_5.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: value
diff --git a/test/cases/measure/data/want/gen_feat_mean_top_asc_group_order_asc_4.yaml b/test/cases/measure/data/want/gen_feat_mean_top_asc_group_order_asc_4.yaml
index b6c334652..6ecd6837d 100644
--- a/test/cases/measure/data/want/gen_feat_mean_top_asc_group_order_asc_4.yaml
+++ b/test/cases/measure/data/want/gen_feat_mean_top_asc_group_order_asc_4.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: value
diff --git a/test/cases/measure/data/want/gen_feat_mean_top_asc_group_order_desc_filter_1.yaml b/test/cases/measure/data/want/gen_feat_mean_top_asc_group_order_desc_filter_1.yaml
index 1f9d7218b..820382017 100644
--- a/test/cases/measure/data/want/gen_feat_mean_top_asc_group_order_desc_filter_1.yaml
+++ b/test/cases/measure/data/want/gen_feat_mean_top_asc_group_order_desc_filter_1.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: value
diff --git a/test/cases/measure/data/want/gen_feat_mean_top_desc_group_order_asc_0.yaml b/test/cases/measure/data/want/gen_feat_mean_top_desc_group_order_asc_0.yaml
index f3b31794d..092622014 100644
--- a/test/cases/measure/data/want/gen_feat_mean_top_desc_group_order_asc_0.yaml
+++ b/test/cases/measure/data/want/gen_feat_mean_top_desc_group_order_asc_0.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: value
diff --git a/test/cases/measure/data/want/gen_feat_mean_top_desc_group_order_desc_3.yaml b/test/cases/measure/data/want/gen_feat_mean_top_desc_group_order_desc_3.yaml
index 876638f23..f126a8165 100644
--- a/test/cases/measure/data/want/gen_feat_mean_top_desc_group_order_desc_3.yaml
+++ b/test/cases/measure/data/want/gen_feat_mean_top_desc_group_order_desc_3.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: value
diff --git a/test/cases/measure/data/want/gen_feat_min_group_order_desc_7.yaml b/test/cases/measure/data/want/gen_feat_min_group_order_desc_7.yaml
index 6dc3bd75a..5295bacb0 100644
--- a/test/cases/measure/data/want/gen_feat_min_group_order_desc_7.yaml
+++ b/test/cases/measure/data/want/gen_feat_min_group_order_desc_7.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: value
diff --git a/test/cases/measure/data/want/gen_feat_sum_group_order_desc_9.yaml b/test/cases/measure/data/want/gen_feat_sum_group_order_desc_9.yaml
index 3c2c38d24..4dd6030ef 100644
--- a/test/cases/measure/data/want/gen_feat_sum_group_order_desc_9.yaml
+++ b/test/cases/measure/data/want/gen_feat_sum_group_order_desc_9.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: value
diff --git a/test/cases/measure/data/want/gen_leaf_eq_int.yaml b/test/cases/measure/data/want/gen_leaf_eq_int.yaml
index a9d96b64a..0311ec9a8 100644
--- a/test/cases/measure/data/want/gen_leaf_eq_int.yaml
+++ b/test/cases/measure/data/want/gen_leaf_eq_int.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - sid: "15142466043926325685"
   tagFamilies:
diff --git a/test/cases/measure/data/want/gen_leaf_eq_str.yaml b/test/cases/measure/data/want/gen_leaf_eq_str.yaml
index 86b692a1a..2f5b511c7 100644
--- a/test/cases/measure/data/want/gen_leaf_eq_str.yaml
+++ b/test/cases/measure/data/want/gen_leaf_eq_str.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: total
diff --git a/test/cases/measure/data/want/gen_leaf_ge_int.yaml b/test/cases/measure/data/want/gen_leaf_ge_int.yaml
index 72b42fcc4..3e10c6999 100644
--- a/test/cases/measure/data/want/gen_leaf_ge_int.yaml
+++ b/test/cases/measure/data/want/gen_leaf_ge_int.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - sid: "3906119849472468294"
   tagFamilies:
diff --git a/test/cases/measure/data/want/gen_leaf_ge_str.yaml b/test/cases/measure/data/want/gen_leaf_ge_str.yaml
index fd793e1fe..07d744be8 100644
--- a/test/cases/measure/data/want/gen_leaf_ge_str.yaml
+++ b/test/cases/measure/data/want/gen_leaf_ge_str.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: total
diff --git a/test/cases/measure/data/want/gen_leaf_gt_int.yaml b/test/cases/measure/data/want/gen_leaf_gt_int.yaml
index 72b42fcc4..3e10c6999 100644
--- a/test/cases/measure/data/want/gen_leaf_gt_int.yaml
+++ b/test/cases/measure/data/want/gen_leaf_gt_int.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - sid: "3906119849472468294"
   tagFamilies:
diff --git a/test/cases/measure/data/want/gen_leaf_gt_str.yaml b/test/cases/measure/data/want/gen_leaf_gt_str.yaml
index fd793e1fe..07d744be8 100644
--- a/test/cases/measure/data/want/gen_leaf_gt_str.yaml
+++ b/test/cases/measure/data/want/gen_leaf_gt_str.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: total
diff --git a/test/cases/measure/data/want/gen_leaf_in_int_array.yaml b/test/cases/measure/data/want/gen_leaf_in_int_array.yaml
index 16346cb66..b8f645603 100644
--- a/test/cases/measure/data/want/gen_leaf_in_int_array.yaml
+++ b/test/cases/measure/data/want/gen_leaf_in_int_array.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - sid: "15142466043926325685"
   tagFamilies:
diff --git a/test/cases/measure/data/want/gen_leaf_in_str_array.yaml b/test/cases/measure/data/want/gen_leaf_in_str_array.yaml
index 3825dfdd0..5765886a0 100644
--- a/test/cases/measure/data/want/gen_leaf_in_str_array.yaml
+++ b/test/cases/measure/data/want/gen_leaf_in_str_array.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: total
diff --git a/test/cases/measure/data/want/gen_leaf_le_int.yaml b/test/cases/measure/data/want/gen_leaf_le_int.yaml
index 305e42af9..17b205c83 100644
--- a/test/cases/measure/data/want/gen_leaf_le_int.yaml
+++ b/test/cases/measure/data/want/gen_leaf_le_int.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - sid: "15142466043926325685"
   tagFamilies:
diff --git a/test/cases/measure/data/want/gen_leaf_le_str.yaml b/test/cases/measure/data/want/gen_leaf_le_str.yaml
index 3825dfdd0..5765886a0 100644
--- a/test/cases/measure/data/want/gen_leaf_le_str.yaml
+++ b/test/cases/measure/data/want/gen_leaf_le_str.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: total
diff --git a/test/cases/measure/data/want/gen_leaf_lt_int.yaml b/test/cases/measure/data/want/gen_leaf_lt_int.yaml
index 305e42af9..17b205c83 100644
--- a/test/cases/measure/data/want/gen_leaf_lt_int.yaml
+++ b/test/cases/measure/data/want/gen_leaf_lt_int.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - sid: "15142466043926325685"
   tagFamilies:
diff --git a/test/cases/measure/data/want/gen_leaf_lt_str.yaml b/test/cases/measure/data/want/gen_leaf_lt_str.yaml
index 86b692a1a..2f5b511c7 100644
--- a/test/cases/measure/data/want/gen_leaf_lt_str.yaml
+++ b/test/cases/measure/data/want/gen_leaf_lt_str.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: total
diff --git a/test/cases/measure/data/want/gen_leaf_match_str.yaml b/test/cases/measure/data/want/gen_leaf_match_str.yaml
index f6df497dd..88a0ff371 100644
--- a/test/cases/measure/data/want/gen_leaf_match_str.yaml
+++ b/test/cases/measure/data/want/gen_leaf_match_str.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - sid: "17262786445563424612"
   tagFamilies:
diff --git a/test/cases/measure/data/want/gen_leaf_ne_int.yaml b/test/cases/measure/data/want/gen_leaf_ne_int.yaml
index 60110eed3..5c750c2b1 100644
--- a/test/cases/measure/data/want/gen_leaf_ne_int.yaml
+++ b/test/cases/measure/data/want/gen_leaf_ne_int.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - sid: "3906119849472468294"
   tagFamilies:
diff --git a/test/cases/measure/data/want/gen_leaf_ne_str.yaml b/test/cases/measure/data/want/gen_leaf_ne_str.yaml
index fd793e1fe..07d744be8 100644
--- a/test/cases/measure/data/want/gen_leaf_ne_str.yaml
+++ b/test/cases/measure/data/want/gen_leaf_ne_str.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: total
diff --git a/test/cases/measure/data/want/gen_leaf_not_in_int_array.yaml b/test/cases/measure/data/want/gen_leaf_not_in_int_array.yaml
index 16346cb66..b8f645603 100644
--- a/test/cases/measure/data/want/gen_leaf_not_in_int_array.yaml
+++ b/test/cases/measure/data/want/gen_leaf_not_in_int_array.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - sid: "15142466043926325685"
   tagFamilies:
diff --git a/test/cases/measure/data/want/gen_leaf_not_in_str_array.yaml b/test/cases/measure/data/want/gen_leaf_not_in_str_array.yaml
index 3825dfdd0..5765886a0 100644
--- a/test/cases/measure/data/want/gen_leaf_not_in_str_array.yaml
+++ b/test/cases/measure/data/want/gen_leaf_not_in_str_array.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: total
diff --git a/test/cases/measure/data/want/gen_tree_depth1_leaf.yaml b/test/cases/measure/data/want/gen_tree_depth1_leaf.yaml
index 86b692a1a..2f5b511c7 100644
--- a/test/cases/measure/data/want/gen_tree_depth1_leaf.yaml
+++ b/test/cases/measure/data/want/gen_tree_depth1_leaf.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: total
diff --git a/test/cases/measure/data/want/gen_tree_depth2_and.yaml b/test/cases/measure/data/want/gen_tree_depth2_and.yaml
index b90cd7225..2775f5640 100644
--- a/test/cases/measure/data/want/gen_tree_depth2_and.yaml
+++ b/test/cases/measure/data/want/gen_tree_depth2_and.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: total
diff --git a/test/cases/measure/data/want/gen_tree_depth2_or.yaml b/test/cases/measure/data/want/gen_tree_depth2_or.yaml
index 86b692a1a..2f5b511c7 100644
--- a/test/cases/measure/data/want/gen_tree_depth2_or.yaml
+++ b/test/cases/measure/data/want/gen_tree_depth2_or.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: total
diff --git a/test/cases/measure/data/want/gen_tree_depth3_and_or.yaml b/test/cases/measure/data/want/gen_tree_depth3_and_or.yaml
index 0967ef424..9d699e84b 100644
--- a/test/cases/measure/data/want/gen_tree_depth3_and_or.yaml
+++ b/test/cases/measure/data/want/gen_tree_depth3_and_or.yaml
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 {}
diff --git a/test/cases/measure/data/want/gen_tree_depth3_or_and.yaml b/test/cases/measure/data/want/gen_tree_depth3_or_and.yaml
index b90cd7225..2775f5640 100644
--- a/test/cases/measure/data/want/gen_tree_depth3_or_and.yaml
+++ b/test/cases/measure/data/want/gen_tree_depth3_or_and.yaml
@@ -1,3 +1,20 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 dataPoints:
 - fields:
   - name: total
diff --git a/test/cases/measure/data/want/gen_tree_depth5_deep_and.yaml b/test/cases/measure/data/want/gen_tree_depth5_deep_and.yaml
index 0967ef424..9d699e84b 100644
--- a/test/cases/measure/data/want/gen_tree_depth5_deep_and.yaml
+++ b/test/cases/measure/data/want/gen_tree_depth5_deep_and.yaml
@@ -1 +1,18 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
 {}


(skywalking-banyandb) 01/04: fix(query): validate MATCH/IN conditions and handle nil OR branch

Posted by ha...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hanahmily pushed a commit to branch test/query
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git

commit 02c720f0f66eb4812268686b67b4f6c3a63fc067
Author: Hongtao Gao <ha...@gmail.com>
AuthorDate: Wed Apr 15 02:40:11 2026 +0000

    fix(query): validate MATCH/IN conditions and handle nil OR branch
    
    Add nil-value and type checks for MATCH (string required) and IN
    (array required) conditions to fail fast with clear errors. Fix OR
    branch handling when one side is entity-only by returning match-all
    when all entities are specific. Also fix topN debug logging to iterate
    all tuples unconditionally. Add comprehensive measure query test cases.
---
 CHANGES.md                                         |   1 +
 banyand/measure/topn.go                            |   4 +-
 pkg/index/inverted/query.go                        |  39 ++++
 test/cases/measure/cmd/capture/capture_test.go     | 174 ++++++++++++++++
 test/cases/measure/cmd/generate/capture.go         | 175 ++++++++++++++++
 test/cases/measure/cmd/generate/entry.go           |  52 +++++
 test/cases/measure/cmd/generate/gap.go             | 186 +++++++++++++++++
 test/cases/measure/cmd/generate/layer1_leaf.go     | 162 +++++++++++++++
 test/cases/measure/cmd/generate/layer2_tree.go     | 110 ++++++++++
 test/cases/measure/cmd/generate/layer3_features.go | 193 +++++++++++++++++
 test/cases/measure/cmd/generate/main.go            | 121 +++++++++++
 test/cases/measure/cmd/generate/pairwise.go        | 155 ++++++++++++++
 test/cases/measure/cmd/generate/ql_renderer.go     | 228 +++++++++++++++++++++
 test/cases/measure/cmd/generate/seed.go            | 156 ++++++++++++++
 test/cases/measure/cmd/generate/types.go           | 137 +++++++++++++
 test/cases/measure/data/input/complex_and_or.ql    |  21 ++
 test/cases/measure/data/input/complex_and_or.yaml  |  52 +++++
 test/cases/measure/data/input/gen_err_in_scalar.ql |   1 +
 .../measure/data/input/gen_err_in_scalar.yaml      |  20 ++
 test/cases/measure/data/input/gen_err_match_int.ql |   1 +
 .../measure/data/input/gen_err_match_int.yaml      |  20 ++
 .../input/gen_feat_count_group_order_desc_8.ql     |   1 +
 .../input/gen_feat_count_group_order_desc_8.yaml   |  25 +++
 .../data/input/gen_feat_max_group_order_desc_6.ql  |   1 +
 .../input/gen_feat_max_group_order_desc_6.yaml     |  25 +++
 .../measure/data/input/gen_feat_mean_group_2.ql    |   1 +
 .../measure/data/input/gen_feat_mean_group_2.yaml  |  23 +++
 .../data/input/gen_feat_mean_group_order_asc_5.ql  |   1 +
 .../input/gen_feat_mean_group_order_asc_5.yaml     |  25 +++
 .../gen_feat_mean_top_asc_group_order_asc_4.ql     |   1 +
 .../gen_feat_mean_top_asc_group_order_asc_4.yaml   |  29 +++
 ..._feat_mean_top_asc_group_order_desc_filter_1.ql |   1 +
 ...eat_mean_top_asc_group_order_desc_filter_1.yaml |  36 ++++
 .../gen_feat_mean_top_desc_group_order_asc_0.ql    |   1 +
 .../gen_feat_mean_top_desc_group_order_asc_0.yaml  |  29 +++
 .../gen_feat_mean_top_desc_group_order_desc_3.ql   |   1 +
 .../gen_feat_mean_top_desc_group_order_desc_3.yaml |  29 +++
 .../data/input/gen_feat_min_group_order_desc_7.ql  |   1 +
 .../input/gen_feat_min_group_order_desc_7.yaml     |  25 +++
 .../data/input/gen_feat_sum_group_order_desc_9.ql  |   1 +
 .../input/gen_feat_sum_group_order_desc_9.yaml     |  25 +++
 test/cases/measure/data/input/gen_leaf_eq_int.ql   |   1 +
 test/cases/measure/data/input/gen_leaf_eq_int.yaml |  20 ++
 test/cases/measure/data/input/gen_leaf_eq_null.ql  |   1 +
 .../cases/measure/data/input/gen_leaf_eq_null.yaml |  19 ++
 test/cases/measure/data/input/gen_leaf_eq_str.ql   |   1 +
 test/cases/measure/data/input/gen_leaf_eq_str.yaml |  20 ++
 test/cases/measure/data/input/gen_leaf_ge_int.ql   |   1 +
 test/cases/measure/data/input/gen_leaf_ge_int.yaml |  20 ++
 test/cases/measure/data/input/gen_leaf_ge_str.ql   |   1 +
 test/cases/measure/data/input/gen_leaf_ge_str.yaml |  20 ++
 test/cases/measure/data/input/gen_leaf_gt_int.ql   |   1 +
 test/cases/measure/data/input/gen_leaf_gt_int.yaml |  20 ++
 test/cases/measure/data/input/gen_leaf_gt_str.ql   |   1 +
 test/cases/measure/data/input/gen_leaf_gt_str.yaml |  20 ++
 .../measure/data/input/gen_leaf_in_int_array.ql    |   1 +
 .../measure/data/input/gen_leaf_in_int_array.yaml  |  22 ++
 .../measure/data/input/gen_leaf_in_str_array.ql    |   1 +
 .../measure/data/input/gen_leaf_in_str_array.yaml  |  22 ++
 test/cases/measure/data/input/gen_leaf_le_int.ql   |   1 +
 test/cases/measure/data/input/gen_leaf_le_int.yaml |  20 ++
 test/cases/measure/data/input/gen_leaf_le_str.ql   |   1 +
 test/cases/measure/data/input/gen_leaf_le_str.yaml |  20 ++
 test/cases/measure/data/input/gen_leaf_lt_int.ql   |   1 +
 test/cases/measure/data/input/gen_leaf_lt_int.yaml |  20 ++
 test/cases/measure/data/input/gen_leaf_lt_str.ql   |   1 +
 test/cases/measure/data/input/gen_leaf_lt_str.yaml |  20 ++
 .../cases/measure/data/input/gen_leaf_match_str.ql |   1 +
 .../measure/data/input/gen_leaf_match_str.yaml     |  19 ++
 test/cases/measure/data/input/gen_leaf_ne_int.ql   |   1 +
 test/cases/measure/data/input/gen_leaf_ne_int.yaml |  20 ++
 test/cases/measure/data/input/gen_leaf_ne_null.ql  |   1 +
 .../cases/measure/data/input/gen_leaf_ne_null.yaml |  19 ++
 test/cases/measure/data/input/gen_leaf_ne_str.ql   |   1 +
 test/cases/measure/data/input/gen_leaf_ne_str.yaml |  20 ++
 .../data/input/gen_leaf_not_in_int_array.ql        |   1 +
 .../data/input/gen_leaf_not_in_int_array.yaml      |  21 ++
 .../data/input/gen_leaf_not_in_str_array.ql        |   1 +
 .../data/input/gen_leaf_not_in_str_array.yaml      |  21 ++
 .../measure/data/input/gen_tree_depth1_leaf.ql     |   1 +
 .../measure/data/input/gen_tree_depth1_leaf.yaml   |  20 ++
 .../measure/data/input/gen_tree_depth2_and.ql      |   1 +
 .../measure/data/input/gen_tree_depth2_and.yaml    |  30 +++
 .../cases/measure/data/input/gen_tree_depth2_or.ql |   1 +
 .../measure/data/input/gen_tree_depth2_or.yaml     |  30 +++
 .../measure/data/input/gen_tree_depth3_and_or.ql   |   1 +
 .../measure/data/input/gen_tree_depth3_and_or.yaml |  50 +++++
 .../measure/data/input/gen_tree_depth3_or_and.ql   |   1 +
 .../measure/data/input/gen_tree_depth3_or_and.yaml |  50 +++++
 .../measure/data/input/gen_tree_depth5_deep_and.ql |   1 +
 .../data/input/gen_tree_depth5_deep_and.yaml       | 170 +++++++++++++++
 .../measure/data/input/gen_tree_depth5_deep_or.ql  |   1 +
 .../data/input/gen_tree_depth5_deep_or.yaml        | 170 +++++++++++++++
 .../measure/data/input/group_sum_with_filter.ql    |  22 ++
 .../measure/data/input/group_sum_with_filter.yaml  |  41 ++++
 test/cases/measure/data/input/index_mode_le.ql     |  21 ++
 test/cases/measure/data/input/index_mode_le.yaml   |  30 +++
 test/cases/measure/data/input/index_mode_ne.ql     |  21 ++
 test/cases/measure/data/input/index_mode_ne.yaml   |  30 +++
 test/cases/measure/data/input/offset_empty.ql      |  22 ++
 test/cases/measure/data/input/offset_empty.yaml    |  27 +++
 test/cases/measure/data/input/tag_filter_ge.ql     |  21 ++
 test/cases/measure/data/input/tag_filter_ge.yaml   |  32 +++
 test/cases/measure/data/input/tag_filter_ne.ql     |  21 ++
 test/cases/measure/data/input/tag_filter_ne.yaml   |  32 +++
 test/cases/measure/data/input/tag_filter_not_in.ql |  21 ++
 .../measure/data/input/tag_filter_not_in.yaml      |  32 +++
 test/cases/measure/data/input/top_with_filter.ql   |  22 ++
 test/cases/measure/data/input/top_with_filter.yaml |  45 ++++
 test/cases/measure/data/want/complex_and_or.yaml   |  58 ++++++
 .../want/gen_feat_count_group_order_desc_8.yaml    |  49 +++++
 .../data/want/gen_feat_max_group_order_desc_6.yaml |  49 +++++
 .../measure/data/want/gen_feat_mean_group_2.yaml   |  49 +++++
 .../data/want/gen_feat_mean_group_order_asc_5.yaml |  49 +++++
 .../gen_feat_mean_top_asc_group_order_asc_4.yaml   |  33 +++
 ...eat_mean_top_asc_group_order_desc_filter_1.yaml |  33 +++
 .../gen_feat_mean_top_desc_group_order_asc_0.yaml  |  33 +++
 .../gen_feat_mean_top_desc_group_order_desc_3.yaml |  33 +++
 .../data/want/gen_feat_min_group_order_desc_7.yaml |  49 +++++
 .../data/want/gen_feat_sum_group_order_desc_9.yaml |  49 +++++
 test/cases/measure/data/want/gen_leaf_eq_int.yaml  |  31 +++
 test/cases/measure/data/want/gen_leaf_eq_str.yaml  |  70 +++++++
 test/cases/measure/data/want/gen_leaf_ge_int.yaml  |  31 +++
 test/cases/measure/data/want/gen_leaf_ge_str.yaml  |  70 +++++++
 test/cases/measure/data/want/gen_leaf_gt_int.yaml  |  31 +++
 test/cases/measure/data/want/gen_leaf_gt_str.yaml  |  70 +++++++
 .../measure/data/want/gen_leaf_in_int_array.yaml   |  61 ++++++
 .../measure/data/want/gen_leaf_in_str_array.yaml   | 116 +++++++++++
 test/cases/measure/data/want/gen_leaf_le_int.yaml  |  60 ++++++
 test/cases/measure/data/want/gen_leaf_le_str.yaml  | 116 +++++++++++
 test/cases/measure/data/want/gen_leaf_lt_int.yaml  |  60 ++++++
 test/cases/measure/data/want/gen_leaf_lt_str.yaml  |  70 +++++++
 .../measure/data/want/gen_leaf_match_str.yaml      |  51 +++++
 test/cases/measure/data/want/gen_leaf_ne_int.yaml  |  60 ++++++
 test/cases/measure/data/want/gen_leaf_ne_str.yaml  |  70 +++++++
 .../data/want/gen_leaf_not_in_int_array.yaml       |  61 ++++++
 .../data/want/gen_leaf_not_in_str_array.yaml       | 116 +++++++++++
 .../measure/data/want/gen_tree_depth1_leaf.yaml    |  70 +++++++
 .../measure/data/want/gen_tree_depth2_and.yaml     |  24 +++
 .../measure/data/want/gen_tree_depth2_or.yaml      |  70 +++++++
 .../measure/data/want/gen_tree_depth3_and_or.yaml  |   1 +
 .../measure/data/want/gen_tree_depth3_or_and.yaml  |  24 +++
 .../data/want/gen_tree_depth5_deep_and.yaml        |   1 +
 .../measure/data/want/group_sum_with_filter.yaml   |  42 ++++
 test/cases/measure/data/want/index_mode_le.yaml    |  48 +++++
 test/cases/measure/data/want/index_mode_ne.yaml    |  56 +++++
 test/cases/measure/data/want/tag_filter_ge.yaml    |  78 +++++++
 test/cases/measure/data/want/tag_filter_ne.yaml    |  78 +++++++
 .../cases/measure/data/want/tag_filter_not_in.yaml |  98 +++++++++
 test/cases/measure/data/want/top_with_filter.yaml  |  42 ++++
 test/cases/measure/measure.go                      |  49 +++++
 151 files changed, 5965 insertions(+), 2 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 6c404a496..16fc68228 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -34,6 +34,7 @@ Release Notes.
 - Fix handoff controller TOCTOU race allowing disk size limit bypass, and populate sidx MinTimestamp/MaxTimestamp during replay to prevent corrupt segment creation on recovered nodes.
 - Delete orphaned parts when no snapshot references them during tsTable initialization.
 - Extract shared LocateAll on NodeRegistry to ensure resolveAssignments and syncer GetNodes always produce identical node lists, preventing liaison from enqueuing parts to online/healthy data nodes.
+- Add validation for MATCH and IN conditions in inverted index query builder, and handle nil OR branch when all entities are specific.
 
 ### Chores
 
diff --git a/banyand/measure/topn.go b/banyand/measure/topn.go
index 3a35f1fcd..5ca997c7e 100644
--- a/banyand/measure/topn.go
+++ b/banyand/measure/topn.go
@@ -341,8 +341,8 @@ func (t *topNStreamingProcessor) writeStreamRecord(record flow.StreamRecord, buf
 	topNValue := GenerateTopNValue()
 	defer ReleaseTopNValue(topNValue)
 	for group, tuples := range tuplesGroups {
-		if e := t.l.Debug(); e.Enabled() {
-			for i := range tuples {
+		for i := range tuples {
+			if e := t.l.Debug(); e.Enabled() {
 				tuple := tuples[i]
 				data := tuple.V2.(flow.StreamRecord).Data().(flow.Data)
 				e.
diff --git a/pkg/index/inverted/query.go b/pkg/index/inverted/query.go
index 12deb057c..add0f2f4c 100644
--- a/pkg/index/inverted/query.go
+++ b/pkg/index/inverted/query.go
@@ -32,6 +32,7 @@ import (
 	propertyv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/property/v1"
 	"github.com/apache/skywalking-banyandb/pkg/convert"
 	"github.com/apache/skywalking-banyandb/pkg/index"
+	pbv1 "github.com/apache/skywalking-banyandb/pkg/pb/v1"
 	"github.com/apache/skywalking-banyandb/pkg/query/logical"
 	"github.com/apache/skywalking-banyandb/pkg/timestamp"
 )
@@ -146,6 +147,29 @@ func BuildQuery(criteria *modelv1.Criteria, schema logical.Schema, entityDict ma
 					node:  newMatchAllNode(),
 				}, entities, true, nil
 			}
+			// When one OR branch has no query (entity-only), check if all entities are specific.
+			// If no AnyTagValue is present, the nil branch's entity needs match-all to find its documents.
+			// When AnyTagValue exists, the other branch's query already covers all entities.
+			if left == nil || right == nil {
+				allSpecific := true
+				for _, entity := range entities {
+					for _, entry := range entity {
+						if entry == pbv1.AnyTagValue {
+							allSpecific = false
+							break
+						}
+					}
+					if !allSpecific {
+						break
+					}
+				}
+				if allSpecific {
+					return &queryNode{
+						query: bluge.NewMatchAllQuery(),
+						node:  newMatchAllNode(),
+					}, entities, true, nil
+				}
+			}
 			query, node := bluge.NewBooleanQuery(), newShouldNode()
 			query.SetMinShould(1)
 			if left != nil {
@@ -315,6 +339,12 @@ func parseConditionToQuery(cond *modelv1.Condition, indexRule *databasev1.IndexR
 		if indexRule == nil {
 			return nil, errors.WithMessagef(logical.ErrUnsupportedConditionOp, "index rule is mandatory for match operation: %s", cond)
 		}
+		if cond.Value == nil {
+			return nil, errors.WithMessagef(logical.ErrUnsupportedConditionValue, "MATCH condition requires non-nil value")
+		}
+		if _, ok := cond.Value.Value.(*modelv1.TagValue_Str); !ok {
+			return nil, errors.WithMessagef(logical.ErrUnsupportedConditionValue, "MATCH condition requires string value type: %s", cond)
+		}
 		bb := expr.Bytes()
 		if len(bb) != 1 {
 			return nil, errors.WithMessagef(logical.ErrUnsupportedConditionOp, "don't support multiple or null value: %s", cond)
@@ -356,6 +386,15 @@ func parseConditionToQuery(cond *modelv1.Condition, indexRule *databasev1.IndexR
 		node.SetSubNode(node)
 		return &queryNode{query, node}, nil
 	case modelv1.Condition_BINARY_OP_IN:
+		if cond.Value == nil {
+			return nil, errors.WithMessagef(logical.ErrUnsupportedConditionValue, "IN condition requires non-nil value")
+		}
+		switch cond.Value.Value.(type) {
+		case *modelv1.TagValue_StrArray, *modelv1.TagValue_IntArray:
+			// valid
+		default:
+			return nil, errors.WithMessagef(logical.ErrUnsupportedConditionValue, "IN condition requires array value type: %s", cond)
+		}
 		bb, elements := expr.Bytes(), expr.Elements()
 		query, node := bluge.NewBooleanQuery(), newShouldNode()
 		query.SetMinShould(1)
diff --git a/test/cases/measure/cmd/capture/capture_test.go b/test/cases/measure/cmd/capture/capture_test.go
new file mode 100644
index 000000000..8774619dc
--- /dev/null
+++ b/test/cases/measure/cmd/capture/capture_test.go
@@ -0,0 +1,174 @@
+// Package capture_test runs a standalone server, loads seed data, executes
+// generated queries, and writes responses as want/*.yaml files.
+//
+// Usage:
+//
+//	go test -run TestCapture ./test/cases/measure/cmd/capture/ -args [output-dir]
+//
+// output-dir defaults to ../../data (i.e. test/cases/measure/data).
+package capture_test
+
+import (
+	"context"
+	"os"
+	"path/filepath"
+	"testing"
+	"time"
+
+	"github.com/onsi/gomega"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/credentials/insecure"
+	"google.golang.org/protobuf/encoding/protojson"
+	"google.golang.org/protobuf/types/known/timestamppb"
+	"sigs.k8s.io/yaml"
+
+	measurev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/measure/v1"
+	modelv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
+	"github.com/apache/skywalking-banyandb/pkg/grpchelper"
+	"github.com/apache/skywalking-banyandb/pkg/logger"
+	"github.com/apache/skywalking-banyandb/pkg/test/setup"
+	"github.com/apache/skywalking-banyandb/pkg/timestamp"
+	casesMeasureData "github.com/apache/skywalking-banyandb/test/cases/measure/data"
+)
+
+func TestCapture(t *testing.T) {
+	gomega.RegisterTestingT(t)
+
+	// Initialize logger
+	gomega.Expect(logger.Init(logger.Logging{Env: "dev", Level: "warn"})).To(gomega.Succeed())
+
+	// Determine output directory
+	outputDir := filepath.Join("..", "..", "data")
+	if len(os.Args) > 1 {
+		// The first extra arg after -args is the output dir
+		for idx, arg := range os.Args {
+			if arg == "-args" && idx+1 < len(os.Args) {
+				outputDir = os.Args[idx+1]
+				break
+			}
+		}
+	}
+
+	inputDirPath := filepath.Join(outputDir, "input")
+	wantDirPath := filepath.Join(outputDir, "want")
+
+	// Ensure want directory exists
+	gomega.Expect(os.MkdirAll(wantDirPath, 0o755)).To(gomega.Succeed())
+
+	// Start standalone server
+	addr, _, deferFn := setup.Standalone(nil)
+	defer deferFn()
+
+	// Connect to server
+	conn, connErr := grpchelper.Conn(addr, 30*time.Second, grpc.WithTransportCredentials(insecure.NewCredentials()))
+	gomega.Expect(connErr).NotTo(gomega.HaveOccurred())
+	defer func() { _ = conn.Close() }()
+
+	// Write seed data - matching the integration test pattern
+	ns := timestamp.NowMilli().UnixNano()
+	baseTime := time.Unix(0, ns-ns%int64(time.Minute))
+	interval := 500 * time.Millisecond
+	casesMeasureData.Write(conn, "service_traffic", "index_mode", "service_traffic_data.json", baseTime, interval)
+	casesMeasureData.Write(conn, "service_instance_traffic", "sw_metric", "service_instance_traffic_data.json", baseTime, interval)
+	casesMeasureData.Write(conn, "service_cpm_minute", "sw_metric", "service_cpm_minute_data.json", baseTime, interval)
+
+	// Default time range matching test setup
+	duration := 25 * time.Minute
+	offset := -20 * time.Minute
+
+	// Discover generated input YAML files
+	entries, readErr := os.ReadDir(inputDirPath)
+	gomega.Expect(readErr).NotTo(gomega.HaveOccurred())
+
+	capturedCount := 0
+	for _, entry := range entries {
+		if entry.IsDir() {
+			continue
+		}
+		name := entry.Name()
+		if filepath.Ext(name) != ".yaml" {
+			continue
+		}
+		testName := name[:len(name)-len(".yaml")]
+		if !isGeneratedTest(testName) {
+			continue
+		}
+		if isErrorTest(testName) {
+			t.Logf("  [SKIP] %s: error case, no want file needed", testName)
+			continue
+		}
+
+		// Read input YAML
+		inputPath := filepath.Join(inputDirPath, name)
+		inputData, readInputErr := os.ReadFile(inputPath)
+		if readInputErr != nil {
+			t.Errorf("Error reading %s: %v", inputPath, readInputErr)
+			continue
+		}
+
+		// Parse QueryRequest from YAML
+		jsonBytes, yamlErr := yaml.YAMLToJSON(inputData)
+		if yamlErr != nil {
+			t.Errorf("Error converting YAML to JSON for %s: %v", testName, yamlErr)
+			continue
+		}
+
+		queryReq := &measurev1.QueryRequest{}
+		if unmarshalErr := protojson.Unmarshal(jsonBytes, queryReq); unmarshalErr != nil {
+			t.Errorf("Error unmarshaling request for %s: %v", testName, unmarshalErr)
+			continue
+		}
+
+		// Set time range
+		begin := baseTime.Add(offset)
+		queryReq.TimeRange = &modelv1.TimeRange{
+			Begin: timestamppb.New(begin),
+			End:   timestamppb.New(begin.Add(duration)),
+		}
+
+		// Execute query
+		client := measurev1.NewMeasureServiceClient(conn)
+		ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
+		resp, queryErr := client.Query(ctx, queryReq)
+		cancel()
+
+		if queryErr != nil {
+			t.Logf("  [SKIP] %s: query returned error (likely WantErr case): %v", testName, queryErr)
+			continue
+		}
+
+		// Write response as want YAML
+		marshaler := protojson.MarshalOptions{Multiline: true}
+		respJSON, marshalErr := marshaler.Marshal(resp)
+		if marshalErr != nil {
+			t.Errorf("  [ERROR] %s: marshal failed: %v", testName, marshalErr)
+			continue
+		}
+
+		respYAML, yamlConvertErr := yaml.JSONToYAML(respJSON)
+		if yamlConvertErr != nil {
+			t.Errorf("  [ERROR] %s: YAML conversion failed: %v", testName, yamlConvertErr)
+			continue
+		}
+
+		wantPath := filepath.Join(wantDirPath, testName+".yaml")
+		if writeErr := os.WriteFile(wantPath, respYAML, 0o644); writeErr != nil {
+			t.Errorf("  [ERROR] %s: write failed: %v", testName, writeErr)
+			continue
+		}
+
+		pointCount := len(resp.GetDataPoints())
+		t.Logf("  [OK] %s: %d data points", testName, pointCount)
+		capturedCount++
+	}
+
+	t.Logf("Capture complete: %d want files written to %s", capturedCount, wantDirPath)
+}
+
+func isGeneratedTest(name string) bool {
+	return len(name) > 4 && name[:4] == "gen_"
+}
+
+func isErrorTest(name string) bool {
+	return len(name) > 8 && name[:8] == "gen_err_"
+}
diff --git a/test/cases/measure/cmd/generate/capture.go b/test/cases/measure/cmd/generate/capture.go
new file mode 100644
index 000000000..0f0fc9029
--- /dev/null
+++ b/test/cases/measure/cmd/generate/capture.go
@@ -0,0 +1,175 @@
+package main
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"path/filepath"
+	"time"
+
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/credentials/insecure"
+	"google.golang.org/protobuf/encoding/protojson"
+	"google.golang.org/protobuf/types/known/timestamppb"
+	"sigs.k8s.io/yaml"
+
+	measurev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/measure/v1"
+	modelv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
+	"github.com/apache/skywalking-banyandb/pkg/grpchelper"
+)
+
+// runCapture connects to a live BanyanDB server, executes all generated queries,
+// and writes the responses as want/*.yaml files.
+func runCapture(outputDir, serverAddr string) {
+	inputDirPath := filepath.Join(outputDir, "input")
+	wantDirPath := filepath.Join(outputDir, "want")
+
+	// Ensure want directory exists
+	if mkdirErr := os.MkdirAll(wantDirPath, 0o755); mkdirErr != nil {
+		fmt.Fprintf(os.Stderr, "Error creating want directory: %v\n", mkdirErr)
+		os.Exit(1)
+	}
+
+	// Connect to server
+	fmt.Printf("Connecting to %s ...\n", serverAddr)
+	conn, connErr := grpchelper.Conn(serverAddr, 30*time.Second,
+		grpc.WithTransportCredentials(insecure.NewCredentials()))
+	if connErr != nil {
+		fmt.Fprintf(os.Stderr, "Error connecting to server: %v\n", connErr)
+		os.Exit(1)
+	}
+	defer func() {
+		if closeErr := conn.Close(); closeErr != nil {
+			fmt.Fprintf(os.Stderr, "Warning: error closing connection: %v\n", closeErr)
+		}
+	}()
+
+	// Discover all generated input YAML files
+	entries, readErr := os.ReadDir(inputDirPath)
+	if readErr != nil {
+		fmt.Fprintf(os.Stderr, "Error reading input directory: %v\n", readErr)
+		os.Exit(1)
+	}
+
+	// Default time range parameters matching test setup
+	baseTime := time.Now()
+	duration := 25 * time.Minute
+	offset := -20 * time.Minute
+
+	capturedCount := 0
+	skippedCount := 0
+	errorCount := 0
+
+	for _, entry := range entries {
+		if entry.IsDir() {
+			continue
+		}
+		name := entry.Name()
+		if filepath.Ext(name) != ".yaml" {
+			continue
+		}
+
+		testName := name[:len(name)-len(".yaml")]
+
+		// Only process generated test cases (prefixed with gen_)
+		if !isGeneratedTest(testName) {
+			continue
+		}
+
+		// Read input YAML
+		inputPath := filepath.Join(inputDirPath, name)
+		inputData, readInputErr := os.ReadFile(inputPath)
+		if readInputErr != nil {
+			fmt.Fprintf(os.Stderr, "Error reading %s: %v\n", inputPath, readInputErr)
+			errorCount++
+			continue
+		}
+
+		// Parse QueryRequest from YAML
+		jsonBytes, yamlErr := yaml.YAMLToJSON(inputData)
+		if yamlErr != nil {
+			fmt.Fprintf(os.Stderr, "Error converting YAML to JSON for %s: %v\n", testName, yamlErr)
+			errorCount++
+			continue
+		}
+
+		queryReq := &measurev1.QueryRequest{}
+		if unmarshalErr := protojson.Unmarshal(jsonBytes, queryReq); unmarshalErr != nil {
+			fmt.Fprintf(os.Stderr, "Error unmarshaling request for %s: %v\n", testName, unmarshalErr)
+			errorCount++
+			continue
+		}
+
+		// Set time range
+		begin := baseTime.Add(offset)
+		queryReq.TimeRange = &modelv1.TimeRange{
+			Begin: timestamppb.New(begin),
+			End:   timestamppb.New(begin.Add(duration)),
+		}
+
+		// Determine if this is an error case
+		isErrorCase := isErrorTest(testName)
+
+		// Execute query
+		client := measurev1.NewMeasureServiceClient(conn)
+		ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
+		resp, queryErr := client.Query(ctx, queryReq)
+		cancel()
+
+		if isErrorCase {
+			if queryErr != nil {
+				fmt.Printf("  [ERR-OK] %s: got expected error\n", testName)
+				skippedCount++
+			} else {
+				fmt.Printf("  [ERR-UNEXPECTED] %s: expected error but got response\n", testName)
+			}
+			continue
+		}
+
+		if queryErr != nil {
+			fmt.Fprintf(os.Stderr, "  [ERROR] %s: query failed: %v\n", testName, queryErr)
+			errorCount++
+			continue
+		}
+
+		// Write response as want YAML
+		marshaler := protojson.MarshalOptions{Multiline: true}
+		respJSON, marshalErr := marshaler.Marshal(resp)
+		if marshalErr != nil {
+			fmt.Fprintf(os.Stderr, "  [ERROR] %s: marshal failed: %v\n", testName, marshalErr)
+			errorCount++
+			continue
+		}
+
+		respYAML, yamlConvertErr := yaml.JSONToYAML(respJSON)
+		if yamlConvertErr != nil {
+			fmt.Fprintf(os.Stderr, "  [ERROR] %s: YAML conversion failed: %v\n", testName, yamlConvertErr)
+			errorCount++
+			continue
+		}
+
+		wantPath := filepath.Join(wantDirPath, testName+".yaml")
+		if writeErr := os.WriteFile(wantPath, respYAML, 0o644); writeErr != nil {
+			fmt.Fprintf(os.Stderr, "  [ERROR] %s: write failed: %v\n", testName, writeErr)
+			errorCount++
+			continue
+		}
+
+		pointCount := len(resp.GetDataPoints())
+		fmt.Printf("  [OK] %s: %d data points\n", testName, pointCount)
+		capturedCount++
+	}
+
+	fmt.Printf("\nCapture complete: %d captured, %d skipped (error cases), %d errors\n",
+		capturedCount, skippedCount, errorCount)
+}
+
+// isGeneratedTest checks if a test name was produced by the generator.
+func isGeneratedTest(name string) bool {
+	return len(name) > 4 && name[:4] == "gen_"
+}
+
+// isErrorTest checks if a generated test is an error case.
+func isErrorTest(name string) bool {
+	return len(name) > 8 && name[:8] == "gen_err_"
+}
diff --git a/test/cases/measure/cmd/generate/entry.go b/test/cases/measure/cmd/generate/entry.go
new file mode 100644
index 000000000..1270dfd81
--- /dev/null
+++ b/test/cases/measure/cmd/generate/entry.go
@@ -0,0 +1,52 @@
+package main
+
+import (
+	"fmt"
+	"strings"
+)
+
+// GenerateEntryCode produces g.Entry lines for measure.go.
+func GenerateEntryCode(cases []*TestCase) string {
+	var lines []string
+	for _, tc := range cases {
+		if tc.Measure == nil {
+			continue
+		}
+		line := renderEntryLine(tc)
+		lines = append(lines, line)
+	}
+	return strings.Join(lines, "\n")
+}
+
+func renderEntryLine(tc *TestCase) string {
+	name := tc.Name
+	want := name
+	wantEmpty := "false"
+	wantErr := "false"
+	disOrder := "false"
+	duration := "25 * time.Minute"
+	offset := "-20 * time.Minute"
+
+	if tc.WantErr {
+		wantErr = "true"
+	}
+	if tc.WantEmpty {
+		wantEmpty = "true"
+	}
+	if tc.DisOrder {
+		disOrder = "true"
+	}
+	if tc.Duration != "" {
+		duration = tc.Duration
+	}
+
+	return fmt.Sprintf(`g.Entry("%s", helpers.Args{Input: "%s", Want: "%s", WantEmpty: %s, WantErr: %s, DisOrder: %s, Duration: %s, Offset: %s})`,
+		name, name, want, wantEmpty, wantErr, disOrder, duration, offset)
+}
+
+// PrintEntryCode prints all entry lines to stdout.
+func PrintEntryCode(cases []*TestCase) {
+	fmt.Println("\n=== Entry Code for measure.go ===")
+	fmt.Println("// Paste the following lines into measureEntries:")
+	fmt.Println(GenerateEntryCode(cases))
+}
diff --git a/test/cases/measure/cmd/generate/gap.go b/test/cases/measure/cmd/generate/gap.go
new file mode 100644
index 000000000..bd4b77ce0
--- /dev/null
+++ b/test/cases/measure/cmd/generate/gap.go
@@ -0,0 +1,186 @@
+package main
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// CoverageMap tracks which combinations are already tested by existing cases.
+type CoverageMap struct {
+	BinaryOps   map[string]bool // "EQ:str", "NE:int", etc.
+	AggFunctions map[string]bool // "MEAN", "MAX", etc.
+	TreeDepths  map[int]bool    // 1, 2, 3, 5
+	LogicalOps  map[string]bool // "AND", "OR"
+	SortOrders  map[string]bool // "ASC", "DESC"
+	TopN        bool
+	GroupBy     bool
+	Limit       bool
+	Offset      bool
+	Criteria    map[string]bool // "EQ", "NE", "IN", etc. (any use of criteria)
+	TestNames   map[string]bool // existing test names
+}
+
+// AnalyzeGaps reads existing test files and builds a coverage map.
+func AnalyzeGaps(inputDir string) (*CoverageMap, error) {
+	cm := &CoverageMap{
+		BinaryOps:    make(map[string]bool),
+		AggFunctions: make(map[string]bool),
+		TreeDepths:   make(map[int]bool),
+		LogicalOps:   make(map[string]bool),
+		SortOrders:   make(map[string]bool),
+		Criteria:     make(map[string]bool),
+		TestNames:    make(map[string]bool),
+	}
+
+	qlFiles, readErr := filepath.Glob(filepath.Join(inputDir, "*.ql"))
+	if readErr != nil {
+		return nil, fmt.Errorf("failed to read ql files: %w", readErr)
+	}
+
+	for _, qlFile := range qlFiles {
+		name := strings.TrimSuffix(filepath.Base(qlFile), ".ql")
+		cm.TestNames[name] = true
+
+		content, readErr := os.ReadFile(qlFile)
+		if readErr != nil {
+			continue
+		}
+		qlText := string(content)
+		analyzeQLContent(cm, qlText)
+	}
+
+	return cm, nil
+}
+
+func analyzeQLContent(cm *CoverageMap, qlText string) {
+	upper := strings.ToUpper(qlText)
+
+	// Detect operators in WHERE clause
+	opPatterns := map[string]string{
+		"!=": "NE", "=": "EQ", "<=": "LE", ">=": "GE",
+		"<": "LT", ">": "GT",
+	}
+	for pattern, opName := range opPatterns {
+		if strings.Contains(qlText, pattern) {
+			cm.BinaryOps[opName] = true
+			cm.Criteria[opName] = true
+		}
+	}
+
+	if strings.Contains(upper, " NOT IN ") {
+		cm.BinaryOps["NOT_IN"] = true
+		cm.Criteria["NOT_IN"] = true
+	}
+	if strings.Contains(upper, " IN ") {
+		cm.BinaryOps["IN"] = true
+		cm.Criteria["IN"] = true
+	}
+	if strings.Contains(upper, " MATCH") {
+		cm.BinaryOps["MATCH"] = true
+		cm.Criteria["MATCH"] = true
+	}
+	if strings.Contains(upper, " HAVING ") {
+		cm.BinaryOps["HAVING"] = true
+		cm.Criteria["HAVING"] = true
+	}
+	if strings.Contains(upper, " NOT HAVING ") {
+		cm.BinaryOps["NOT_HAVING"] = true
+		cm.Criteria["NOT_HAVING"] = true
+	}
+
+	// Detect AND/OR
+	if strings.Contains(upper, " AND ") {
+		cm.LogicalOps["AND"] = true
+	}
+	if strings.Contains(upper, " OR ") {
+		cm.LogicalOps["OR"] = true
+	}
+
+	// Detect aggregation functions
+	aggNames := []string{"MEAN", "MAX", "MIN", "COUNT", "SUM"}
+	for _, aggName := range aggNames {
+		if strings.Contains(upper, aggName+"(") {
+			cm.AggFunctions[aggName] = true
+		}
+	}
+
+	// Detect sort order
+	if strings.Contains(upper, "ORDER BY ASC") || strings.Contains(upper, "ORDER ASC") {
+		cm.SortOrders["ASC"] = true
+	}
+	if strings.Contains(upper, "ORDER BY DESC") || strings.Contains(upper, "ORDER DESC") {
+		cm.SortOrders["DESC"] = true
+	}
+
+	// Detect TOP N
+	if strings.Contains(upper, "TOP ") {
+		cm.TopN = true
+	}
+
+	// Detect GROUP BY
+	if strings.Contains(upper, "GROUP BY") {
+		cm.GroupBy = true
+	}
+
+	// Detect LIMIT
+	if strings.Contains(upper, "LIMIT ") {
+		cm.Limit = true
+	}
+
+	// Detect OFFSET
+	if strings.Contains(upper, "OFFSET ") {
+		cm.Offset = true
+	}
+}
+
+// FilterGaps removes test cases that are already covered by existing tests.
+func FilterGaps(cases []*TestCase, cm *CoverageMap) []*TestCase {
+	var filtered []*TestCase
+	for _, tc := range cases {
+		if cm.TestNames[tc.Name] {
+			continue // exact name collision
+		}
+		filtered = append(filtered, tc)
+	}
+	return filtered
+}
+
+// PrintCoverageReport prints a summary of coverage.
+func PrintCoverageReport(cm *CoverageMap, generated []*TestCase) {
+	fmt.Println("\n=== Coverage Report ===")
+
+	fmt.Println("\nBinaryOps covered by existing tests:")
+	for op, covered := range cm.BinaryOps {
+		fmt.Printf("  %s: %v\n", op, covered)
+	}
+
+	fmt.Println("\nAggregationFunctions covered by existing tests:")
+	for agg, covered := range cm.AggFunctions {
+		fmt.Printf("  %s: %v\n", agg, covered)
+	}
+
+	fmt.Println("\nLogicalOps covered by existing tests:")
+	for op, covered := range cm.LogicalOps {
+		fmt.Printf("  %s: %v\n", op, covered)
+	}
+
+	fmt.Println("\nFeature coverage by existing tests:")
+	fmt.Printf("  TopN: %v\n", cm.TopN)
+	fmt.Printf("  GroupBy: %v\n", cm.GroupBy)
+	fmt.Printf("  Limit: %v\n", cm.Limit)
+	fmt.Printf("  Offset: %v\n", cm.Offset)
+
+	fmt.Printf("\nExisting test names: %d\n", len(cm.TestNames))
+	fmt.Printf("New test cases to generate: %d\n", len(generated))
+
+	fmt.Println("\nNew test case names:")
+	for _, tc := range generated {
+		prefix := "  "
+		if tc.WantErr {
+			prefix = "  [ERR] "
+		}
+		fmt.Printf("%s%s\n", prefix, tc.Name)
+	}
+}
diff --git a/test/cases/measure/cmd/generate/layer1_leaf.go b/test/cases/measure/cmd/generate/layer1_leaf.go
new file mode 100644
index 000000000..76fc65c13
--- /dev/null
+++ b/test/cases/measure/cmd/generate/layer1_leaf.go
@@ -0,0 +1,162 @@
+package main
+
+import (
+	"fmt"
+
+	measurev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/measure/v1"
+	modelv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
+)
+
+// LeafOp defines a valid BinaryOp + TagValue + measure + tag combination.
+type LeafOp struct {
+	Op         modelv1.Condition_BinaryOp
+	ValueType  string           // "str", "int", "null", "str_array", "int_array"
+	Value      *modelv1.TagValue // example value to use
+	Measure    string           // target measure name
+	TagName    string           // tag to filter on
+	QLValue    string           // QL representation of the value
+}
+
+// GenerateLayer1 produces test cases for the Criteria leaf layer.
+// Covers 9 in-scope BinaryOps × their valid TagValue types = 18 pairs.
+func GenerateLayer1() []*TestCase {
+	ops := defineLeafOps()
+	var cases []*TestCase
+	for idx, op := range ops {
+		m := FindMeasure(op.Measure)
+		if m == nil {
+			continue
+		}
+		req := buildLeafRequest(m, op)
+		ql, qlErr := RenderQL(req)
+		if qlErr != nil {
+			continue
+		}
+		cases = append(cases, &TestCase{
+			Name:    fmt.Sprintf("gen_leaf_%s_%s", binaryOpShortName(op.Op), op.ValueType),
+			Measure: m,
+			Request: req,
+			QL:      ql,
+			WantErr:  op.ValueType == "null", // server rejects null value comparisons
+			Duration: "25 * time.Minute",
+		})
+		_ = idx
+	}
+
+	// Add error cases for invalid combinations
+	cases = append(cases, generateLeafErrorCases()...)
+
+	return cases
+}
+
+func defineLeafOps() []LeafOp {
+	return []LeafOp{
+		// EQ with str, int, null
+		{Op: modelv1.Condition_BINARY_OP_EQ, ValueType: "str", Value: TagValueStr("svc1"), Measure: "service_cpm_minute", TagName: "id", QLValue: "'svc1'"},
+		{Op: modelv1.Condition_BINARY_OP_EQ, ValueType: "int", Value: TagValueInt(1), Measure: "service_traffic", TagName: "layer", QLValue: "1"},
+		{Op: modelv1.Condition_BINARY_OP_EQ, ValueType: "null", Value: TagValueNull(), Measure: "service_cpm_minute", TagName: "id", QLValue: "NULL"},
+		// NE with str, int, null
+		{Op: modelv1.Condition_BINARY_OP_NE, ValueType: "str", Value: TagValueStr("svc1"), Measure: "service_cpm_minute", TagName: "id", QLValue: "'svc1'"},
+		{Op: modelv1.Condition_BINARY_OP_NE, ValueType: "int", Value: TagValueInt(1), Measure: "service_traffic", TagName: "layer", QLValue: "1"},
+		{Op: modelv1.Condition_BINARY_OP_NE, ValueType: "null", Value: TagValueNull(), Measure: "service_cpm_minute", TagName: "id", QLValue: "NULL"},
+		// LT with str, int
+		{Op: modelv1.Condition_BINARY_OP_LT, ValueType: "str", Value: TagValueStr("svc2"), Measure: "service_cpm_minute", TagName: "id", QLValue: "'svc2'"},
+		{Op: modelv1.Condition_BINARY_OP_LT, ValueType: "int", Value: TagValueInt(2), Measure: "service_traffic", TagName: "layer", QLValue: "2"},
+		// GT with str, int
+		{Op: modelv1.Condition_BINARY_OP_GT, ValueType: "str", Value: TagValueStr("svc1"), Measure: "service_cpm_minute", TagName: "id", QLValue: "'svc1'"},
+		{Op: modelv1.Condition_BINARY_OP_GT, ValueType: "int", Value: TagValueInt(1), Measure: "service_traffic", TagName: "layer", QLValue: "1"},
+		// LE with str, int
+		{Op: modelv1.Condition_BINARY_OP_LE, ValueType: "str", Value: TagValueStr("svc2"), Measure: "service_cpm_minute", TagName: "id", QLValue: "'svc2'"},
+		{Op: modelv1.Condition_BINARY_OP_LE, ValueType: "int", Value: TagValueInt(1), Measure: "service_traffic", TagName: "layer", QLValue: "1"},
+		// GE with str, int
+		{Op: modelv1.Condition_BINARY_OP_GE, ValueType: "str", Value: TagValueStr("svc2"), Measure: "service_cpm_minute", TagName: "id", QLValue: "'svc2'"},
+		{Op: modelv1.Condition_BINARY_OP_GE, ValueType: "int", Value: TagValueInt(2), Measure: "service_traffic", TagName: "layer", QLValue: "2"},
+		// IN with str_array, int_array
+		{Op: modelv1.Condition_BINARY_OP_IN, ValueType: "str_array", Value: TagValueStrArray([]string{"svc1", "svc2"}), Measure: "service_cpm_minute", TagName: "id", QLValue: "('svc1', 'svc2')"},
+		{Op: modelv1.Condition_BINARY_OP_IN, ValueType: "int_array", Value: TagValueIntArray([]int64{1, 2}), Measure: "service_traffic", TagName: "layer", QLValue: "(1, 2)"},
+		// NOT_IN with str_array, int_array
+		{Op: modelv1.Condition_BINARY_OP_NOT_IN, ValueType: "str_array", Value: TagValueStrArray([]string{"svc3"}), Measure: "service_cpm_minute", TagName: "id", QLValue: "('svc3')"},
+		{Op: modelv1.Condition_BINARY_OP_NOT_IN, ValueType: "int_array", Value: TagValueIntArray([]int64{0}), Measure: "service_traffic", TagName: "layer", QLValue: "(0)"},
+		// MATCH with str
+		{Op: modelv1.Condition_BINARY_OP_MATCH, ValueType: "str", Value: TagValueStr("nodea"), Measure: "service_instance_traffic", TagName: "name", QLValue: "MATCH('nodea')"},
+	}
+}
+
+func buildLeafRequest(m *Measure, op LeafOp) *measurev1.QueryRequest {
+	cond := BuildCondition(op.TagName, op.Op, op.Value)
+	criteria := BuildCriteriaFromCondition(cond)
+	tagNames := AllTagNames(m)
+	fieldNames := AllFieldNames(m)
+	return &measurev1.QueryRequest{
+		Name:           m.Name,
+		Groups:         []string{m.Group},
+		Criteria:       criteria,
+		TagProjection:  BuildTagProjection(m, tagNames),
+		FieldProjection: BuildFieldProjection(fieldNames),
+	}
+}
+
+func generateLeafErrorCases() []*TestCase {
+	return []*TestCase{
+		// IN with scalar (should use array)
+		{
+			Name: "gen_err_in_scalar",
+			Measure: FindMeasure("service_cpm_minute"),
+			Request: &measurev1.QueryRequest{
+				Name:   "service_cpm_minute",
+				Groups: []string{"sw_metric"},
+				Criteria: BuildCriteriaFromCondition(
+					BuildCondition("id", modelv1.Condition_BINARY_OP_IN, TagValueStr("svc1")),
+				),
+				TagProjection:  BuildTagProjection(FindMeasure("service_cpm_minute"), []string{"id", "entity_id"}),
+				FieldProjection: BuildFieldProjection([]string{"total", "value"}),
+			},
+			QL:      "SELECT id, entity_id, total, value FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id IN ('svc1')",
+			WantErr: true,
+		},
+		// MATCH with int value
+		{
+			Name: "gen_err_match_int",
+			Measure: FindMeasure("service_traffic"),
+			Request: &measurev1.QueryRequest{
+				Name:   "service_traffic",
+				Groups: []string{"index_mode"},
+				Criteria: BuildCriteriaFromCondition(
+					BuildCondition("layer", modelv1.Condition_BINARY_OP_MATCH, TagValueInt(1)),
+				),
+				TagProjection: BuildTagProjection(FindMeasure("service_traffic"), []string{"id", "service_id", "name", "short_name", "service_group", "layer"}),
+			},
+			QL:      "SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer MATCH 1",
+			WantErr: true,
+		},
+	}
+}
+
+func binaryOpShortName(op modelv1.Condition_BinaryOp) string {
+	switch op {
+	case modelv1.Condition_BINARY_OP_EQ:
+		return "eq"
+	case modelv1.Condition_BINARY_OP_NE:
+		return "ne"
+	case modelv1.Condition_BINARY_OP_LT:
+		return "lt"
+	case modelv1.Condition_BINARY_OP_GT:
+		return "gt"
+	case modelv1.Condition_BINARY_OP_LE:
+		return "le"
+	case modelv1.Condition_BINARY_OP_GE:
+		return "ge"
+	case modelv1.Condition_BINARY_OP_IN:
+		return "in"
+	case modelv1.Condition_BINARY_OP_NOT_IN:
+		return "not_in"
+	case modelv1.Condition_BINARY_OP_MATCH:
+		return "match"
+	case modelv1.Condition_BINARY_OP_HAVING:
+		return "having"
+	case modelv1.Condition_BINARY_OP_NOT_HAVING:
+		return "not_having"
+	default:
+		return "unknown"
+	}
+}
diff --git a/test/cases/measure/cmd/generate/layer2_tree.go b/test/cases/measure/cmd/generate/layer2_tree.go
new file mode 100644
index 000000000..107b8eaf3
--- /dev/null
+++ b/test/cases/measure/cmd/generate/layer2_tree.go
@@ -0,0 +1,110 @@
+package main
+
+import (
+	"fmt"
+
+	measurev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/measure/v1"
+	modelv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
+)
+
+// GenerateLayer2 produces test cases for criteria tree depth.
+// Tests depths 1, 2, 3, 5 with AND/OR combinations.
+func GenerateLayer2() []*TestCase {
+	m := FindMeasure("service_cpm_minute")
+	if m == nil {
+		return nil
+	}
+
+	type depthConfig struct {
+		depth    int
+		rootOp   modelv1.LogicalExpression_LogicalOp
+		suffix   string
+	}
+
+	configs := []depthConfig{
+		{depth: 1, rootOp: modelv1.LogicalExpression_LOGICAL_OP_AND, suffix: "leaf"},
+		{depth: 2, rootOp: modelv1.LogicalExpression_LOGICAL_OP_AND, suffix: "and"},
+		{depth: 2, rootOp: modelv1.LogicalExpression_LOGICAL_OP_OR, suffix: "or"},
+		{depth: 3, rootOp: modelv1.LogicalExpression_LOGICAL_OP_AND, suffix: "and_or"},
+		{depth: 3, rootOp: modelv1.LogicalExpression_LOGICAL_OP_OR, suffix: "or_and"},
+		{depth: 5, rootOp: modelv1.LogicalExpression_LOGICAL_OP_AND, suffix: "deep_and"},
+		{depth: 5, rootOp: modelv1.LogicalExpression_LOGICAL_OP_OR, suffix: "deep_or"},
+	}
+
+	var cases []*TestCase
+	for _, cfg := range configs {
+		criteria := buildTree(cfg.depth, cfg.rootOp, 0)
+		if criteria == nil {
+			continue
+		}
+		req := &measurev1.QueryRequest{
+			Name:           m.Name,
+			Groups:         []string{m.Group},
+			Criteria:       criteria,
+			TagProjection:  BuildTagProjection(m, []string{"id", "entity_id"}),
+			FieldProjection: BuildFieldProjection([]string{"total", "value"}),
+		}
+		ql, qlErr := RenderQL(req)
+		if qlErr != nil {
+			continue
+		}
+
+		// Determine DisOrder: trees with OR at any level may produce non-deterministic order
+		disOrder := cfg.depth > 1
+
+		cases = append(cases, &TestCase{
+			Name:     fmt.Sprintf("gen_tree_depth%d_%s", cfg.depth, cfg.suffix),
+			Measure:  m,
+			Request:  req,
+			QL:       ql,
+			DisOrder: disOrder,
+			WantErr:  cfg.depth == 5 && cfg.rootOp == modelv1.LogicalExpression_LOGICAL_OP_OR, // server rejects deep OR trees
+			Duration: "25 * time.Minute",
+		})
+	}
+
+	return cases
+}
+
+// buildTree recursively builds a balanced criteria tree of the given depth.
+// Leaves use EQ conditions with values from seed data, rotated across leaves.
+func buildTree(depth int, op modelv1.LogicalExpression_LogicalOp, leafOffset int) *modelv1.Criteria {
+	if depth <= 1 {
+		return buildLeafCondition(leafOffset)
+	}
+
+	// Build left and right subtrees
+	left := buildTree(depth-1, alternateOp(op), leafOffset)
+	right := buildTree(depth-1, alternateOp(op), leafOffset+1<<(depth-2))
+	if left == nil || right == nil {
+		return left
+	}
+
+	return BuildLogicalExpr(op, left, right)
+}
+
+func buildLeafCondition(offset int) *modelv1.Criteria {
+	// Rotate through available tag values from seed data
+	tagNames := []string{"id", "entity_id"}
+	tagValues := [][]string{
+		{"svc1", "svc2", "svc3"},
+		{"entity_1", "entity_2", "entity_3", "entity_4", "entity_5", "entity_6"},
+	}
+
+	tagIdx := offset % len(tagNames)
+	valIdx := offset % len(tagValues[tagIdx])
+
+	cond := BuildCondition(
+		tagNames[tagIdx],
+		modelv1.Condition_BINARY_OP_EQ,
+		TagValueStr(tagValues[tagIdx][valIdx]),
+	)
+	return BuildCriteriaFromCondition(cond)
+}
+
+func alternateOp(op modelv1.LogicalExpression_LogicalOp) modelv1.LogicalExpression_LogicalOp {
+	if op == modelv1.LogicalExpression_LOGICAL_OP_AND {
+		return modelv1.LogicalExpression_LOGICAL_OP_OR
+	}
+	return modelv1.LogicalExpression_LOGICAL_OP_AND
+}
diff --git a/test/cases/measure/cmd/generate/layer3_features.go b/test/cases/measure/cmd/generate/layer3_features.go
new file mode 100644
index 000000000..12bc95365
--- /dev/null
+++ b/test/cases/measure/cmd/generate/layer3_features.go
@@ -0,0 +1,193 @@
+package main
+
+import (
+	"fmt"
+	"strings"
+
+	measurev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/measure/v1"
+	modelv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
+)
+
+// GenerateLayer3 produces test cases for query feature combinations.
+// Uses pairwise testing across AggFunction, TopN, GroupBy, OrderBy, and CriteriaPresence.
+func GenerateLayer3() []*TestCase {
+	m := FindMeasure("service_cpm_minute")
+	if m == nil {
+		return nil
+	}
+
+	params := map[string][]string{
+		"agg":    {"MEAN", "MAX", "MIN", "COUNT", "SUM", "none"},
+		"top":    {"desc", "asc", "none"},
+		"group":  {"true", "false"},
+		"order":  {"asc", "desc", "none"},
+		"filter": {"none", "eq"},
+	}
+
+	constraints := []ConstraintFunc{
+		// TopN requires GroupBy
+		func(tv TestVector) bool {
+			if tv["top"] != "none" && tv["group"] == "false" {
+				return false
+			}
+			return true
+		},
+		// Agg (non-none) requires GroupBy
+		func(tv TestVector) bool {
+			if tv["agg"] != "none" && tv["group"] == "false" {
+				return false
+			}
+			return true
+		},
+		// TopN requires Agg
+		func(tv TestVector) bool {
+			if tv["top"] != "none" && tv["agg"] == "none" {
+				return false
+			}
+			return true
+		},
+	}
+
+	vectors := PairwiseGenerate(params, constraints)
+	// Ensure all 5 agg functions are covered by injecting explicit cases
+	vectors = ensureAggCoverage(vectors)
+	var cases []*TestCase
+
+	for vecIdx, tv := range vectors {
+		req := buildLayer3Request(m, tv)
+		if req == nil {
+			continue
+		}
+		ql, qlErr := RenderQL(req)
+		if qlErr != nil {
+			continue
+		}
+
+		disOrder := tv["order"] == "none" && tv["group"] == "true"
+		nameParts := []string{}
+		if tv["agg"] != "none" {
+			nameParts = append(nameParts, strings.ToLower(tv["agg"]))
+		}
+		if tv["top"] != "none" {
+			nameParts = append(nameParts, "top_"+tv["top"])
+		}
+		if tv["group"] == "true" {
+			nameParts = append(nameParts, "group")
+		}
+		if tv["order"] != "none" {
+			nameParts = append(nameParts, "order_"+tv["order"])
+		}
+		if tv["filter"] != "none" {
+			nameParts = append(nameParts, "filter")
+		}
+		if len(nameParts) == 0 {
+			nameParts = append(nameParts, "plain")
+		}
+		name := "gen_feat_" + strings.Join(nameParts, "_")
+		name = fmt.Sprintf("%s_%d", name, vecIdx)
+
+		cases = append(cases, &TestCase{
+			Name:     name,
+			Measure:  m,
+			Request:  req,
+			QL:       ql,
+			DisOrder: disOrder,
+			Duration: "25 * time.Minute",
+		})
+	}
+
+	return cases
+}
+
+func buildLayer3Request(m *Measure, tv TestVector) *measurev1.QueryRequest {
+	req := &measurev1.QueryRequest{
+		Name:            m.Name,
+		Groups:          []string{m.Group},
+		TagProjection:   BuildTagProjection(m, []string{"id", "entity_id"}),
+		FieldProjection: BuildFieldProjection([]string{"total", "value"}),
+	}
+
+	// Criteria
+	if tv["filter"] == "eq" {
+		cond := BuildCondition("id", modelv1.Condition_BINARY_OP_NE, TagValueStr("svc3"))
+		req.Criteria = BuildCriteriaFromCondition(cond)
+	}
+
+	// GroupBy
+	if tv["group"] == "true" {
+		req.GroupBy = &measurev1.QueryRequest_GroupBy{
+			TagProjection: BuildTagProjection(m, []string{"id"}),
+			FieldName:     "value",
+		}
+	}
+
+	// Aggregation
+	if tv["agg"] != "none" {
+		req.Agg = &measurev1.QueryRequest_Aggregation{
+			Function:  parseAggFunction(tv["agg"]),
+			FieldName: "value",
+		}
+	}
+
+	// TopN
+	if tv["top"] != "none" {
+		sortVal := modelv1.Sort_SORT_DESC
+		if tv["top"] == "asc" {
+			sortVal = modelv1.Sort_SORT_ASC
+		}
+		req.Top = &measurev1.QueryRequest_Top{
+			Number:         2,
+			FieldName:      "value",
+			FieldValueSort: sortVal,
+		}
+	}
+
+	// Order
+	if tv["order"] == "asc" {
+		req.OrderBy = &modelv1.QueryOrder{Sort: modelv1.Sort_SORT_ASC}
+	} else if tv["order"] == "desc" {
+		req.OrderBy = &modelv1.QueryOrder{Sort: modelv1.Sort_SORT_DESC}
+	}
+
+	return req
+}
+
+func parseAggFunction(name string) modelv1.AggregationFunction {
+	switch name {
+	case "MEAN":
+		return modelv1.AggregationFunction_AGGREGATION_FUNCTION_MEAN
+	case "MAX":
+		return modelv1.AggregationFunction_AGGREGATION_FUNCTION_MAX
+	case "MIN":
+		return modelv1.AggregationFunction_AGGREGATION_FUNCTION_MIN
+	case "COUNT":
+		return modelv1.AggregationFunction_AGGREGATION_FUNCTION_COUNT
+	case "SUM":
+		return modelv1.AggregationFunction_AGGREGATION_FUNCTION_SUM
+	default:
+		return modelv1.AggregationFunction_AGGREGATION_FUNCTION_UNSPECIFIED
+	}
+}
+
+// ensureAggCoverage adds explicit vectors for each agg function not covered by pairwise.
+func ensureAggCoverage(vectors []TestVector) []TestVector {
+	covered := make(map[string]bool)
+	for _, tv := range vectors {
+		covered[tv["agg"]] = true
+	}
+	aggFunctions := []string{"MEAN", "MAX", "MIN", "COUNT", "SUM"}
+	for _, aggFn := range aggFunctions {
+		if covered[aggFn] {
+			continue
+		}
+		// Add a vector with this agg function, group=true, varied other params
+		vectors = append(vectors, TestVector{
+			"agg":    aggFn,
+			"top":    "none",
+			"group":  "true",
+			"order":  "desc",
+			"filter": "none",
+		})
+	}
+	return vectors
+}
diff --git a/test/cases/measure/cmd/generate/main.go b/test/cases/measure/cmd/generate/main.go
new file mode 100644
index 000000000..d70ffd6fb
--- /dev/null
+++ b/test/cases/measure/cmd/generate/main.go
@@ -0,0 +1,121 @@
+package main
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+)
+
+func main() {
+	if len(os.Args) < 2 {
+		fmt.Fprintln(os.Stderr, "Usage: generate <generate|capture> [options]")
+		fmt.Fprintln(os.Stderr, "")
+		fmt.Fprintln(os.Stderr, "Subcommands:")
+		fmt.Fprintln(os.Stderr, "  generate [output-dir]  Generate .ql and input/*.yaml files")
+		fmt.Fprintln(os.Stderr, "  capture [output-dir] [server-addr]  Capture want/*.yaml from live server")
+		os.Exit(1)
+	}
+
+	switch os.Args[1] {
+	case "generate":
+		outputDir := "."
+		if len(os.Args) > 2 {
+			outputDir = os.Args[2]
+		}
+		runGenerate(outputDir)
+	case "capture":
+		outputDir := "."
+		if len(os.Args) > 2 {
+			outputDir = os.Args[2]
+		}
+		serverAddr := "localhost:17912"
+		if len(os.Args) > 3 {
+			serverAddr = os.Args[3]
+		}
+		runCapture(outputDir, serverAddr)
+	default:
+		fmt.Fprintf(os.Stderr, "Unknown subcommand: %s\n", os.Args[1])
+		os.Exit(1)
+	}
+}
+
+func runGenerate(outputDir string) {
+	inputDir := filepath.Join(outputDir, "input")
+
+	// Step 1: Analyze existing tests for gap detection
+	cm, gapErr := AnalyzeGaps(inputDir)
+	if gapErr != nil {
+		fmt.Fprintf(os.Stderr, "Warning: gap analysis failed: %v\n", gapErr)
+		cm = &CoverageMap{
+			BinaryOps:    make(map[string]bool),
+			AggFunctions: make(map[string]bool),
+			TreeDepths:   make(map[int]bool),
+			LogicalOps:   make(map[string]bool),
+			SortOrders:   make(map[string]bool),
+			Criteria:     make(map[string]bool),
+			TestNames:    make(map[string]bool),
+		}
+	}
+
+	// Step 2: Generate all layers
+	var allCases []*TestCase
+	allCases = append(allCases, GenerateLayer1()...)
+	allCases = append(allCases, GenerateLayer2()...)
+	allCases = append(allCases, GenerateLayer3()...)
+
+	// Step 3: Filter out already-covered cases
+	filtered := FilterGaps(allCases, cm)
+
+	// Step 4: Write files
+	inputDirPath := filepath.Join(outputDir, "input")
+	wantDirPath := filepath.Join(outputDir, "want")
+
+	if mkdirErr := os.MkdirAll(inputDirPath, 0o755); mkdirErr != nil {
+		fmt.Fprintf(os.Stderr, "Error creating input directory: %v\n", mkdirErr)
+		os.Exit(1)
+	}
+	if mkdirErr := os.MkdirAll(wantDirPath, 0o755); mkdirErr != nil {
+		fmt.Fprintf(os.Stderr, "Error creating want directory: %v\n", mkdirErr)
+		os.Exit(1)
+	}
+
+	writtenCount := 0
+	for _, tc := range filtered {
+		// Write .ql file
+		qlPath := filepath.Join(inputDirPath, tc.Name+".ql")
+		if writeErr := os.WriteFile(qlPath, tc.QLFileContent(), 0o644); writeErr != nil {
+			fmt.Fprintf(os.Stderr, "Error writing %s: %v\n", qlPath, writeErr)
+			continue
+		}
+
+		// Write input YAML file
+		yamlContent, yamlErr := tc.InputYAMLContent()
+		if yamlErr != nil {
+			fmt.Fprintf(os.Stderr, "Error marshaling YAML for %s: %v\n", tc.Name, yamlErr)
+			continue
+		}
+		yamlPath := filepath.Join(inputDirPath, tc.Name+".yaml")
+		if writeErr := os.WriteFile(yamlPath, yamlContent, 0o644); writeErr != nil {
+			fmt.Fprintf(os.Stderr, "Error writing %s: %v\n", yamlPath, writeErr)
+			continue
+		}
+
+		// WantErr and WantEmpty cases don't need want files
+		if !tc.WantErr && !tc.WantEmpty {
+			// Write placeholder want file (to be filled by capture)
+			wantPath := filepath.Join(wantDirPath, tc.Name+".yaml")
+			placeholder := []byte("# Placeholder - run 'capture' subcommand to fill this file\n")
+			if writeErr := os.WriteFile(wantPath, placeholder, 0o644); writeErr != nil {
+				fmt.Fprintf(os.Stderr, "Error writing %s: %v\n", wantPath, writeErr)
+				continue
+			}
+		}
+
+		writtenCount++
+	}
+
+	// Step 5: Print reports
+	PrintCoverageReport(cm, filtered)
+	fmt.Printf("\nWrote %d test cases to %s\n", writtenCount, outputDir)
+	PrintEntryCode(filtered)
+}
diff --git a/test/cases/measure/cmd/generate/pairwise.go b/test/cases/measure/cmd/generate/pairwise.go
new file mode 100644
index 000000000..fb4182a0e
--- /dev/null
+++ b/test/cases/measure/cmd/generate/pairwise.go
@@ -0,0 +1,155 @@
+package main
+
+// Pair holds a parameter-value pair for pairwise testing.
+type Pair struct {
+	Param string
+	Value string
+}
+
+// TestVector is a set of parameter-value assignments.
+type TestVector map[string]string
+
+// PairwiseGenerate generates a minimal set of test vectors covering all pairs
+// of parameter values using a simplified IPO (In-Parameter-Order) algorithm.
+func PairwiseGenerate(params map[string][]string, constraints []ConstraintFunc) []TestVector {
+	paramNames := sortedParamNames(params)
+	if len(paramNames) == 0 {
+		return nil
+	}
+
+	// Collect all required pairs
+	requiredPairs := collectAllPairs(params, constraints)
+
+	// Build test vectors using greedy pair covering
+	var vectors []TestVector
+	coveredPairs := make(map[Pair]bool)
+
+	for len(coveredPairs) < len(requiredPairs) {
+		tv := buildVector(params, paramNames, coveredPairs, constraints)
+		if tv == nil {
+			break
+		}
+		// Mark newly covered pairs
+		newCovers := 0
+		for _, pn := range paramNames {
+			for _, pn2 := range paramNames {
+				if pn >= pn2 {
+					continue
+				}
+				p := Pair{Param: pn + "=" + tv[pn], Value: pn2 + "=" + tv[pn2]}
+				if !coveredPairs[p] {
+					coveredPairs[p] = true
+					newCovers++
+				}
+			}
+		}
+		if newCovers == 0 {
+			break
+		}
+		vectors = append(vectors, tv)
+	}
+	return vectors
+}
+
+// ConstraintFunc returns false if the combination is invalid.
+type ConstraintFunc func(tv TestVector) bool
+
+func collectAllPairs(params map[string][]string, constraints []ConstraintFunc) map[Pair]bool {
+	pairs := make(map[Pair]bool)
+	paramNames := sortedParamNames(params)
+	for _, pn := range paramNames {
+		for _, pn2 := range paramNames {
+			if pn >= pn2 {
+				continue
+			}
+			for _, v1 := range params[pn] {
+				for _, v2 := range params[pn2] {
+					tv := TestVector{pn: v1, pn2: v2}
+					if validConstraints(tv, constraints) {
+						pairs[Pair{Param: pn + "=" + v1, Value: pn2 + "=" + v2}] = true
+					}
+				}
+			}
+		}
+	}
+	return pairs
+}
+
+func buildVector(params map[string][]string, paramNames []string, coveredPairs map[Pair]bool, constraints []ConstraintFunc) TestVector {
+	tv := make(TestVector)
+	fillVector(tv, params, paramNames, 0, coveredPairs, constraints)
+	return tv
+}
+
+func fillVector(tv TestVector, params map[string][]string, paramNames []string, paramIdx int, coveredPairs map[Pair]bool, constraints []ConstraintFunc) bool {
+	if paramIdx >= len(paramNames) {
+		return validConstraints(tv, constraints)
+	}
+	pn := paramNames[paramIdx]
+	bestVal := ""
+	bestScore := -1
+	for _, val := range params[pn] {
+		tv[pn] = val
+		if !validConstraints(tv, constraints) {
+			continue
+		}
+		score := countNewPairs(tv, paramNames, paramIdx, coveredPairs)
+		if score > bestScore {
+			bestScore = score
+			bestVal = val
+		}
+	}
+	if bestVal == "" {
+		// Fallback: pick first valid value
+		for _, val := range params[pn] {
+			tv[pn] = val
+			if validConstraints(tv, constraints) {
+				bestVal = val
+				break
+			}
+		}
+		if bestVal == "" {
+			tv[pn] = params[pn][0]
+		}
+	}
+	tv[pn] = bestVal
+	return fillVector(tv, params, paramNames, paramIdx+1, coveredPairs, constraints)
+}
+
+func countNewPairs(tv TestVector, paramNames []string, currentIdx int, coveredPairs map[Pair]bool) int {
+	count := 0
+	pn := paramNames[currentIdx]
+	for prevIdx := 0; prevIdx < currentIdx; prevIdx++ {
+		prevPn := paramNames[prevIdx]
+		p := Pair{Param: prevPn + "=" + tv[prevPn], Value: pn + "=" + tv[pn]}
+		if !coveredPairs[p] {
+			count++
+		}
+	}
+	return count
+}
+
+func validConstraints(tv TestVector, constraints []ConstraintFunc) bool {
+	for _, constraint := range constraints {
+		if !constraint(tv) {
+			return false
+		}
+	}
+	return true
+}
+
+func sortedParamNames(params map[string][]string) []string {
+	names := make([]string, 0, len(params))
+	for name := range params {
+		names = append(names, name)
+	}
+	// Deterministic order
+	for i := 0; i < len(names); i++ {
+		for j := i + 1; j < len(names); j++ {
+			if names[i] > names[j] {
+				names[i], names[j] = names[j], names[i]
+			}
+		}
+	}
+	return names
+}
diff --git a/test/cases/measure/cmd/generate/ql_renderer.go b/test/cases/measure/cmd/generate/ql_renderer.go
new file mode 100644
index 000000000..a019656ab
--- /dev/null
+++ b/test/cases/measure/cmd/generate/ql_renderer.go
@@ -0,0 +1,228 @@
+package main
+
+import (
+	"fmt"
+	"strings"
+
+	measurev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/measure/v1"
+	modelv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
+)
+
+// RenderQL converts a QueryRequest proto to a BanyanDB QL string.
+func RenderQL(req *measurev1.QueryRequest) (string, error) {
+	var parts []string
+
+	// SELECT projection
+	projection := renderProjection(req)
+	parts = append(parts, fmt.Sprintf("SELECT %s FROM MEASURE %s IN %s", projection, req.GetName(), strings.Join(req.GetGroups(), ",")))
+
+	// TIME — always required by the BydbQL service; actual range is injected by the test framework
+	parts = append(parts, "TIME > '-15m'")
+
+	// WHERE
+	if req.GetCriteria() != nil {
+		whereClause, whereErr := renderCriteria(req.GetCriteria())
+		if whereErr != nil {
+			return "", whereErr
+		}
+		parts = append(parts, "WHERE "+whereClause)
+	}
+
+	// GROUP BY
+	if req.GetGroupBy() != nil {
+		var groupCols []string
+		if tp := req.GetGroupBy().GetTagProjection(); tp != nil {
+			for _, family := range tp.GetTagFamilies() {
+				for _, tag := range family.GetTags() {
+					groupCols = append(groupCols, tag)
+				}
+			}
+		}
+		if fn := req.GetGroupBy().GetFieldName(); fn != "" {
+			groupCols = append(groupCols, fn+"::field")
+		}
+		if len(groupCols) > 0 {
+			parts = append(parts, "GROUP BY "+strings.Join(groupCols, ", "))
+		}
+	}
+
+	// ORDER BY
+	if req.GetOrderBy() != nil {
+		switch req.GetOrderBy().GetSort() {
+		case modelv1.Sort_SORT_ASC:
+			parts = append(parts, "ORDER BY ASC")
+		case modelv1.Sort_SORT_DESC:
+			parts = append(parts, "ORDER BY DESC")
+		}
+	}
+
+	// LIMIT / OFFSET
+	if req.GetLimit() > 0 {
+		parts = append(parts, fmt.Sprintf("LIMIT %d", req.GetLimit()))
+	}
+	if req.GetOffset() > 0 {
+		parts = append(parts, fmt.Sprintf("OFFSET %d", req.GetOffset()))
+	}
+
+	return strings.Join(parts, " "), nil
+}
+
+func renderProjection(req *measurev1.QueryRequest) string {
+	var cols []string
+
+	// TOP N in projection
+	if top := req.GetTop(); top != nil {
+		sortStr := "DESC"
+		if top.GetFieldValueSort() == modelv1.Sort_SORT_ASC {
+			sortStr = "ASC"
+		}
+		cols = append(cols, fmt.Sprintf("TOP %d %s %s", top.GetNumber(), top.GetFieldName(), sortStr))
+	}
+
+	// Tag projection
+	if tp := req.GetTagProjection(); tp != nil {
+		for _, family := range tp.GetTagFamilies() {
+			for _, tag := range family.GetTags() {
+				cols = append(cols, tag)
+			}
+		}
+	}
+
+	// Aggregation in projection
+	if agg := req.GetAgg(); agg != nil {
+		aggFn := aggFunctionName(agg.GetFunction())
+		fieldName := agg.GetFieldName()
+		cols = append(cols, fmt.Sprintf("%s(%s)", aggFn, fieldName))
+	}
+
+	// Field projection — all fields need the ::field suffix to disambiguate from tags
+	if fp := req.GetFieldProjection(); fp != nil {
+		for _, fieldName := range fp.GetNames() {
+			cols = append(cols, fieldName+"::field")
+		}
+	}
+
+	if len(cols) == 0 {
+		return "*"
+	}
+	return strings.Join(cols, ", ")
+}
+
+func aggFunctionName(fn modelv1.AggregationFunction) string {
+	switch fn {
+	case modelv1.AggregationFunction_AGGREGATION_FUNCTION_MEAN:
+		return "MEAN"
+	case modelv1.AggregationFunction_AGGREGATION_FUNCTION_MAX:
+		return "MAX"
+	case modelv1.AggregationFunction_AGGREGATION_FUNCTION_MIN:
+		return "MIN"
+	case modelv1.AggregationFunction_AGGREGATION_FUNCTION_COUNT:
+		return "COUNT"
+	case modelv1.AggregationFunction_AGGREGATION_FUNCTION_SUM:
+		return "SUM"
+	default:
+		return "SUM"
+	}
+}
+
+func renderCriteria(criteria *modelv1.Criteria) (string, error) {
+	switch c := criteria.GetExp().(type) {
+	case *modelv1.Criteria_Condition:
+		return renderCondition(c.Condition), nil
+	case *modelv1.Criteria_Le:
+		return renderLogicalExpr(c.Le)
+	default:
+		return "", fmt.Errorf("unknown criteria type")
+	}
+}
+
+func renderCondition(cond *modelv1.Condition) string {
+	name := cond.GetName()
+	op := cond.GetOp()
+	val := cond.GetValue()
+	valueStr := renderTagValue(val)
+	// MATCH and HAVING operations require parentheses around values (even single strings)
+	if op == modelv1.Condition_BINARY_OP_MATCH || op == modelv1.Condition_BINARY_OP_HAVING ||
+		op == modelv1.Condition_BINARY_OP_NOT_HAVING {
+		// If not already wrapped in parentheses (arrays are), wrap it
+		if !strings.HasPrefix(valueStr, "(") {
+			valueStr = fmt.Sprintf("(%s)", valueStr)
+		}
+	}
+	return fmt.Sprintf("%s %s %s", name, renderBinaryOp(op), valueStr)
+}
+
+func renderBinaryOp(op modelv1.Condition_BinaryOp) string {
+	switch op {
+	case modelv1.Condition_BINARY_OP_EQ:
+		return "="
+	case modelv1.Condition_BINARY_OP_NE:
+		return "!="
+	case modelv1.Condition_BINARY_OP_LT:
+		return "<"
+	case modelv1.Condition_BINARY_OP_GT:
+		return ">"
+	case modelv1.Condition_BINARY_OP_LE:
+		return "<="
+	case modelv1.Condition_BINARY_OP_GE:
+		return ">="
+	case modelv1.Condition_BINARY_OP_IN:
+		return "IN"
+	case modelv1.Condition_BINARY_OP_NOT_IN:
+		return "NOT IN"
+	case modelv1.Condition_BINARY_OP_MATCH:
+		return "MATCH"
+	case modelv1.Condition_BINARY_OP_HAVING:
+		return "HAVING"
+	case modelv1.Condition_BINARY_OP_NOT_HAVING:
+		return "NOT HAVING"
+	default:
+		return "="
+	}
+}
+
+func renderTagValue(tv *modelv1.TagValue) string {
+	if tv == nil {
+		return "NULL"
+	}
+	switch val := tv.GetValue().(type) {
+	case *modelv1.TagValue_Str:
+		return fmt.Sprintf("'%s'", val.Str.GetValue())
+	case *modelv1.TagValue_Int:
+		return fmt.Sprintf("%d", val.Int.GetValue())
+	case *modelv1.TagValue_StrArray:
+		strs := make([]string, 0, len(val.StrArray.GetValue()))
+		for _, strVal := range val.StrArray.GetValue() {
+			strs = append(strs, fmt.Sprintf("'%s'", strVal))
+		}
+		return "(" + strings.Join(strs, ", ") + ")"
+	case *modelv1.TagValue_IntArray:
+		intStrs := make([]string, 0, len(val.IntArray.GetValue()))
+		for _, intVal := range val.IntArray.GetValue() {
+			intStrs = append(intStrs, fmt.Sprintf("%d", intVal))
+		}
+		return "(" + strings.Join(intStrs, ", ") + ")"
+	case *modelv1.TagValue_Null:
+		return "NULL"
+	default:
+		return "NULL"
+	}
+}
+
+func renderLogicalExpr(le *modelv1.LogicalExpression) (string, error) {
+	opStr := "AND"
+	if le.GetOp() == modelv1.LogicalExpression_LOGICAL_OP_OR {
+		opStr = "OR"
+	}
+
+	leftStr, leftErr := renderCriteria(le.GetLeft())
+	if leftErr != nil {
+		return "", leftErr
+	}
+	rightStr, rightErr := renderCriteria(le.GetRight())
+	if rightErr != nil {
+		return "", rightErr
+	}
+
+	return fmt.Sprintf("(%s %s %s)", leftStr, opStr, rightStr), nil
+}
diff --git a/test/cases/measure/cmd/generate/seed.go b/test/cases/measure/cmd/generate/seed.go
new file mode 100644
index 000000000..e67816ee0
--- /dev/null
+++ b/test/cases/measure/cmd/generate/seed.go
@@ -0,0 +1,156 @@
+package main
+
+// TagType represents the type of a tag in the measure schema.
+type TagType int
+
+const (
+	TagTypeString TagType = iota
+	TagTypeInt
+	TagTypeStringArray
+	TagTypeIntArray
+)
+
+// FieldKind represents the type of a field in the measure schema.
+type FieldKind int
+
+const (
+	FieldKindInt FieldKind = iota
+	FieldKindFloat
+)
+
+// TagDef defines a tag in a measure schema.
+type TagDef struct {
+	Name     string
+	Type     TagType
+	Family   string // tag family name (e.g., "default")
+	IsEntity bool   // whether this tag is the entity
+}
+
+// FieldDef defines a field in a measure schema.
+type FieldDef struct {
+	Name string
+	Kind FieldKind
+}
+
+// DataPoint represents a single seed data point.
+type DataPoint struct {
+	TagValues   map[string]interface{} // tag name -> value (string, int64, []string, []int64)
+	FieldValues map[string]interface{} // field name -> value (int64, float64)
+}
+
+// Measure represents a test measure with its schema and seed data.
+type Measure struct {
+	Name       string
+	Group      string
+	Tags       []TagDef
+	Fields     []FieldDef
+	DataPoints []DataPoint
+	IndexMode  bool
+}
+
+// SeedData returns all target measures with their seed data.
+func SeedData() []Measure {
+	return []Measure{
+		serviceCPMMinute(),
+		serviceTraffic(),
+		serviceInstanceTraffic(),
+		instanceCPUCPUMinute(),
+	}
+}
+
+// FindMeasure returns a measure by name from the predefined seed data.
+func FindMeasure(name string) *Measure {
+	allMeasures := SeedData()
+	for idx := range allMeasures {
+		if allMeasures[idx].Name == name {
+			return &allMeasures[idx]
+		}
+	}
+	return nil
+}
+
+func serviceCPMMinute() Measure {
+	return Measure{
+		Name:  "service_cpm_minute",
+		Group: "sw_metric",
+		Tags: []TagDef{
+			{Name: "id", Type: TagTypeString, Family: "default"},
+			{Name: "entity_id", Type: TagTypeString, Family: "default", IsEntity: true},
+		},
+		Fields: []FieldDef{
+			{Name: "total", Kind: FieldKindInt},
+			{Name: "value", Kind: FieldKindInt},
+		},
+		DataPoints: []DataPoint{
+			{TagValues: map[string]interface{}{"id": "svc1", "entity_id": "entity_1"}, FieldValues: map[string]interface{}{"total": int64(100), "value": int64(1)}},
+			{TagValues: map[string]interface{}{"id": "svc1", "entity_id": "entity_2"}, FieldValues: map[string]interface{}{"total": int64(100), "value": int64(2)}},
+			{TagValues: map[string]interface{}{"id": "svc1", "entity_id": "entity_3"}, FieldValues: map[string]interface{}{"total": int64(100), "value": int64(3)}},
+			{TagValues: map[string]interface{}{"id": "svc2", "entity_id": "entity_4"}, FieldValues: map[string]interface{}{"total": int64(100), "value": int64(5)}},
+			{TagValues: map[string]interface{}{"id": "svc2", "entity_id": "entity_5"}, FieldValues: map[string]interface{}{"total": int64(50), "value": int64(4)}},
+			{TagValues: map[string]interface{}{"id": "svc3", "entity_id": "entity_6"}, FieldValues: map[string]interface{}{"total": int64(300), "value": int64(6)}},
+		},
+	}
+}
+
+func serviceTraffic() Measure {
+	return Measure{
+		Name:      "service_traffic",
+		Group:     "index_mode",
+		IndexMode: true,
+		Tags: []TagDef{
+			{Name: "id", Type: TagTypeString, Family: "default", IsEntity: true},
+			{Name: "service_id", Type: TagTypeString, Family: "default"},
+			{Name: "name", Type: TagTypeString, Family: "default"},
+			{Name: "short_name", Type: TagTypeString, Family: "default"},
+			{Name: "service_group", Type: TagTypeString, Family: "default"},
+			{Name: "layer", Type: TagTypeInt, Family: "default"},
+		},
+		Fields: []FieldDef{},
+		DataPoints: []DataPoint{
+			{TagValues: map[string]interface{}{"id": "1", "service_id": "service_1", "name": "service_name_1", "short_name": "service_short_name_1", "service_group": "group1", "layer": int64(1)}},
+			{TagValues: map[string]interface{}{"id": "2", "service_id": "service_2", "name": "service_name_2", "short_name": "service_short_name_2", "service_group": "group1", "layer": int64(2)}},
+			{TagValues: map[string]interface{}{"id": "3", "service_id": "service_3", "name": "service_name_3", "short_name": "service_short_name_3", "service_group": "group1", "layer": int64(0)}},
+		},
+	}
+}
+
+func serviceInstanceTraffic() Measure {
+	return Measure{
+		Name:  "service_instance_traffic",
+		Group: "sw_metric",
+		Tags: []TagDef{
+			{Name: "id", Type: TagTypeString, Family: "default", IsEntity: true},
+			{Name: "service_id", Type: TagTypeString, Family: "default"},
+			{Name: "name", Type: TagTypeString, Family: "default"},
+			{Name: "last_ping", Type: TagTypeInt, Family: "default"},
+			{Name: "layer", Type: TagTypeInt, Family: "default"},
+		},
+		Fields: []FieldDef{},
+		DataPoints: []DataPoint{
+			{TagValues: map[string]interface{}{"id": "1", "service_id": "svc_1", "name": "nodea@west-us", "last_ping": int64(100), "layer": int64(0)}},
+			{TagValues: map[string]interface{}{"id": "2", "service_id": "svc_1", "name": "nodeb@west-us", "last_ping": int64(100), "layer": int64(0)}},
+			{TagValues: map[string]interface{}{"id": "3", "service_id": "svc_1", "name": "nodea@east-cn", "last_ping": int64(100), "layer": int64(0)}},
+		},
+	}
+}
+
+func instanceCPUCPUMinute() Measure {
+	return Measure{
+		Name:  "instance_clr_cpu_minute",
+		Group: "sw_metric",
+		Tags: []TagDef{
+			{Name: "service_id", Type: TagTypeString, Family: "default"},
+			{Name: "entity_id", Type: TagTypeString, Family: "default", IsEntity: true},
+		},
+		Fields: []FieldDef{
+			{Name: "summation", Kind: FieldKindFloat},
+			{Name: "count", Kind: FieldKindInt},
+			{Name: "value", Kind: FieldKindFloat},
+		},
+		DataPoints: []DataPoint{
+			{TagValues: map[string]interface{}{"service_id": "1", "entity_id": "entity_1"}, FieldValues: map[string]interface{}{"summation": 18.1, "count": int64(1), "value": 18.1}},
+			{TagValues: map[string]interface{}{"service_id": "4", "entity_id": "entity_2"}, FieldValues: map[string]interface{}{"summation": 100.4, "count": int64(2), "value": 50.2}},
+			{TagValues: map[string]interface{}{"service_id": "5", "entity_id": "entity_2"}, FieldValues: map[string]interface{}{"summation": 100.0, "count": int64(3), "value": 33.333}},
+		},
+	}
+}
diff --git a/test/cases/measure/cmd/generate/types.go b/test/cases/measure/cmd/generate/types.go
new file mode 100644
index 000000000..c2d6cf4f5
--- /dev/null
+++ b/test/cases/measure/cmd/generate/types.go
@@ -0,0 +1,137 @@
+package main
+
+import (
+	measurev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/measure/v1"
+	modelv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
+	"google.golang.org/protobuf/encoding/protojson"
+	"sigs.k8s.io/yaml"
+)
+
+// TestCase represents a generated test case with all its artifacts.
+type TestCase struct {
+	Name      string
+	Measure   *Measure
+	Request   *measurev1.QueryRequest
+	QL        string // rendered QL query string
+	WantErr   bool
+	WantEmpty bool
+	DisOrder  bool
+	Duration  string // Go duration expression for helpers.Args
+}
+
+// WriteQLFile writes the .ql file content.
+func (tc *TestCase) QLFileContent() []byte {
+	return []byte(tc.QL + "\n")
+}
+
+// WriteInputYAML writes the input YAML file content.
+func (tc *TestCase) InputYAMLContent() ([]byte, error) {
+	marshaler := protojson.MarshalOptions{Multiline: true}
+	jsonBytes, marshalErr := marshaler.Marshal(tc.Request)
+	if marshalErr != nil {
+		return nil, marshalErr
+	}
+	return yaml.JSONToYAML(jsonBytes)
+}
+
+// BuildTagProjection creates a TagProjection for the given tag names.
+func BuildTagProjection(m *Measure, tagNames []string) *modelv1.TagProjection {
+	families := make(map[string][]string)
+	for _, tagName := range tagNames {
+		for _, tag := range m.Tags {
+			if tag.Name == tagName {
+				families[tag.Family] = append(families[tag.Family], tagName)
+				break
+			}
+		}
+	}
+	var tagFamilies []*modelv1.TagProjection_TagFamily
+	for familyName, tags := range families {
+		tagFamilies = append(tagFamilies, &modelv1.TagProjection_TagFamily{
+			Name: familyName,
+			Tags: tags,
+		})
+	}
+	return &modelv1.TagProjection{TagFamilies: tagFamilies}
+}
+
+// BuildFieldProjection creates a FieldProjection for the given field names.
+// Returns nil when fieldNames is empty so the proto marshals without the fieldProjection key.
+func BuildFieldProjection(fieldNames []string) *measurev1.QueryRequest_FieldProjection {
+	if len(fieldNames) == 0 {
+		return nil
+	}
+	return &measurev1.QueryRequest_FieldProjection{Names: fieldNames}
+}
+
+// BuildCondition creates a Condition proto.
+func BuildCondition(name string, op modelv1.Condition_BinaryOp, value *modelv1.TagValue) *modelv1.Condition {
+	return &modelv1.Condition{
+		Name:  name,
+		Op:    op,
+		Value: value,
+	}
+}
+
+// BuildCriteriaFromCondition wraps a Condition in a Criteria.
+func BuildCriteriaFromCondition(cond *modelv1.Condition) *modelv1.Criteria {
+	return &modelv1.Criteria{
+		Exp: &modelv1.Criteria_Condition{Condition: cond},
+	}
+}
+
+// BuildLogicalExpr creates a LogicalExpression Criteria.
+func BuildLogicalExpr(op modelv1.LogicalExpression_LogicalOp, left, right *modelv1.Criteria) *modelv1.Criteria {
+	return &modelv1.Criteria{
+		Exp: &modelv1.Criteria_Le{
+			Le: &modelv1.LogicalExpression{
+				Op:    op,
+				Left:  left,
+				Right: right,
+			},
+		},
+	}
+}
+
+// TagValueStr creates a string TagValue.
+func TagValueStr(val string) *modelv1.TagValue {
+	return &modelv1.TagValue{Value: &modelv1.TagValue_Str{Str: &modelv1.Str{Value: val}}}
+}
+
+// TagValueInt creates an int TagValue.
+func TagValueInt(val int64) *modelv1.TagValue {
+	return &modelv1.TagValue{Value: &modelv1.TagValue_Int{Int: &modelv1.Int{Value: val}}}
+}
+
+// TagValueStrArray creates a string array TagValue.
+func TagValueStrArray(vals []string) *modelv1.TagValue {
+	return &modelv1.TagValue{Value: &modelv1.TagValue_StrArray{StrArray: &modelv1.StrArray{Value: vals}}}
+}
+
+// TagValueIntArray creates an int array TagValue.
+func TagValueIntArray(vals []int64) *modelv1.TagValue {
+	return &modelv1.TagValue{Value: &modelv1.TagValue_IntArray{IntArray: &modelv1.IntArray{Value: vals}}}
+}
+
+// TagValueNull creates a null TagValue.
+func TagValueNull() *modelv1.TagValue {
+	return &modelv1.TagValue{Value: &modelv1.TagValue_Null{}}
+}
+
+// AllTagNames returns all tag names for a measure.
+func AllTagNames(m *Measure) []string {
+	names := make([]string, 0, len(m.Tags))
+	for _, tag := range m.Tags {
+		names = append(names, tag.Name)
+	}
+	return names
+}
+
+// AllFieldNames returns all field names for a measure.
+func AllFieldNames(m *Measure) []string {
+	names := make([]string, 0, len(m.Fields))
+	for _, field := range m.Fields {
+		names = append(names, field.Name)
+	}
+	return names
+}
diff --git a/test/cases/measure/data/input/complex_and_or.ql b/test/cases/measure/data/input/complex_and_or.ql
new file mode 100644
index 000000000..7f6567b13
--- /dev/null
+++ b/test/cases/measure/data/input/complex_and_or.ql
@@ -0,0 +1,21 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+SELECT id, entity_id, total, value FROM MEASURE service_cpm_minute IN sw_metric
+TIME > '-15m'
+WHERE (id = 'svc1' AND entity_id = 'entity_1') OR entity_id = 'entity_6'
diff --git a/test/cases/measure/data/input/complex_and_or.yaml b/test/cases/measure/data/input/complex_and_or.yaml
new file mode 100644
index 000000000..2d96ab1fb
--- /dev/null
+++ b/test/cases/measure/data/input/complex_and_or.yaml
@@ -0,0 +1,52 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: "service_cpm_minute"
+groups: ["sw_metric"]
+tagProjection:
+  tagFamilies:
+  - name: "default"
+    tags: ["id", "entity_id"]
+fieldProjection:
+  names: ["total", "value"]
+criteria:
+  le:
+    op: "LOGICAL_OP_OR"
+    left:
+      le:
+        op: "LOGICAL_OP_AND"
+        left:
+          condition:
+            name: "id"
+            op: "BINARY_OP_EQ"
+            value:
+              str:
+                value: "svc1"
+        right:
+          condition:
+            name: "entity_id"
+            op: "BINARY_OP_EQ"
+            value:
+              str:
+                value: "entity_1"
+    right:
+      condition:
+        name: "entity_id"
+        op: "BINARY_OP_EQ"
+        value:
+          str:
+            value: "entity_6"
diff --git a/test/cases/measure/data/input/gen_err_in_scalar.ql b/test/cases/measure/data/input/gen_err_in_scalar.ql
new file mode 100644
index 000000000..ab1c3634d
--- /dev/null
+++ b/test/cases/measure/data/input/gen_err_in_scalar.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total, value FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id IN ('svc1')
diff --git a/test/cases/measure/data/input/gen_err_in_scalar.yaml b/test/cases/measure/data/input/gen_err_in_scalar.yaml
new file mode 100644
index 000000000..4f4080f4b
--- /dev/null
+++ b/test/cases/measure/data/input/gen_err_in_scalar.yaml
@@ -0,0 +1,20 @@
+criteria:
+  condition:
+    name: id
+    op: BINARY_OP_IN
+    value:
+      str:
+        value: svc1
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_err_match_int.ql b/test/cases/measure/data/input/gen_err_match_int.ql
new file mode 100644
index 000000000..b735d670f
--- /dev/null
+++ b/test/cases/measure/data/input/gen_err_match_int.ql
@@ -0,0 +1 @@
+SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer MATCH 1
diff --git a/test/cases/measure/data/input/gen_err_match_int.yaml b/test/cases/measure/data/input/gen_err_match_int.yaml
new file mode 100644
index 000000000..26a639ad5
--- /dev/null
+++ b/test/cases/measure/data/input/gen_err_match_int.yaml
@@ -0,0 +1,20 @@
+criteria:
+  condition:
+    name: layer
+    op: BINARY_OP_MATCH
+    value:
+      int:
+        value: "1"
+groups:
+- index_mode
+name: service_traffic
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - service_id
+    - name
+    - short_name
+    - service_group
+    - layer
diff --git a/test/cases/measure/data/input/gen_feat_count_group_order_desc_8.ql b/test/cases/measure/data/input/gen_feat_count_group_order_desc_8.ql
new file mode 100644
index 000000000..969ee0d8f
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_count_group_order_desc_8.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, COUNT(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY DESC
diff --git a/test/cases/measure/data/input/gen_feat_count_group_order_desc_8.yaml b/test/cases/measure/data/input/gen_feat_count_group_order_desc_8.yaml
new file mode 100644
index 000000000..655496b34
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_count_group_order_desc_8.yaml
@@ -0,0 +1,25 @@
+agg:
+  fieldName: value
+  function: AGGREGATION_FUNCTION_COUNT
+fieldProjection:
+  names:
+  - total
+  - value
+groupBy:
+  fieldName: value
+  tagProjection:
+    tagFamilies:
+    - name: default
+      tags:
+      - id
+groups:
+- sw_metric
+name: service_cpm_minute
+orderBy:
+  sort: SORT_DESC
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_feat_max_group_order_desc_6.ql b/test/cases/measure/data/input/gen_feat_max_group_order_desc_6.ql
new file mode 100644
index 000000000..79fcdfc27
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_max_group_order_desc_6.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, MAX(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY DESC
diff --git a/test/cases/measure/data/input/gen_feat_max_group_order_desc_6.yaml b/test/cases/measure/data/input/gen_feat_max_group_order_desc_6.yaml
new file mode 100644
index 000000000..859253265
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_max_group_order_desc_6.yaml
@@ -0,0 +1,25 @@
+agg:
+  fieldName: value
+  function: AGGREGATION_FUNCTION_MAX
+fieldProjection:
+  names:
+  - total
+  - value
+groupBy:
+  fieldName: value
+  tagProjection:
+    tagFamilies:
+    - name: default
+      tags:
+      - id
+groups:
+- sw_metric
+name: service_cpm_minute
+orderBy:
+  sort: SORT_DESC
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_feat_mean_group_2.ql b/test/cases/measure/data/input/gen_feat_mean_group_2.ql
new file mode 100644
index 000000000..ceff68626
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_mean_group_2.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, MEAN(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field
diff --git a/test/cases/measure/data/input/gen_feat_mean_group_2.yaml b/test/cases/measure/data/input/gen_feat_mean_group_2.yaml
new file mode 100644
index 000000000..8594d4626
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_mean_group_2.yaml
@@ -0,0 +1,23 @@
+agg:
+  fieldName: value
+  function: AGGREGATION_FUNCTION_MEAN
+fieldProjection:
+  names:
+  - total
+  - value
+groupBy:
+  fieldName: value
+  tagProjection:
+    tagFamilies:
+    - name: default
+      tags:
+      - id
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_feat_mean_group_order_asc_5.ql b/test/cases/measure/data/input/gen_feat_mean_group_order_asc_5.ql
new file mode 100644
index 000000000..4605b2980
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_mean_group_order_asc_5.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, MEAN(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY ASC
diff --git a/test/cases/measure/data/input/gen_feat_mean_group_order_asc_5.yaml b/test/cases/measure/data/input/gen_feat_mean_group_order_asc_5.yaml
new file mode 100644
index 000000000..22d5b1305
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_mean_group_order_asc_5.yaml
@@ -0,0 +1,25 @@
+agg:
+  fieldName: value
+  function: AGGREGATION_FUNCTION_MEAN
+fieldProjection:
+  names:
+  - total
+  - value
+groupBy:
+  fieldName: value
+  tagProjection:
+    tagFamilies:
+    - name: default
+      tags:
+      - id
+groups:
+- sw_metric
+name: service_cpm_minute
+orderBy:
+  sort: SORT_ASC
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_asc_4.ql b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_asc_4.ql
new file mode 100644
index 000000000..ae6b98612
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_asc_4.ql
@@ -0,0 +1 @@
+SELECT TOP 2 value ASC, id, entity_id, MEAN(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY ASC
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_asc_4.yaml b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_asc_4.yaml
new file mode 100644
index 000000000..327c6dfe4
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_asc_4.yaml
@@ -0,0 +1,29 @@
+agg:
+  fieldName: value
+  function: AGGREGATION_FUNCTION_MEAN
+fieldProjection:
+  names:
+  - total
+  - value
+groupBy:
+  fieldName: value
+  tagProjection:
+    tagFamilies:
+    - name: default
+      tags:
+      - id
+groups:
+- sw_metric
+name: service_cpm_minute
+orderBy:
+  sort: SORT_ASC
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
+top:
+  fieldName: value
+  fieldValueSort: SORT_ASC
+  number: 2
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_desc_filter_1.ql b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_desc_filter_1.ql
new file mode 100644
index 000000000..57c3686e2
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_desc_filter_1.ql
@@ -0,0 +1 @@
+SELECT TOP 2 value ASC, id, entity_id, MEAN(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id != 'svc3' GROUP BY id, value::field ORDER BY DESC
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_desc_filter_1.yaml b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_desc_filter_1.yaml
new file mode 100644
index 000000000..27120871a
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_mean_top_asc_group_order_desc_filter_1.yaml
@@ -0,0 +1,36 @@
+agg:
+  fieldName: value
+  function: AGGREGATION_FUNCTION_MEAN
+criteria:
+  condition:
+    name: id
+    op: BINARY_OP_NE
+    value:
+      str:
+        value: svc3
+fieldProjection:
+  names:
+  - total
+  - value
+groupBy:
+  fieldName: value
+  tagProjection:
+    tagFamilies:
+    - name: default
+      tags:
+      - id
+groups:
+- sw_metric
+name: service_cpm_minute
+orderBy:
+  sort: SORT_DESC
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
+top:
+  fieldName: value
+  fieldValueSort: SORT_ASC
+  number: 2
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_asc_0.ql b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_asc_0.ql
new file mode 100644
index 000000000..9577d9cc1
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_asc_0.ql
@@ -0,0 +1 @@
+SELECT TOP 2 value DESC, id, entity_id, MEAN(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY ASC
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_asc_0.yaml b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_asc_0.yaml
new file mode 100644
index 000000000..054c90198
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_asc_0.yaml
@@ -0,0 +1,29 @@
+agg:
+  fieldName: value
+  function: AGGREGATION_FUNCTION_MEAN
+fieldProjection:
+  names:
+  - total
+  - value
+groupBy:
+  fieldName: value
+  tagProjection:
+    tagFamilies:
+    - name: default
+      tags:
+      - id
+groups:
+- sw_metric
+name: service_cpm_minute
+orderBy:
+  sort: SORT_ASC
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
+top:
+  fieldName: value
+  fieldValueSort: SORT_DESC
+  number: 2
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_desc_3.ql b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_desc_3.ql
new file mode 100644
index 000000000..c131c3c38
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_desc_3.ql
@@ -0,0 +1 @@
+SELECT TOP 2 value DESC, id, entity_id, MEAN(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY DESC
diff --git a/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_desc_3.yaml b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_desc_3.yaml
new file mode 100644
index 000000000..ead5c3c4e
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_mean_top_desc_group_order_desc_3.yaml
@@ -0,0 +1,29 @@
+agg:
+  fieldName: value
+  function: AGGREGATION_FUNCTION_MEAN
+fieldProjection:
+  names:
+  - total
+  - value
+groupBy:
+  fieldName: value
+  tagProjection:
+    tagFamilies:
+    - name: default
+      tags:
+      - id
+groups:
+- sw_metric
+name: service_cpm_minute
+orderBy:
+  sort: SORT_DESC
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
+top:
+  fieldName: value
+  fieldValueSort: SORT_DESC
+  number: 2
diff --git a/test/cases/measure/data/input/gen_feat_min_group_order_desc_7.ql b/test/cases/measure/data/input/gen_feat_min_group_order_desc_7.ql
new file mode 100644
index 000000000..53afd1020
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_min_group_order_desc_7.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, MIN(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY DESC
diff --git a/test/cases/measure/data/input/gen_feat_min_group_order_desc_7.yaml b/test/cases/measure/data/input/gen_feat_min_group_order_desc_7.yaml
new file mode 100644
index 000000000..2025a6474
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_min_group_order_desc_7.yaml
@@ -0,0 +1,25 @@
+agg:
+  fieldName: value
+  function: AGGREGATION_FUNCTION_MIN
+fieldProjection:
+  names:
+  - total
+  - value
+groupBy:
+  fieldName: value
+  tagProjection:
+    tagFamilies:
+    - name: default
+      tags:
+      - id
+groups:
+- sw_metric
+name: service_cpm_minute
+orderBy:
+  sort: SORT_DESC
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_feat_sum_group_order_desc_9.ql b/test/cases/measure/data/input/gen_feat_sum_group_order_desc_9.ql
new file mode 100644
index 000000000..7f8ad1269
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_sum_group_order_desc_9.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, SUM(value), total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' GROUP BY id, value::field ORDER BY DESC
diff --git a/test/cases/measure/data/input/gen_feat_sum_group_order_desc_9.yaml b/test/cases/measure/data/input/gen_feat_sum_group_order_desc_9.yaml
new file mode 100644
index 000000000..4aa15eed9
--- /dev/null
+++ b/test/cases/measure/data/input/gen_feat_sum_group_order_desc_9.yaml
@@ -0,0 +1,25 @@
+agg:
+  fieldName: value
+  function: AGGREGATION_FUNCTION_SUM
+fieldProjection:
+  names:
+  - total
+  - value
+groupBy:
+  fieldName: value
+  tagProjection:
+    tagFamilies:
+    - name: default
+      tags:
+      - id
+groups:
+- sw_metric
+name: service_cpm_minute
+orderBy:
+  sort: SORT_DESC
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_leaf_eq_int.ql b/test/cases/measure/data/input/gen_leaf_eq_int.ql
new file mode 100644
index 000000000..5c22c2b2f
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_eq_int.ql
@@ -0,0 +1 @@
+SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer = 1
diff --git a/test/cases/measure/data/input/gen_leaf_eq_int.yaml b/test/cases/measure/data/input/gen_leaf_eq_int.yaml
new file mode 100644
index 000000000..6c371d5a7
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_eq_int.yaml
@@ -0,0 +1,20 @@
+criteria:
+  condition:
+    name: layer
+    op: BINARY_OP_EQ
+    value:
+      int:
+        value: "1"
+groups:
+- index_mode
+name: service_traffic
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - service_id
+    - name
+    - short_name
+    - service_group
+    - layer
diff --git a/test/cases/measure/data/input/gen_leaf_eq_null.ql b/test/cases/measure/data/input/gen_leaf_eq_null.ql
new file mode 100644
index 000000000..4d02b619c
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_eq_null.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id = NULL
diff --git a/test/cases/measure/data/input/gen_leaf_eq_null.yaml b/test/cases/measure/data/input/gen_leaf_eq_null.yaml
new file mode 100644
index 000000000..7e2534a14
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_eq_null.yaml
@@ -0,0 +1,19 @@
+criteria:
+  condition:
+    name: id
+    op: BINARY_OP_EQ
+    value:
+      "null": null
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_leaf_eq_str.ql b/test/cases/measure/data/input/gen_leaf_eq_str.ql
new file mode 100644
index 000000000..10183bd55
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_eq_str.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id = 'svc1'
diff --git a/test/cases/measure/data/input/gen_leaf_eq_str.yaml b/test/cases/measure/data/input/gen_leaf_eq_str.yaml
new file mode 100644
index 000000000..6f4af72dd
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_eq_str.yaml
@@ -0,0 +1,20 @@
+criteria:
+  condition:
+    name: id
+    op: BINARY_OP_EQ
+    value:
+      str:
+        value: svc1
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_leaf_ge_int.ql b/test/cases/measure/data/input/gen_leaf_ge_int.ql
new file mode 100644
index 000000000..209aa666e
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_ge_int.ql
@@ -0,0 +1 @@
+SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer >= 2
diff --git a/test/cases/measure/data/input/gen_leaf_ge_int.yaml b/test/cases/measure/data/input/gen_leaf_ge_int.yaml
new file mode 100644
index 000000000..ace313a31
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_ge_int.yaml
@@ -0,0 +1,20 @@
+criteria:
+  condition:
+    name: layer
+    op: BINARY_OP_GE
+    value:
+      int:
+        value: "2"
+groups:
+- index_mode
+name: service_traffic
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - service_id
+    - name
+    - short_name
+    - service_group
+    - layer
diff --git a/test/cases/measure/data/input/gen_leaf_ge_str.ql b/test/cases/measure/data/input/gen_leaf_ge_str.ql
new file mode 100644
index 000000000..6561e12f5
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_ge_str.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id >= 'svc2'
diff --git a/test/cases/measure/data/input/gen_leaf_ge_str.yaml b/test/cases/measure/data/input/gen_leaf_ge_str.yaml
new file mode 100644
index 000000000..19011fe9b
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_ge_str.yaml
@@ -0,0 +1,20 @@
+criteria:
+  condition:
+    name: id
+    op: BINARY_OP_GE
+    value:
+      str:
+        value: svc2
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_leaf_gt_int.ql b/test/cases/measure/data/input/gen_leaf_gt_int.ql
new file mode 100644
index 000000000..c50f50068
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_gt_int.ql
@@ -0,0 +1 @@
+SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer > 1
diff --git a/test/cases/measure/data/input/gen_leaf_gt_int.yaml b/test/cases/measure/data/input/gen_leaf_gt_int.yaml
new file mode 100644
index 000000000..5ca122c74
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_gt_int.yaml
@@ -0,0 +1,20 @@
+criteria:
+  condition:
+    name: layer
+    op: BINARY_OP_GT
+    value:
+      int:
+        value: "1"
+groups:
+- index_mode
+name: service_traffic
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - service_id
+    - name
+    - short_name
+    - service_group
+    - layer
diff --git a/test/cases/measure/data/input/gen_leaf_gt_str.ql b/test/cases/measure/data/input/gen_leaf_gt_str.ql
new file mode 100644
index 000000000..2d2a1fa80
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_gt_str.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id > 'svc1'
diff --git a/test/cases/measure/data/input/gen_leaf_gt_str.yaml b/test/cases/measure/data/input/gen_leaf_gt_str.yaml
new file mode 100644
index 000000000..a36b80916
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_gt_str.yaml
@@ -0,0 +1,20 @@
+criteria:
+  condition:
+    name: id
+    op: BINARY_OP_GT
+    value:
+      str:
+        value: svc1
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_leaf_in_int_array.ql b/test/cases/measure/data/input/gen_leaf_in_int_array.ql
new file mode 100644
index 000000000..142b461be
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_in_int_array.ql
@@ -0,0 +1 @@
+SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer IN (1, 2)
diff --git a/test/cases/measure/data/input/gen_leaf_in_int_array.yaml b/test/cases/measure/data/input/gen_leaf_in_int_array.yaml
new file mode 100644
index 000000000..31d59776a
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_in_int_array.yaml
@@ -0,0 +1,22 @@
+criteria:
+  condition:
+    name: layer
+    op: BINARY_OP_IN
+    value:
+      intArray:
+        value:
+        - "1"
+        - "2"
+groups:
+- index_mode
+name: service_traffic
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - service_id
+    - name
+    - short_name
+    - service_group
+    - layer
diff --git a/test/cases/measure/data/input/gen_leaf_in_str_array.ql b/test/cases/measure/data/input/gen_leaf_in_str_array.ql
new file mode 100644
index 000000000..06b9130f3
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_in_str_array.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id IN ('svc1', 'svc2')
diff --git a/test/cases/measure/data/input/gen_leaf_in_str_array.yaml b/test/cases/measure/data/input/gen_leaf_in_str_array.yaml
new file mode 100644
index 000000000..675eda500
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_in_str_array.yaml
@@ -0,0 +1,22 @@
+criteria:
+  condition:
+    name: id
+    op: BINARY_OP_IN
+    value:
+      strArray:
+        value:
+        - svc1
+        - svc2
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_leaf_le_int.ql b/test/cases/measure/data/input/gen_leaf_le_int.ql
new file mode 100644
index 000000000..5b10971da
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_le_int.ql
@@ -0,0 +1 @@
+SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer <= 1
diff --git a/test/cases/measure/data/input/gen_leaf_le_int.yaml b/test/cases/measure/data/input/gen_leaf_le_int.yaml
new file mode 100644
index 000000000..c963d2742
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_le_int.yaml
@@ -0,0 +1,20 @@
+criteria:
+  condition:
+    name: layer
+    op: BINARY_OP_LE
+    value:
+      int:
+        value: "1"
+groups:
+- index_mode
+name: service_traffic
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - service_id
+    - name
+    - short_name
+    - service_group
+    - layer
diff --git a/test/cases/measure/data/input/gen_leaf_le_str.ql b/test/cases/measure/data/input/gen_leaf_le_str.ql
new file mode 100644
index 000000000..cbaf25f2c
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_le_str.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id <= 'svc2'
diff --git a/test/cases/measure/data/input/gen_leaf_le_str.yaml b/test/cases/measure/data/input/gen_leaf_le_str.yaml
new file mode 100644
index 000000000..89d11ff53
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_le_str.yaml
@@ -0,0 +1,20 @@
+criteria:
+  condition:
+    name: id
+    op: BINARY_OP_LE
+    value:
+      str:
+        value: svc2
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_leaf_lt_int.ql b/test/cases/measure/data/input/gen_leaf_lt_int.ql
new file mode 100644
index 000000000..29ba57c8f
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_lt_int.ql
@@ -0,0 +1 @@
+SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer < 2
diff --git a/test/cases/measure/data/input/gen_leaf_lt_int.yaml b/test/cases/measure/data/input/gen_leaf_lt_int.yaml
new file mode 100644
index 000000000..c83daddf2
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_lt_int.yaml
@@ -0,0 +1,20 @@
+criteria:
+  condition:
+    name: layer
+    op: BINARY_OP_LT
+    value:
+      int:
+        value: "2"
+groups:
+- index_mode
+name: service_traffic
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - service_id
+    - name
+    - short_name
+    - service_group
+    - layer
diff --git a/test/cases/measure/data/input/gen_leaf_lt_str.ql b/test/cases/measure/data/input/gen_leaf_lt_str.ql
new file mode 100644
index 000000000..d2d491a87
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_lt_str.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id < 'svc2'
diff --git a/test/cases/measure/data/input/gen_leaf_lt_str.yaml b/test/cases/measure/data/input/gen_leaf_lt_str.yaml
new file mode 100644
index 000000000..dece5fe4f
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_lt_str.yaml
@@ -0,0 +1,20 @@
+criteria:
+  condition:
+    name: id
+    op: BINARY_OP_LT
+    value:
+      str:
+        value: svc2
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_leaf_match_str.ql b/test/cases/measure/data/input/gen_leaf_match_str.ql
new file mode 100644
index 000000000..2e0611f00
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_match_str.ql
@@ -0,0 +1 @@
+SELECT id, service_id, name, last_ping, layer FROM MEASURE service_instance_traffic IN sw_metric TIME > '-15m' WHERE name MATCH ('nodea')
diff --git a/test/cases/measure/data/input/gen_leaf_match_str.yaml b/test/cases/measure/data/input/gen_leaf_match_str.yaml
new file mode 100644
index 000000000..787d942d6
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_match_str.yaml
@@ -0,0 +1,19 @@
+criteria:
+  condition:
+    name: name
+    op: BINARY_OP_MATCH
+    value:
+      str:
+        value: nodea
+groups:
+- sw_metric
+name: service_instance_traffic
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - service_id
+    - name
+    - last_ping
+    - layer
diff --git a/test/cases/measure/data/input/gen_leaf_ne_int.ql b/test/cases/measure/data/input/gen_leaf_ne_int.ql
new file mode 100644
index 000000000..0e7204fbf
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_ne_int.ql
@@ -0,0 +1 @@
+SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer != 1
diff --git a/test/cases/measure/data/input/gen_leaf_ne_int.yaml b/test/cases/measure/data/input/gen_leaf_ne_int.yaml
new file mode 100644
index 000000000..733d3c6e4
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_ne_int.yaml
@@ -0,0 +1,20 @@
+criteria:
+  condition:
+    name: layer
+    op: BINARY_OP_NE
+    value:
+      int:
+        value: "1"
+groups:
+- index_mode
+name: service_traffic
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - service_id
+    - name
+    - short_name
+    - service_group
+    - layer
diff --git a/test/cases/measure/data/input/gen_leaf_ne_null.ql b/test/cases/measure/data/input/gen_leaf_ne_null.ql
new file mode 100644
index 000000000..b54248c6e
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_ne_null.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id != NULL
diff --git a/test/cases/measure/data/input/gen_leaf_ne_null.yaml b/test/cases/measure/data/input/gen_leaf_ne_null.yaml
new file mode 100644
index 000000000..341604b05
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_ne_null.yaml
@@ -0,0 +1,19 @@
+criteria:
+  condition:
+    name: id
+    op: BINARY_OP_NE
+    value:
+      "null": null
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_leaf_ne_str.ql b/test/cases/measure/data/input/gen_leaf_ne_str.ql
new file mode 100644
index 000000000..da3bbab79
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_ne_str.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id != 'svc1'
diff --git a/test/cases/measure/data/input/gen_leaf_ne_str.yaml b/test/cases/measure/data/input/gen_leaf_ne_str.yaml
new file mode 100644
index 000000000..578cd8eda
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_ne_str.yaml
@@ -0,0 +1,20 @@
+criteria:
+  condition:
+    name: id
+    op: BINARY_OP_NE
+    value:
+      str:
+        value: svc1
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_leaf_not_in_int_array.ql b/test/cases/measure/data/input/gen_leaf_not_in_int_array.ql
new file mode 100644
index 000000000..8a0c6c416
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_not_in_int_array.ql
@@ -0,0 +1 @@
+SELECT id, service_id, name, short_name, service_group, layer FROM MEASURE service_traffic IN index_mode TIME > '-15m' WHERE layer NOT IN (0)
diff --git a/test/cases/measure/data/input/gen_leaf_not_in_int_array.yaml b/test/cases/measure/data/input/gen_leaf_not_in_int_array.yaml
new file mode 100644
index 000000000..9906692ee
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_not_in_int_array.yaml
@@ -0,0 +1,21 @@
+criteria:
+  condition:
+    name: layer
+    op: BINARY_OP_NOT_IN
+    value:
+      intArray:
+        value:
+        - "0"
+groups:
+- index_mode
+name: service_traffic
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - service_id
+    - name
+    - short_name
+    - service_group
+    - layer
diff --git a/test/cases/measure/data/input/gen_leaf_not_in_str_array.ql b/test/cases/measure/data/input/gen_leaf_not_in_str_array.ql
new file mode 100644
index 000000000..ed4a4fe2d
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_not_in_str_array.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id NOT IN ('svc3')
diff --git a/test/cases/measure/data/input/gen_leaf_not_in_str_array.yaml b/test/cases/measure/data/input/gen_leaf_not_in_str_array.yaml
new file mode 100644
index 000000000..4b500a562
--- /dev/null
+++ b/test/cases/measure/data/input/gen_leaf_not_in_str_array.yaml
@@ -0,0 +1,21 @@
+criteria:
+  condition:
+    name: id
+    op: BINARY_OP_NOT_IN
+    value:
+      strArray:
+        value:
+        - svc3
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_tree_depth1_leaf.ql b/test/cases/measure/data/input/gen_tree_depth1_leaf.ql
new file mode 100644
index 000000000..10183bd55
--- /dev/null
+++ b/test/cases/measure/data/input/gen_tree_depth1_leaf.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE id = 'svc1'
diff --git a/test/cases/measure/data/input/gen_tree_depth1_leaf.yaml b/test/cases/measure/data/input/gen_tree_depth1_leaf.yaml
new file mode 100644
index 000000000..6f4af72dd
--- /dev/null
+++ b/test/cases/measure/data/input/gen_tree_depth1_leaf.yaml
@@ -0,0 +1,20 @@
+criteria:
+  condition:
+    name: id
+    op: BINARY_OP_EQ
+    value:
+      str:
+        value: svc1
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_tree_depth2_and.ql b/test/cases/measure/data/input/gen_tree_depth2_and.ql
new file mode 100644
index 000000000..5e6351288
--- /dev/null
+++ b/test/cases/measure/data/input/gen_tree_depth2_and.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE (id = 'svc1' AND entity_id = 'entity_2')
diff --git a/test/cases/measure/data/input/gen_tree_depth2_and.yaml b/test/cases/measure/data/input/gen_tree_depth2_and.yaml
new file mode 100644
index 000000000..eefdad86d
--- /dev/null
+++ b/test/cases/measure/data/input/gen_tree_depth2_and.yaml
@@ -0,0 +1,30 @@
+criteria:
+  le:
+    left:
+      condition:
+        name: id
+        op: BINARY_OP_EQ
+        value:
+          str:
+            value: svc1
+    op: LOGICAL_OP_AND
+    right:
+      condition:
+        name: entity_id
+        op: BINARY_OP_EQ
+        value:
+          str:
+            value: entity_2
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_tree_depth2_or.ql b/test/cases/measure/data/input/gen_tree_depth2_or.ql
new file mode 100644
index 000000000..ba15d9e6e
--- /dev/null
+++ b/test/cases/measure/data/input/gen_tree_depth2_or.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE (id = 'svc1' OR entity_id = 'entity_2')
diff --git a/test/cases/measure/data/input/gen_tree_depth2_or.yaml b/test/cases/measure/data/input/gen_tree_depth2_or.yaml
new file mode 100644
index 000000000..3308529da
--- /dev/null
+++ b/test/cases/measure/data/input/gen_tree_depth2_or.yaml
@@ -0,0 +1,30 @@
+criteria:
+  le:
+    left:
+      condition:
+        name: id
+        op: BINARY_OP_EQ
+        value:
+          str:
+            value: svc1
+    op: LOGICAL_OP_OR
+    right:
+      condition:
+        name: entity_id
+        op: BINARY_OP_EQ
+        value:
+          str:
+            value: entity_2
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_tree_depth3_and_or.ql b/test/cases/measure/data/input/gen_tree_depth3_and_or.ql
new file mode 100644
index 000000000..e2ba77a27
--- /dev/null
+++ b/test/cases/measure/data/input/gen_tree_depth3_and_or.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE ((id = 'svc1' OR entity_id = 'entity_2') AND (id = 'svc3' OR entity_id = 'entity_4'))
diff --git a/test/cases/measure/data/input/gen_tree_depth3_and_or.yaml b/test/cases/measure/data/input/gen_tree_depth3_and_or.yaml
new file mode 100644
index 000000000..62ca0bf20
--- /dev/null
+++ b/test/cases/measure/data/input/gen_tree_depth3_and_or.yaml
@@ -0,0 +1,50 @@
+criteria:
+  le:
+    left:
+      le:
+        left:
+          condition:
+            name: id
+            op: BINARY_OP_EQ
+            value:
+              str:
+                value: svc1
+        op: LOGICAL_OP_OR
+        right:
+          condition:
+            name: entity_id
+            op: BINARY_OP_EQ
+            value:
+              str:
+                value: entity_2
+    op: LOGICAL_OP_AND
+    right:
+      le:
+        left:
+          condition:
+            name: id
+            op: BINARY_OP_EQ
+            value:
+              str:
+                value: svc3
+        op: LOGICAL_OP_OR
+        right:
+          condition:
+            name: entity_id
+            op: BINARY_OP_EQ
+            value:
+              str:
+                value: entity_4
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_tree_depth3_or_and.ql b/test/cases/measure/data/input/gen_tree_depth3_or_and.ql
new file mode 100644
index 000000000..bb7bf6e4d
--- /dev/null
+++ b/test/cases/measure/data/input/gen_tree_depth3_or_and.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE ((id = 'svc1' AND entity_id = 'entity_2') OR (id = 'svc3' AND entity_id = 'entity_4'))
diff --git a/test/cases/measure/data/input/gen_tree_depth3_or_and.yaml b/test/cases/measure/data/input/gen_tree_depth3_or_and.yaml
new file mode 100644
index 000000000..2ee0d2285
--- /dev/null
+++ b/test/cases/measure/data/input/gen_tree_depth3_or_and.yaml
@@ -0,0 +1,50 @@
+criteria:
+  le:
+    left:
+      le:
+        left:
+          condition:
+            name: id
+            op: BINARY_OP_EQ
+            value:
+              str:
+                value: svc1
+        op: LOGICAL_OP_AND
+        right:
+          condition:
+            name: entity_id
+            op: BINARY_OP_EQ
+            value:
+              str:
+                value: entity_2
+    op: LOGICAL_OP_OR
+    right:
+      le:
+        left:
+          condition:
+            name: id
+            op: BINARY_OP_EQ
+            value:
+              str:
+                value: svc3
+        op: LOGICAL_OP_AND
+        right:
+          condition:
+            name: entity_id
+            op: BINARY_OP_EQ
+            value:
+              str:
+                value: entity_4
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_tree_depth5_deep_and.ql b/test/cases/measure/data/input/gen_tree_depth5_deep_and.ql
new file mode 100644
index 000000000..53377cbbd
--- /dev/null
+++ b/test/cases/measure/data/input/gen_tree_depth5_deep_and.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE ((((id = 'svc1' OR entity_id = 'entity_2') AND (id = 'svc3' OR entity_id = 'entity_4')) OR ((id = 'svc2' OR entity_id = 'entity_6') AND (id = 'svc1' OR entity_id = 'entity_2'))) AND (((id = 'svc3' OR entity_id = 'entity_4') AND (id = 'svc2' OR entity_id = 'entity_6')) OR ((id = 'svc1' OR entity_id = 'entity_2') AND (id = 'svc3' OR entity_id = 'entity_4'))))
diff --git a/test/cases/measure/data/input/gen_tree_depth5_deep_and.yaml b/test/cases/measure/data/input/gen_tree_depth5_deep_and.yaml
new file mode 100644
index 000000000..4ae105904
--- /dev/null
+++ b/test/cases/measure/data/input/gen_tree_depth5_deep_and.yaml
@@ -0,0 +1,170 @@
+criteria:
+  le:
+    left:
+      le:
+        left:
+          le:
+            left:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc1
+                op: LOGICAL_OP_OR
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_2
+            op: LOGICAL_OP_AND
+            right:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc3
+                op: LOGICAL_OP_OR
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_4
+        op: LOGICAL_OP_OR
+        right:
+          le:
+            left:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc2
+                op: LOGICAL_OP_OR
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_6
+            op: LOGICAL_OP_AND
+            right:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc1
+                op: LOGICAL_OP_OR
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_2
+    op: LOGICAL_OP_AND
+    right:
+      le:
+        left:
+          le:
+            left:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc3
+                op: LOGICAL_OP_OR
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_4
+            op: LOGICAL_OP_AND
+            right:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc2
+                op: LOGICAL_OP_OR
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_6
+        op: LOGICAL_OP_OR
+        right:
+          le:
+            left:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc1
+                op: LOGICAL_OP_OR
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_2
+            op: LOGICAL_OP_AND
+            right:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc3
+                op: LOGICAL_OP_OR
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_4
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/gen_tree_depth5_deep_or.ql b/test/cases/measure/data/input/gen_tree_depth5_deep_or.ql
new file mode 100644
index 000000000..05b838c29
--- /dev/null
+++ b/test/cases/measure/data/input/gen_tree_depth5_deep_or.ql
@@ -0,0 +1 @@
+SELECT id, entity_id, total::field, value::field FROM MEASURE service_cpm_minute IN sw_metric TIME > '-15m' WHERE ((((id = 'svc1' AND entity_id = 'entity_2') OR (id = 'svc3' AND entity_id = 'entity_4')) AND ((id = 'svc2' AND entity_id = 'entity_6') OR (id = 'svc1' AND entity_id = 'entity_2'))) OR (((id = 'svc3' AND entity_id = 'entity_4') OR (id = 'svc2' AND entity_id = 'entity_6')) AND ((id = 'svc1' AND entity_id = 'entity_2') OR (id = 'svc3' AND entity_id = 'entity_4'))))
diff --git a/test/cases/measure/data/input/gen_tree_depth5_deep_or.yaml b/test/cases/measure/data/input/gen_tree_depth5_deep_or.yaml
new file mode 100644
index 000000000..fc4f81a29
--- /dev/null
+++ b/test/cases/measure/data/input/gen_tree_depth5_deep_or.yaml
@@ -0,0 +1,170 @@
+criteria:
+  le:
+    left:
+      le:
+        left:
+          le:
+            left:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc1
+                op: LOGICAL_OP_AND
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_2
+            op: LOGICAL_OP_OR
+            right:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc3
+                op: LOGICAL_OP_AND
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_4
+        op: LOGICAL_OP_AND
+        right:
+          le:
+            left:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc2
+                op: LOGICAL_OP_AND
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_6
+            op: LOGICAL_OP_OR
+            right:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc1
+                op: LOGICAL_OP_AND
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_2
+    op: LOGICAL_OP_OR
+    right:
+      le:
+        left:
+          le:
+            left:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc3
+                op: LOGICAL_OP_AND
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_4
+            op: LOGICAL_OP_OR
+            right:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc2
+                op: LOGICAL_OP_AND
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_6
+        op: LOGICAL_OP_AND
+        right:
+          le:
+            left:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc1
+                op: LOGICAL_OP_AND
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_2
+            op: LOGICAL_OP_OR
+            right:
+              le:
+                left:
+                  condition:
+                    name: id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: svc3
+                op: LOGICAL_OP_AND
+                right:
+                  condition:
+                    name: entity_id
+                    op: BINARY_OP_EQ
+                    value:
+                      str:
+                        value: entity_4
+fieldProjection:
+  names:
+  - total
+  - value
+groups:
+- sw_metric
+name: service_cpm_minute
+tagProjection:
+  tagFamilies:
+  - name: default
+    tags:
+    - id
+    - entity_id
diff --git a/test/cases/measure/data/input/group_sum_with_filter.ql b/test/cases/measure/data/input/group_sum_with_filter.ql
new file mode 100644
index 000000000..a6befa811
--- /dev/null
+++ b/test/cases/measure/data/input/group_sum_with_filter.ql
@@ -0,0 +1,22 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+SELECT id, value::field, SUM(value) FROM MEASURE service_cpm_minute IN sw_metric
+TIME > '-15m'
+WHERE id != 'svc3'
+GROUP BY id, value
diff --git a/test/cases/measure/data/input/group_sum_with_filter.yaml b/test/cases/measure/data/input/group_sum_with_filter.yaml
new file mode 100644
index 000000000..d080af237
--- /dev/null
+++ b/test/cases/measure/data/input/group_sum_with_filter.yaml
@@ -0,0 +1,41 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: "service_cpm_minute"
+groups: ["sw_metric"]
+tagProjection:
+  tagFamilies:
+  - name: "default"
+    tags: ["id"]
+fieldProjection:
+  names: ["value"]
+criteria:
+  condition:
+    name: "id"
+    op: "BINARY_OP_NE"
+    value:
+      str:
+        value: "svc3"
+groupBy:
+  tagProjection:
+    tagFamilies:
+    - name: "default"
+      tags: ["id"]
+  fieldName: "value"
+agg:
+  function: "AGGREGATION_FUNCTION_SUM"
+  fieldName: "value"
diff --git a/test/cases/measure/data/input/index_mode_le.ql b/test/cases/measure/data/input/index_mode_le.ql
new file mode 100644
index 000000000..d965dc4d7
--- /dev/null
+++ b/test/cases/measure/data/input/index_mode_le.ql
@@ -0,0 +1,21 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+SELECT id, service_id, layer FROM MEASURE service_traffic IN index_mode
+TIME > '-15m'
+WHERE layer <= 1
diff --git a/test/cases/measure/data/input/index_mode_le.yaml b/test/cases/measure/data/input/index_mode_le.yaml
new file mode 100644
index 000000000..25e5ad64b
--- /dev/null
+++ b/test/cases/measure/data/input/index_mode_le.yaml
@@ -0,0 +1,30 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: "service_traffic"
+groups: ["index_mode"]
+tagProjection:
+  tagFamilies:
+  - name: "default"
+    tags: ["id", "service_id", "layer"]
+criteria:
+  condition:
+    name: "layer"
+    op: "BINARY_OP_LE"
+    value:
+      int:
+        value: "1"
diff --git a/test/cases/measure/data/input/index_mode_ne.ql b/test/cases/measure/data/input/index_mode_ne.ql
new file mode 100644
index 000000000..91b402ac3
--- /dev/null
+++ b/test/cases/measure/data/input/index_mode_ne.ql
@@ -0,0 +1,21 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+SELECT id, service_id, name, layer FROM MEASURE service_traffic IN index_mode
+TIME > '-15m'
+WHERE service_id != 'service_1'
diff --git a/test/cases/measure/data/input/index_mode_ne.yaml b/test/cases/measure/data/input/index_mode_ne.yaml
new file mode 100644
index 000000000..6227eeef1
--- /dev/null
+++ b/test/cases/measure/data/input/index_mode_ne.yaml
@@ -0,0 +1,30 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: "service_traffic"
+groups: ["index_mode"]
+tagProjection:
+  tagFamilies:
+  - name: "default"
+    tags: ["id", "service_id", "name", "layer"]
+criteria:
+  condition:
+    name: "service_id"
+    op: "BINARY_OP_NE"
+    value:
+      str:
+        value: "service_1"
diff --git a/test/cases/measure/data/input/offset_empty.ql b/test/cases/measure/data/input/offset_empty.ql
new file mode 100644
index 000000000..7e9eb9661
--- /dev/null
+++ b/test/cases/measure/data/input/offset_empty.ql
@@ -0,0 +1,22 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+SELECT id, entity_id, total, value FROM MEASURE service_cpm_minute IN sw_metric
+TIME > '-15m'
+LIMIT 10
+OFFSET 6
diff --git a/test/cases/measure/data/input/offset_empty.yaml b/test/cases/measure/data/input/offset_empty.yaml
new file mode 100644
index 000000000..57e433fc6
--- /dev/null
+++ b/test/cases/measure/data/input/offset_empty.yaml
@@ -0,0 +1,27 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: "service_cpm_minute"
+groups: ["sw_metric"]
+tagProjection:
+  tagFamilies:
+  - name: "default"
+    tags: ["id", "entity_id"]
+fieldProjection:
+  names: ["total", "value"]
+limit: 10
+offset: 6
diff --git a/test/cases/measure/data/input/tag_filter_ge.ql b/test/cases/measure/data/input/tag_filter_ge.ql
new file mode 100644
index 000000000..c8d30d768
--- /dev/null
+++ b/test/cases/measure/data/input/tag_filter_ge.ql
@@ -0,0 +1,21 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+SELECT id, entity_id, total, value FROM MEASURE service_cpm_minute IN sw_metric
+TIME > '-15m'
+WHERE id >= 'svc2'
diff --git a/test/cases/measure/data/input/tag_filter_ge.yaml b/test/cases/measure/data/input/tag_filter_ge.yaml
new file mode 100644
index 000000000..d6d61053b
--- /dev/null
+++ b/test/cases/measure/data/input/tag_filter_ge.yaml
@@ -0,0 +1,32 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: "service_cpm_minute"
+groups: ["sw_metric"]
+tagProjection:
+  tagFamilies:
+  - name: "default"
+    tags: ["id", "entity_id"]
+fieldProjection:
+  names: ["total", "value"]
+criteria:
+  condition:
+    name: "id"
+    op: "BINARY_OP_GE"
+    value:
+      str:
+        value: "svc2"
diff --git a/test/cases/measure/data/input/tag_filter_ne.ql b/test/cases/measure/data/input/tag_filter_ne.ql
new file mode 100644
index 000000000..a147081fc
--- /dev/null
+++ b/test/cases/measure/data/input/tag_filter_ne.ql
@@ -0,0 +1,21 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+SELECT id, entity_id, total, value FROM MEASURE service_cpm_minute IN sw_metric
+TIME > '-15m'
+WHERE id != 'svc1'
diff --git a/test/cases/measure/data/input/tag_filter_ne.yaml b/test/cases/measure/data/input/tag_filter_ne.yaml
new file mode 100644
index 000000000..c0826e280
--- /dev/null
+++ b/test/cases/measure/data/input/tag_filter_ne.yaml
@@ -0,0 +1,32 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: "service_cpm_minute"
+groups: ["sw_metric"]
+tagProjection:
+  tagFamilies:
+  - name: "default"
+    tags: ["id", "entity_id"]
+fieldProjection:
+  names: ["total", "value"]
+criteria:
+  condition:
+    name: "id"
+    op: "BINARY_OP_NE"
+    value:
+      str:
+        value: "svc1"
diff --git a/test/cases/measure/data/input/tag_filter_not_in.ql b/test/cases/measure/data/input/tag_filter_not_in.ql
new file mode 100644
index 000000000..845991713
--- /dev/null
+++ b/test/cases/measure/data/input/tag_filter_not_in.ql
@@ -0,0 +1,21 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+SELECT id, entity_id, total, value FROM MEASURE service_cpm_minute IN sw_metric
+TIME > '-15m'
+WHERE id NOT IN ('svc2')
diff --git a/test/cases/measure/data/input/tag_filter_not_in.yaml b/test/cases/measure/data/input/tag_filter_not_in.yaml
new file mode 100644
index 000000000..583b24d72
--- /dev/null
+++ b/test/cases/measure/data/input/tag_filter_not_in.yaml
@@ -0,0 +1,32 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: "service_cpm_minute"
+groups: ["sw_metric"]
+tagProjection:
+  tagFamilies:
+  - name: "default"
+    tags: ["id", "entity_id"]
+fieldProjection:
+  names: ["total", "value"]
+criteria:
+  condition:
+    name: "id"
+    op: "BINARY_OP_NOT_IN"
+    value:
+      str_array:
+        value: ["svc2"]
diff --git a/test/cases/measure/data/input/top_with_filter.ql b/test/cases/measure/data/input/top_with_filter.ql
new file mode 100644
index 000000000..4f76d8313
--- /dev/null
+++ b/test/cases/measure/data/input/top_with_filter.ql
@@ -0,0 +1,22 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+SELECT TOP 2 value DESC, id, value::field, MEAN(value) FROM MEASURE service_cpm_minute IN sw_metric
+TIME > '-15m'
+WHERE id != 'svc3'
+GROUP BY id, value
diff --git a/test/cases/measure/data/input/top_with_filter.yaml b/test/cases/measure/data/input/top_with_filter.yaml
new file mode 100644
index 000000000..bb2e1b5fc
--- /dev/null
+++ b/test/cases/measure/data/input/top_with_filter.yaml
@@ -0,0 +1,45 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: "service_cpm_minute"
+groups: ["sw_metric"]
+tagProjection:
+  tagFamilies:
+  - name: "default"
+    tags: ["id"]
+fieldProjection:
+  names: ["value"]
+criteria:
+  condition:
+    name: "id"
+    op: "BINARY_OP_NE"
+    value:
+      str:
+        value: "svc3"
+groupBy:
+  tagProjection:
+    tagFamilies:
+    - name: "default"
+      tags: ["id"]
+  fieldName: "value"
+agg:
+  function: "AGGREGATION_FUNCTION_MEAN"
+  fieldName: "value"
+top:
+  number: 2
+  fieldName: "value"
+  fieldValueSort: "SORT_DESC"
diff --git a/test/cases/measure/data/want/complex_and_or.yaml b/test/cases/measure/data/want/complex_and_or.yaml
new file mode 100644
index 000000000..bf83b4ebd
--- /dev/null
+++ b/test/cases/measure/data/want/complex_and_or.yaml
@@ -0,0 +1,58 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "300"
+  - name: value
+    value:
+      int:
+        value: "6"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc3
+    - key: entity_id
+      value:
+        str:
+          value: entity_6
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "1"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_1
diff --git a/test/cases/measure/data/want/gen_feat_count_group_order_desc_8.yaml b/test/cases/measure/data/want/gen_feat_count_group_order_desc_8.yaml
new file mode 100644
index 000000000..2450bddf2
--- /dev/null
+++ b/test/cases/measure/data/want/gen_feat_count_group_order_desc_8.yaml
@@ -0,0 +1,49 @@
+dataPoints:
+- fields:
+  - name: value
+    value:
+      int:
+        value: "1"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc3
+    - key: entity_id
+      value:
+        str:
+          value: entity_6
+- fields:
+  - name: value
+    value:
+      int:
+        value: "2"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_5
+- fields:
+  - name: value
+    value:
+      int:
+        value: "3"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_3
diff --git a/test/cases/measure/data/want/gen_feat_max_group_order_desc_6.yaml b/test/cases/measure/data/want/gen_feat_max_group_order_desc_6.yaml
new file mode 100644
index 000000000..44cdaa245
--- /dev/null
+++ b/test/cases/measure/data/want/gen_feat_max_group_order_desc_6.yaml
@@ -0,0 +1,49 @@
+dataPoints:
+- fields:
+  - name: value
+    value:
+      int:
+        value: "6"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc3
+    - key: entity_id
+      value:
+        str:
+          value: entity_6
+- fields:
+  - name: value
+    value:
+      int:
+        value: "5"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_5
+- fields:
+  - name: value
+    value:
+      int:
+        value: "3"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_3
diff --git a/test/cases/measure/data/want/gen_feat_mean_group_2.yaml b/test/cases/measure/data/want/gen_feat_mean_group_2.yaml
new file mode 100644
index 000000000..f38fb2306
--- /dev/null
+++ b/test/cases/measure/data/want/gen_feat_mean_group_2.yaml
@@ -0,0 +1,49 @@
+dataPoints:
+- fields:
+  - name: value
+    value:
+      int:
+        value: "2"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_1
+- fields:
+  - name: value
+    value:
+      int:
+        value: "4"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_4
+- fields:
+  - name: value
+    value:
+      int:
+        value: "6"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc3
+    - key: entity_id
+      value:
+        str:
+          value: entity_6
diff --git a/test/cases/measure/data/want/gen_feat_mean_group_order_asc_5.yaml b/test/cases/measure/data/want/gen_feat_mean_group_order_asc_5.yaml
new file mode 100644
index 000000000..f38fb2306
--- /dev/null
+++ b/test/cases/measure/data/want/gen_feat_mean_group_order_asc_5.yaml
@@ -0,0 +1,49 @@
+dataPoints:
+- fields:
+  - name: value
+    value:
+      int:
+        value: "2"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_1
+- fields:
+  - name: value
+    value:
+      int:
+        value: "4"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_4
+- fields:
+  - name: value
+    value:
+      int:
+        value: "6"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc3
+    - key: entity_id
+      value:
+        str:
+          value: entity_6
diff --git a/test/cases/measure/data/want/gen_feat_mean_top_asc_group_order_asc_4.yaml b/test/cases/measure/data/want/gen_feat_mean_top_asc_group_order_asc_4.yaml
new file mode 100644
index 000000000..b6c334652
--- /dev/null
+++ b/test/cases/measure/data/want/gen_feat_mean_top_asc_group_order_asc_4.yaml
@@ -0,0 +1,33 @@
+dataPoints:
+- fields:
+  - name: value
+    value:
+      int:
+        value: "2"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_1
+- fields:
+  - name: value
+    value:
+      int:
+        value: "4"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_4
diff --git a/test/cases/measure/data/want/gen_feat_mean_top_asc_group_order_desc_filter_1.yaml b/test/cases/measure/data/want/gen_feat_mean_top_asc_group_order_desc_filter_1.yaml
new file mode 100644
index 000000000..1f9d7218b
--- /dev/null
+++ b/test/cases/measure/data/want/gen_feat_mean_top_asc_group_order_desc_filter_1.yaml
@@ -0,0 +1,33 @@
+dataPoints:
+- fields:
+  - name: value
+    value:
+      int:
+        value: "2"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_3
+- fields:
+  - name: value
+    value:
+      int:
+        value: "4"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_5
diff --git a/test/cases/measure/data/want/gen_feat_mean_top_desc_group_order_asc_0.yaml b/test/cases/measure/data/want/gen_feat_mean_top_desc_group_order_asc_0.yaml
new file mode 100644
index 000000000..f3b31794d
--- /dev/null
+++ b/test/cases/measure/data/want/gen_feat_mean_top_desc_group_order_asc_0.yaml
@@ -0,0 +1,33 @@
+dataPoints:
+- fields:
+  - name: value
+    value:
+      int:
+        value: "6"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc3
+    - key: entity_id
+      value:
+        str:
+          value: entity_6
+- fields:
+  - name: value
+    value:
+      int:
+        value: "4"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_4
diff --git a/test/cases/measure/data/want/gen_feat_mean_top_desc_group_order_desc_3.yaml b/test/cases/measure/data/want/gen_feat_mean_top_desc_group_order_desc_3.yaml
new file mode 100644
index 000000000..876638f23
--- /dev/null
+++ b/test/cases/measure/data/want/gen_feat_mean_top_desc_group_order_desc_3.yaml
@@ -0,0 +1,33 @@
+dataPoints:
+- fields:
+  - name: value
+    value:
+      int:
+        value: "6"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc3
+    - key: entity_id
+      value:
+        str:
+          value: entity_6
+- fields:
+  - name: value
+    value:
+      int:
+        value: "4"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_5
diff --git a/test/cases/measure/data/want/gen_feat_min_group_order_desc_7.yaml b/test/cases/measure/data/want/gen_feat_min_group_order_desc_7.yaml
new file mode 100644
index 000000000..6dc3bd75a
--- /dev/null
+++ b/test/cases/measure/data/want/gen_feat_min_group_order_desc_7.yaml
@@ -0,0 +1,49 @@
+dataPoints:
+- fields:
+  - name: value
+    value:
+      int:
+        value: "6"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc3
+    - key: entity_id
+      value:
+        str:
+          value: entity_6
+- fields:
+  - name: value
+    value:
+      int:
+        value: "4"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_5
+- fields:
+  - name: value
+    value:
+      int:
+        value: "1"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_3
diff --git a/test/cases/measure/data/want/gen_feat_sum_group_order_desc_9.yaml b/test/cases/measure/data/want/gen_feat_sum_group_order_desc_9.yaml
new file mode 100644
index 000000000..3c2c38d24
--- /dev/null
+++ b/test/cases/measure/data/want/gen_feat_sum_group_order_desc_9.yaml
@@ -0,0 +1,49 @@
+dataPoints:
+- fields:
+  - name: value
+    value:
+      int:
+        value: "6"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc3
+    - key: entity_id
+      value:
+        str:
+          value: entity_6
+- fields:
+  - name: value
+    value:
+      int:
+        value: "9"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_5
+- fields:
+  - name: value
+    value:
+      int:
+        value: "6"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_3
diff --git a/test/cases/measure/data/want/gen_leaf_eq_int.yaml b/test/cases/measure/data/want/gen_leaf_eq_int.yaml
new file mode 100644
index 000000000..a9d96b64a
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_eq_int.yaml
@@ -0,0 +1,31 @@
+dataPoints:
+- sid: "15142466043926325685"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "1"
+    - key: service_id
+      value:
+        str:
+          value: service_1
+    - key: name
+      value:
+        str:
+          value: service_name_1
+    - key: short_name
+      value:
+        str:
+          value: service_short_name_1
+    - key: service_group
+      value:
+        str:
+          value: group1
+    - key: layer
+      value:
+        int:
+          value: "1"
+  timestamp: "2026-04-15T01:02:59Z"
+  version: "1776215003965297926"
diff --git a/test/cases/measure/data/want/gen_leaf_eq_str.yaml b/test/cases/measure/data/want/gen_leaf_eq_str.yaml
new file mode 100644
index 000000000..86b692a1a
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_eq_str.yaml
@@ -0,0 +1,70 @@
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "1"
+  sid: "17296083384258897030"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_1
+  timestamp: "2026-04-15T01:02:57.500Z"
+  version: "1776215004052000314"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "2"
+  sid: "13293155473432710288"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_2
+  timestamp: "2026-04-15T01:02:58Z"
+  version: "1776215004052042343"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "3"
+  sid: "8985479151150409059"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_3
+  timestamp: "2026-04-15T01:02:58.500Z"
+  version: "1776215004052067554"
diff --git a/test/cases/measure/data/want/gen_leaf_ge_int.yaml b/test/cases/measure/data/want/gen_leaf_ge_int.yaml
new file mode 100644
index 000000000..72b42fcc4
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_ge_int.yaml
@@ -0,0 +1,31 @@
+dataPoints:
+- sid: "3906119849472468294"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "2"
+    - key: service_id
+      value:
+        str:
+          value: service_2
+    - key: name
+      value:
+        str:
+          value: service_name_2
+    - key: short_name
+      value:
+        str:
+          value: service_short_name_2
+    - key: service_group
+      value:
+        str:
+          value: group1
+    - key: layer
+      value:
+        int:
+          value: "2"
+  timestamp: "2026-04-15T01:02:59.500Z"
+  version: "1776215003965417936"
diff --git a/test/cases/measure/data/want/gen_leaf_ge_str.yaml b/test/cases/measure/data/want/gen_leaf_ge_str.yaml
new file mode 100644
index 000000000..fd793e1fe
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_ge_str.yaml
@@ -0,0 +1,70 @@
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "5"
+  sid: "11425316604636028751"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_4
+  timestamp: "2026-04-15T01:02:59Z"
+  version: "1776215004052089874"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "50"
+  - name: value
+    value:
+      int:
+        value: "4"
+  sid: "13633132181018445499"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_5
+  timestamp: "2026-04-15T01:02:59.500Z"
+  version: "1776215004052119554"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "300"
+  - name: value
+    value:
+      int:
+        value: "6"
+  sid: "12160318715725963202"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc3
+    - key: entity_id
+      value:
+        str:
+          value: entity_6
+  timestamp: "2026-04-15T01:03:00Z"
+  version: "1776215004052179134"
diff --git a/test/cases/measure/data/want/gen_leaf_gt_int.yaml b/test/cases/measure/data/want/gen_leaf_gt_int.yaml
new file mode 100644
index 000000000..72b42fcc4
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_gt_int.yaml
@@ -0,0 +1,31 @@
+dataPoints:
+- sid: "3906119849472468294"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "2"
+    - key: service_id
+      value:
+        str:
+          value: service_2
+    - key: name
+      value:
+        str:
+          value: service_name_2
+    - key: short_name
+      value:
+        str:
+          value: service_short_name_2
+    - key: service_group
+      value:
+        str:
+          value: group1
+    - key: layer
+      value:
+        int:
+          value: "2"
+  timestamp: "2026-04-15T01:02:59.500Z"
+  version: "1776215003965417936"
diff --git a/test/cases/measure/data/want/gen_leaf_gt_str.yaml b/test/cases/measure/data/want/gen_leaf_gt_str.yaml
new file mode 100644
index 000000000..fd793e1fe
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_gt_str.yaml
@@ -0,0 +1,70 @@
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "5"
+  sid: "11425316604636028751"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_4
+  timestamp: "2026-04-15T01:02:59Z"
+  version: "1776215004052089874"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "50"
+  - name: value
+    value:
+      int:
+        value: "4"
+  sid: "13633132181018445499"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_5
+  timestamp: "2026-04-15T01:02:59.500Z"
+  version: "1776215004052119554"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "300"
+  - name: value
+    value:
+      int:
+        value: "6"
+  sid: "12160318715725963202"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc3
+    - key: entity_id
+      value:
+        str:
+          value: entity_6
+  timestamp: "2026-04-15T01:03:00Z"
+  version: "1776215004052179134"
diff --git a/test/cases/measure/data/want/gen_leaf_in_int_array.yaml b/test/cases/measure/data/want/gen_leaf_in_int_array.yaml
new file mode 100644
index 000000000..16346cb66
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_in_int_array.yaml
@@ -0,0 +1,61 @@
+dataPoints:
+- sid: "15142466043926325685"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "1"
+    - key: service_id
+      value:
+        str:
+          value: service_1
+    - key: name
+      value:
+        str:
+          value: service_name_1
+    - key: short_name
+      value:
+        str:
+          value: service_short_name_1
+    - key: service_group
+      value:
+        str:
+          value: group1
+    - key: layer
+      value:
+        int:
+          value: "1"
+  timestamp: "2026-04-15T01:02:59Z"
+  version: "1776215003965297926"
+- sid: "3906119849472468294"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "2"
+    - key: service_id
+      value:
+        str:
+          value: service_2
+    - key: name
+      value:
+        str:
+          value: service_name_2
+    - key: short_name
+      value:
+        str:
+          value: service_short_name_2
+    - key: service_group
+      value:
+        str:
+          value: group1
+    - key: layer
+      value:
+        int:
+          value: "2"
+  timestamp: "2026-04-15T01:02:59.500Z"
+  version: "1776215003965417936"
diff --git a/test/cases/measure/data/want/gen_leaf_in_str_array.yaml b/test/cases/measure/data/want/gen_leaf_in_str_array.yaml
new file mode 100644
index 000000000..3825dfdd0
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_in_str_array.yaml
@@ -0,0 +1,116 @@
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "1"
+  sid: "17296083384258897030"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_1
+  timestamp: "2026-04-15T01:02:57.500Z"
+  version: "1776215004052000314"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "2"
+  sid: "13293155473432710288"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_2
+  timestamp: "2026-04-15T01:02:58Z"
+  version: "1776215004052042343"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "3"
+  sid: "8985479151150409059"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_3
+  timestamp: "2026-04-15T01:02:58.500Z"
+  version: "1776215004052067554"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "5"
+  sid: "11425316604636028751"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_4
+  timestamp: "2026-04-15T01:02:59Z"
+  version: "1776215004052089874"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "50"
+  - name: value
+    value:
+      int:
+        value: "4"
+  sid: "13633132181018445499"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_5
+  timestamp: "2026-04-15T01:02:59.500Z"
+  version: "1776215004052119554"
diff --git a/test/cases/measure/data/want/gen_leaf_le_int.yaml b/test/cases/measure/data/want/gen_leaf_le_int.yaml
new file mode 100644
index 000000000..305e42af9
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_le_int.yaml
@@ -0,0 +1,60 @@
+dataPoints:
+- sid: "15142466043926325685"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "1"
+    - key: service_id
+      value:
+        str:
+          value: service_1
+    - key: name
+      value:
+        str:
+          value: service_name_1
+    - key: short_name
+      value:
+        str:
+          value: service_short_name_1
+    - key: service_group
+      value:
+        str:
+          value: group1
+    - key: layer
+      value:
+        int:
+          value: "1"
+  timestamp: "2026-04-15T01:02:59Z"
+  version: "1776215003965297926"
+- sid: "12370392692163567533"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "3"
+    - key: service_id
+      value:
+        str:
+          value: service_3
+    - key: name
+      value:
+        str:
+          value: service_name_3
+    - key: short_name
+      value:
+        str:
+          value: service_short_name_3
+    - key: service_group
+      value:
+        str:
+          value: group1
+    - key: layer
+      value:
+        int: {}
+  timestamp: "2026-04-15T01:03:00Z"
+  version: "1776215003965449446"
diff --git a/test/cases/measure/data/want/gen_leaf_le_str.yaml b/test/cases/measure/data/want/gen_leaf_le_str.yaml
new file mode 100644
index 000000000..3825dfdd0
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_le_str.yaml
@@ -0,0 +1,116 @@
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "1"
+  sid: "17296083384258897030"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_1
+  timestamp: "2026-04-15T01:02:57.500Z"
+  version: "1776215004052000314"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "2"
+  sid: "13293155473432710288"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_2
+  timestamp: "2026-04-15T01:02:58Z"
+  version: "1776215004052042343"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "3"
+  sid: "8985479151150409059"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_3
+  timestamp: "2026-04-15T01:02:58.500Z"
+  version: "1776215004052067554"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "5"
+  sid: "11425316604636028751"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_4
+  timestamp: "2026-04-15T01:02:59Z"
+  version: "1776215004052089874"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "50"
+  - name: value
+    value:
+      int:
+        value: "4"
+  sid: "13633132181018445499"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_5
+  timestamp: "2026-04-15T01:02:59.500Z"
+  version: "1776215004052119554"
diff --git a/test/cases/measure/data/want/gen_leaf_lt_int.yaml b/test/cases/measure/data/want/gen_leaf_lt_int.yaml
new file mode 100644
index 000000000..305e42af9
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_lt_int.yaml
@@ -0,0 +1,60 @@
+dataPoints:
+- sid: "15142466043926325685"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "1"
+    - key: service_id
+      value:
+        str:
+          value: service_1
+    - key: name
+      value:
+        str:
+          value: service_name_1
+    - key: short_name
+      value:
+        str:
+          value: service_short_name_1
+    - key: service_group
+      value:
+        str:
+          value: group1
+    - key: layer
+      value:
+        int:
+          value: "1"
+  timestamp: "2026-04-15T01:02:59Z"
+  version: "1776215003965297926"
+- sid: "12370392692163567533"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "3"
+    - key: service_id
+      value:
+        str:
+          value: service_3
+    - key: name
+      value:
+        str:
+          value: service_name_3
+    - key: short_name
+      value:
+        str:
+          value: service_short_name_3
+    - key: service_group
+      value:
+        str:
+          value: group1
+    - key: layer
+      value:
+        int: {}
+  timestamp: "2026-04-15T01:03:00Z"
+  version: "1776215003965449446"
diff --git a/test/cases/measure/data/want/gen_leaf_lt_str.yaml b/test/cases/measure/data/want/gen_leaf_lt_str.yaml
new file mode 100644
index 000000000..86b692a1a
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_lt_str.yaml
@@ -0,0 +1,70 @@
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "1"
+  sid: "17296083384258897030"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_1
+  timestamp: "2026-04-15T01:02:57.500Z"
+  version: "1776215004052000314"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "2"
+  sid: "13293155473432710288"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_2
+  timestamp: "2026-04-15T01:02:58Z"
+  version: "1776215004052042343"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "3"
+  sid: "8985479151150409059"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_3
+  timestamp: "2026-04-15T01:02:58.500Z"
+  version: "1776215004052067554"
diff --git a/test/cases/measure/data/want/gen_leaf_match_str.yaml b/test/cases/measure/data/want/gen_leaf_match_str.yaml
new file mode 100644
index 000000000..f6df497dd
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_match_str.yaml
@@ -0,0 +1,51 @@
+dataPoints:
+- sid: "17262786445563424612"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "1"
+    - key: service_id
+      value:
+        str:
+          value: svc_1
+    - key: name
+      value:
+        str:
+          value: nodea@west-us
+    - key: last_ping
+      value:
+        int:
+          value: "100"
+    - key: layer
+      value:
+        int: {}
+  timestamp: "2026-04-15T01:02:59Z"
+  version: "1776215004007632395"
+- sid: "18180773176666025369"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "3"
+    - key: service_id
+      value:
+        str:
+          value: svc_1
+    - key: name
+      value:
+        str:
+          value: nodea@east-cn
+    - key: last_ping
+      value:
+        int:
+          value: "100"
+    - key: layer
+      value:
+        int: {}
+  timestamp: "2026-04-15T01:03:00Z"
+  version: "1776215004007743095"
diff --git a/test/cases/measure/data/want/gen_leaf_ne_int.yaml b/test/cases/measure/data/want/gen_leaf_ne_int.yaml
new file mode 100644
index 000000000..60110eed3
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_ne_int.yaml
@@ -0,0 +1,60 @@
+dataPoints:
+- sid: "3906119849472468294"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "2"
+    - key: service_id
+      value:
+        str:
+          value: service_2
+    - key: name
+      value:
+        str:
+          value: service_name_2
+    - key: short_name
+      value:
+        str:
+          value: service_short_name_2
+    - key: service_group
+      value:
+        str:
+          value: group1
+    - key: layer
+      value:
+        int:
+          value: "2"
+  timestamp: "2026-04-15T01:02:59.500Z"
+  version: "1776215003965417936"
+- sid: "12370392692163567533"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "3"
+    - key: service_id
+      value:
+        str:
+          value: service_3
+    - key: name
+      value:
+        str:
+          value: service_name_3
+    - key: short_name
+      value:
+        str:
+          value: service_short_name_3
+    - key: service_group
+      value:
+        str:
+          value: group1
+    - key: layer
+      value:
+        int: {}
+  timestamp: "2026-04-15T01:03:00Z"
+  version: "1776215003965449446"
diff --git a/test/cases/measure/data/want/gen_leaf_ne_str.yaml b/test/cases/measure/data/want/gen_leaf_ne_str.yaml
new file mode 100644
index 000000000..fd793e1fe
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_ne_str.yaml
@@ -0,0 +1,70 @@
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "5"
+  sid: "11425316604636028751"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_4
+  timestamp: "2026-04-15T01:02:59Z"
+  version: "1776215004052089874"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "50"
+  - name: value
+    value:
+      int:
+        value: "4"
+  sid: "13633132181018445499"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_5
+  timestamp: "2026-04-15T01:02:59.500Z"
+  version: "1776215004052119554"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "300"
+  - name: value
+    value:
+      int:
+        value: "6"
+  sid: "12160318715725963202"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc3
+    - key: entity_id
+      value:
+        str:
+          value: entity_6
+  timestamp: "2026-04-15T01:03:00Z"
+  version: "1776215004052179134"
diff --git a/test/cases/measure/data/want/gen_leaf_not_in_int_array.yaml b/test/cases/measure/data/want/gen_leaf_not_in_int_array.yaml
new file mode 100644
index 000000000..16346cb66
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_not_in_int_array.yaml
@@ -0,0 +1,61 @@
+dataPoints:
+- sid: "15142466043926325685"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "1"
+    - key: service_id
+      value:
+        str:
+          value: service_1
+    - key: name
+      value:
+        str:
+          value: service_name_1
+    - key: short_name
+      value:
+        str:
+          value: service_short_name_1
+    - key: service_group
+      value:
+        str:
+          value: group1
+    - key: layer
+      value:
+        int:
+          value: "1"
+  timestamp: "2026-04-15T01:02:59Z"
+  version: "1776215003965297926"
+- sid: "3906119849472468294"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "2"
+    - key: service_id
+      value:
+        str:
+          value: service_2
+    - key: name
+      value:
+        str:
+          value: service_name_2
+    - key: short_name
+      value:
+        str:
+          value: service_short_name_2
+    - key: service_group
+      value:
+        str:
+          value: group1
+    - key: layer
+      value:
+        int:
+          value: "2"
+  timestamp: "2026-04-15T01:02:59.500Z"
+  version: "1776215003965417936"
diff --git a/test/cases/measure/data/want/gen_leaf_not_in_str_array.yaml b/test/cases/measure/data/want/gen_leaf_not_in_str_array.yaml
new file mode 100644
index 000000000..3825dfdd0
--- /dev/null
+++ b/test/cases/measure/data/want/gen_leaf_not_in_str_array.yaml
@@ -0,0 +1,116 @@
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "1"
+  sid: "17296083384258897030"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_1
+  timestamp: "2026-04-15T01:02:57.500Z"
+  version: "1776215004052000314"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "2"
+  sid: "13293155473432710288"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_2
+  timestamp: "2026-04-15T01:02:58Z"
+  version: "1776215004052042343"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "3"
+  sid: "8985479151150409059"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_3
+  timestamp: "2026-04-15T01:02:58.500Z"
+  version: "1776215004052067554"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "5"
+  sid: "11425316604636028751"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_4
+  timestamp: "2026-04-15T01:02:59Z"
+  version: "1776215004052089874"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "50"
+  - name: value
+    value:
+      int:
+        value: "4"
+  sid: "13633132181018445499"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_5
+  timestamp: "2026-04-15T01:02:59.500Z"
+  version: "1776215004052119554"
diff --git a/test/cases/measure/data/want/gen_tree_depth1_leaf.yaml b/test/cases/measure/data/want/gen_tree_depth1_leaf.yaml
new file mode 100644
index 000000000..86b692a1a
--- /dev/null
+++ b/test/cases/measure/data/want/gen_tree_depth1_leaf.yaml
@@ -0,0 +1,70 @@
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "1"
+  sid: "17296083384258897030"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_1
+  timestamp: "2026-04-15T01:02:57.500Z"
+  version: "1776215004052000314"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "2"
+  sid: "13293155473432710288"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_2
+  timestamp: "2026-04-15T01:02:58Z"
+  version: "1776215004052042343"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "3"
+  sid: "8985479151150409059"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_3
+  timestamp: "2026-04-15T01:02:58.500Z"
+  version: "1776215004052067554"
diff --git a/test/cases/measure/data/want/gen_tree_depth2_and.yaml b/test/cases/measure/data/want/gen_tree_depth2_and.yaml
new file mode 100644
index 000000000..b90cd7225
--- /dev/null
+++ b/test/cases/measure/data/want/gen_tree_depth2_and.yaml
@@ -0,0 +1,24 @@
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "2"
+  sid: "13293155473432710288"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_2
+  timestamp: "2026-04-15T01:02:58Z"
+  version: "1776215004052042343"
diff --git a/test/cases/measure/data/want/gen_tree_depth2_or.yaml b/test/cases/measure/data/want/gen_tree_depth2_or.yaml
new file mode 100644
index 000000000..86b692a1a
--- /dev/null
+++ b/test/cases/measure/data/want/gen_tree_depth2_or.yaml
@@ -0,0 +1,70 @@
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "1"
+  sid: "17296083384258897030"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_1
+  timestamp: "2026-04-15T01:02:57.500Z"
+  version: "1776215004052000314"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "2"
+  sid: "13293155473432710288"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_2
+  timestamp: "2026-04-15T01:02:58Z"
+  version: "1776215004052042343"
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "3"
+  sid: "8985479151150409059"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_3
+  timestamp: "2026-04-15T01:02:58.500Z"
+  version: "1776215004052067554"
diff --git a/test/cases/measure/data/want/gen_tree_depth3_and_or.yaml b/test/cases/measure/data/want/gen_tree_depth3_and_or.yaml
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/test/cases/measure/data/want/gen_tree_depth3_and_or.yaml
@@ -0,0 +1 @@
+{}
diff --git a/test/cases/measure/data/want/gen_tree_depth3_or_and.yaml b/test/cases/measure/data/want/gen_tree_depth3_or_and.yaml
new file mode 100644
index 000000000..b90cd7225
--- /dev/null
+++ b/test/cases/measure/data/want/gen_tree_depth3_or_and.yaml
@@ -0,0 +1,24 @@
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "2"
+  sid: "13293155473432710288"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_2
+  timestamp: "2026-04-15T01:02:58Z"
+  version: "1776215004052042343"
diff --git a/test/cases/measure/data/want/gen_tree_depth5_deep_and.yaml b/test/cases/measure/data/want/gen_tree_depth5_deep_and.yaml
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/test/cases/measure/data/want/gen_tree_depth5_deep_and.yaml
@@ -0,0 +1 @@
+{}
diff --git a/test/cases/measure/data/want/group_sum_with_filter.yaml b/test/cases/measure/data/want/group_sum_with_filter.yaml
new file mode 100644
index 000000000..ee7d95211
--- /dev/null
+++ b/test/cases/measure/data/want/group_sum_with_filter.yaml
@@ -0,0 +1,42 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+dataPoints:
+- fields:
+  - name: value
+    value:
+      int:
+        value: "6"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+- fields:
+  - name: value
+    value:
+      int:
+        value: "9"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
diff --git a/test/cases/measure/data/want/index_mode_le.yaml b/test/cases/measure/data/want/index_mode_le.yaml
new file mode 100644
index 000000000..c495d91cc
--- /dev/null
+++ b/test/cases/measure/data/want/index_mode_le.yaml
@@ -0,0 +1,48 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+dataPoints:
+- tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "3"
+    - key: service_id
+      value:
+        str:
+          value: service_3
+    - key: layer
+      value:
+        int:
+          value: "0"
+- tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "1"
+    - key: service_id
+      value:
+        str:
+          value: service_1
+    - key: layer
+      value:
+        int:
+          value: "1"
diff --git a/test/cases/measure/data/want/index_mode_ne.yaml b/test/cases/measure/data/want/index_mode_ne.yaml
new file mode 100644
index 000000000..30eaef6d8
--- /dev/null
+++ b/test/cases/measure/data/want/index_mode_ne.yaml
@@ -0,0 +1,56 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+dataPoints:
+- tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "2"
+    - key: service_id
+      value:
+        str:
+          value: service_2
+    - key: name
+      value:
+        str:
+          value: service_name_2
+    - key: layer
+      value:
+        int:
+          value: "2"
+- tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: "3"
+    - key: service_id
+      value:
+        str:
+          value: service_3
+    - key: name
+      value:
+        str:
+          value: service_name_3
+    - key: layer
+      value:
+        int:
+          value: "0"
diff --git a/test/cases/measure/data/want/tag_filter_ge.yaml b/test/cases/measure/data/want/tag_filter_ge.yaml
new file mode 100644
index 000000000..165d9cc97
--- /dev/null
+++ b/test/cases/measure/data/want/tag_filter_ge.yaml
@@ -0,0 +1,78 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "5"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_4
+- fields:
+  - name: total
+    value:
+      int:
+        value: "300"
+  - name: value
+    value:
+      int:
+        value: "6"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc3
+    - key: entity_id
+      value:
+        str:
+          value: entity_6
+- fields:
+  - name: total
+    value:
+      int:
+        value: "50"
+  - name: value
+    value:
+      int:
+        value: "4"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_5
diff --git a/test/cases/measure/data/want/tag_filter_ne.yaml b/test/cases/measure/data/want/tag_filter_ne.yaml
new file mode 100644
index 000000000..165d9cc97
--- /dev/null
+++ b/test/cases/measure/data/want/tag_filter_ne.yaml
@@ -0,0 +1,78 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "5"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_4
+- fields:
+  - name: total
+    value:
+      int:
+        value: "300"
+  - name: value
+    value:
+      int:
+        value: "6"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc3
+    - key: entity_id
+      value:
+        str:
+          value: entity_6
+- fields:
+  - name: total
+    value:
+      int:
+        value: "50"
+  - name: value
+    value:
+      int:
+        value: "4"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+    - key: entity_id
+      value:
+        str:
+          value: entity_5
diff --git a/test/cases/measure/data/want/tag_filter_not_in.yaml b/test/cases/measure/data/want/tag_filter_not_in.yaml
new file mode 100644
index 000000000..d78cfd59d
--- /dev/null
+++ b/test/cases/measure/data/want/tag_filter_not_in.yaml
@@ -0,0 +1,98 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+dataPoints:
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "3"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_3
+- fields:
+  - name: total
+    value:
+      int:
+        value: "300"
+  - name: value
+    value:
+      int:
+        value: "6"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc3
+    - key: entity_id
+      value:
+        str:
+          value: entity_6
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "2"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_2
+- fields:
+  - name: total
+    value:
+      int:
+        value: "100"
+  - name: value
+    value:
+      int:
+        value: "1"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
+    - key: entity_id
+      value:
+        str:
+          value: entity_1
diff --git a/test/cases/measure/data/want/top_with_filter.yaml b/test/cases/measure/data/want/top_with_filter.yaml
new file mode 100644
index 000000000..c288dd822
--- /dev/null
+++ b/test/cases/measure/data/want/top_with_filter.yaml
@@ -0,0 +1,42 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+dataPoints:
+- fields:
+  - name: value
+    value:
+      int:
+        value: "4"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc2
+- fields:
+  - name: value
+    value:
+      int:
+        value: "2"
+  tagFamilies:
+  - name: default
+    tags:
+    - key: id
+      value:
+        str:
+          value: svc1
diff --git a/test/cases/measure/measure.go b/test/cases/measure/measure.go
index 743df02b5..43ad0617c 100644
--- a/test/cases/measure/measure.go
+++ b/test/cases/measure/measure.go
@@ -93,6 +93,55 @@ var measureEntries = []any{
 	g.Entry("filter by non-existent tag", helpers.Args{Input: "filter_non_existent_tag", Duration: 25 * time.Minute, Offset: -20 * time.Minute, WantErr: true}),
 	g.Entry("project non-existent tag", helpers.Args{Input: "project_non_existent_tag", Duration: 25 * time.Minute, Offset: -20 * time.Minute, WantErr: true}),
 	g.Entry("write mixed", helpers.Args{Input: "write_mixed", Duration: 15 * time.Minute, Offset: 25 * time.Minute, DisOrder: true}),
+	g.Entry("filter by tag with NE", helpers.Args{Input: "tag_filter_ne", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("filter by tag with NOT IN", helpers.Args{Input: "tag_filter_not_in", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("filter by tag with GE", helpers.Args{Input: "tag_filter_ge", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("complex AND/OR", helpers.Args{Input: "complex_and_or", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("group sum with filter", helpers.Args{Input: "group_sum_with_filter", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("top N with filter", helpers.Args{Input: "top_with_filter", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("index mode filter by NE", helpers.Args{Input: "index_mode_ne", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("index mode filter by LE on int", helpers.Args{Input: "index_mode_le", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("offset beyond results", helpers.Args{Input: "offset_empty", Duration: 25 * time.Minute, Offset: -20 * time.Minute, WantEmpty: true}),
+
+	// Generated test cases (Layer 1: leaf conditions)
+	g.Entry("gen: leaf EQ string", helpers.Args{Input: "gen_leaf_eq_str", Want: "gen_leaf_eq_str", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: leaf NE string", helpers.Args{Input: "gen_leaf_ne_str", Want: "gen_leaf_ne_str", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: leaf LT string", helpers.Args{Input: "gen_leaf_lt_str", Want: "gen_leaf_lt_str", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: leaf GT string", helpers.Args{Input: "gen_leaf_gt_str", Want: "gen_leaf_gt_str", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: leaf LE string", helpers.Args{Input: "gen_leaf_le_str", Want: "gen_leaf_le_str", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: leaf GE string", helpers.Args{Input: "gen_leaf_ge_str", Want: "gen_leaf_ge_str", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: leaf IN str array", helpers.Args{Input: "gen_leaf_in_str_array", Want: "gen_leaf_in_str_array", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: leaf NOT IN str array", helpers.Args{Input: "gen_leaf_not_in_str_array", Want: "gen_leaf_not_in_str_array", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: leaf EQ int", helpers.Args{Input: "gen_leaf_eq_int", Want: "gen_leaf_eq_int", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("gen: leaf NE int", helpers.Args{Input: "gen_leaf_ne_int", Want: "gen_leaf_ne_int", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("gen: leaf LT int", helpers.Args{Input: "gen_leaf_lt_int", Want: "gen_leaf_lt_int", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("gen: leaf GT int", helpers.Args{Input: "gen_leaf_gt_int", Want: "gen_leaf_gt_int", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("gen: leaf LE int", helpers.Args{Input: "gen_leaf_le_int", Want: "gen_leaf_le_int", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("gen: leaf GE int", helpers.Args{Input: "gen_leaf_ge_int", Want: "gen_leaf_ge_int", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("gen: leaf IN int array", helpers.Args{Input: "gen_leaf_in_int_array", Want: "gen_leaf_in_int_array", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("gen: leaf NOT IN int array", helpers.Args{Input: "gen_leaf_not_in_int_array", Want: "gen_leaf_not_in_int_array", Duration: 25 * time.Minute, Offset: -20 * time.Minute, DisOrder: true}),
+	g.Entry("gen: leaf MATCH string", helpers.Args{Input: "gen_leaf_match_str", Want: "gen_leaf_match_str", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: err IN with scalar value", helpers.Args{Input: "gen_err_in_scalar", Duration: 25 * time.Minute, Offset: -20 * time.Minute, WantErr: true}),
+	g.Entry("gen: err MATCH on int field", helpers.Args{Input: "gen_err_match_int", Duration: 25 * time.Minute, Offset: -20 * time.Minute, WantErr: true}),
+	// Generated test cases (Layer 2: tree conditions)
+	g.Entry("gen: tree depth 1 leaf", helpers.Args{Input: "gen_tree_depth1_leaf", Want: "gen_tree_depth1_leaf", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: tree depth 2 AND", helpers.Args{Input: "gen_tree_depth2_and", Want: "gen_tree_depth2_and", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: tree depth 2 OR", helpers.Args{Input: "gen_tree_depth2_or", Want: "gen_tree_depth2_or", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: tree depth 3 AND/OR", helpers.Args{Input: "gen_tree_depth3_and_or", Want: "gen_tree_depth3_and_or", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: tree depth 3 OR/AND", helpers.Args{Input: "gen_tree_depth3_or_and", Want: "gen_tree_depth3_or_and", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: tree depth 5 deep AND", helpers.Args{Input: "gen_tree_depth5_deep_and", Want: "gen_tree_depth5_deep_and", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: tree depth 5 deep OR", helpers.Args{Input: "gen_tree_depth5_deep_or", WantErr: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	// Generated test cases (Layer 3: feature combinations)
+	g.Entry("gen: mean top desc group order asc", helpers.Args{Input: "gen_feat_mean_top_desc_group_order_asc_0", Want: "gen_feat_mean_top_desc_group_order_asc_0", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: mean top asc group order desc filter", helpers.Args{Input: "gen_feat_mean_top_asc_group_order_desc_filter_1", Want: "gen_feat_mean_top_asc_group_order_desc_filter_1", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: mean group", helpers.Args{Input: "gen_feat_mean_group_2", Want: "gen_feat_mean_group_2", DisOrder: true, Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: mean top desc group order desc", helpers.Args{Input: "gen_feat_mean_top_desc_group_order_desc_3", Want: "gen_feat_mean_top_desc_group_order_desc_3", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: mean top asc group order asc", helpers.Args{Input: "gen_feat_mean_top_asc_group_order_asc_4", Want: "gen_feat_mean_top_asc_group_order_asc_4", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: mean group order asc", helpers.Args{Input: "gen_feat_mean_group_order_asc_5", Want: "gen_feat_mean_group_order_asc_5", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: max group order desc", helpers.Args{Input: "gen_feat_max_group_order_desc_6", Want: "gen_feat_max_group_order_desc_6", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: min group order desc", helpers.Args{Input: "gen_feat_min_group_order_desc_7", Want: "gen_feat_min_group_order_desc_7", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: count group order desc", helpers.Args{Input: "gen_feat_count_group_order_desc_8", Want: "gen_feat_count_group_order_desc_8", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
+	g.Entry("gen: sum group order desc", helpers.Args{Input: "gen_feat_sum_group_order_desc_9", Want: "gen_feat_sum_group_order_desc_9", Duration: 25 * time.Minute, Offset: -20 * time.Minute}),
 }
 
 // RegisterTable registers the measure test table with the given description.


(skywalking-banyandb) 02/04: chore(query): clarify OR nil-branch comment and add test-case generation CI check

Posted by ha...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hanahmily pushed a commit to branch test/query
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git

commit 61645759fd119390be4293921ff80cf2aec02524
Author: Hongtao Gao <ha...@gmail.com>
AuthorDate: Wed Apr 15 04:52:01 2026 +0000

    chore(query): clarify OR nil-branch comment and add test-case generation CI check
    
    Improve the comment accuracy in BuildQuery's OR nil-branch handling to
    precisely describe entity-level series lookup scoping. Add CI drift
    detection for generated measure test cases and a Makefile target for
    local regeneration.
    
    via [HAPI](https://hapi.run)
    
    Co-Authored-By: HAPI <no...@hapi.run>
---
 .github/workflows/ci.yml    | 7 +++++++
 Makefile                    | 6 +++++-
 pkg/index/inverted/query.go | 6 +++---
 3 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6c63110c4..5024c7113 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -62,6 +62,13 @@ jobs:
             git status -s; \
             exit 1; \
           fi
+      - name: Generate test cases and check drift
+        run: |
+          make generate-test-cases
+          git diff --exit-code -- test/cases/measure/data/input/ \
+          || (echo "::error::Generated test cases have drifted. Run 'make generate-test-cases' locally and commit the changes." \
+          && git diff --stat -- test/cases/measure/data/input/ \
+          && exit 1)
       - name: Check License Header
         run: make license-check
       - name: Check requirements
diff --git a/Makefile b/Makefile
index 210087df9..922b24f95 100644
--- a/Makefile
+++ b/Makefile
@@ -47,6 +47,9 @@ generate: TARGET=generate
 generate: PROJECTS:=api $(PROJECTS) pkg
 generate: default  ## Generate API codes
 
+generate-test-cases:  ## Regenerate measure query test cases (input/*.ql, input/*.yaml)
+	go run ./test/cases/measure/cmd/generate generate test/cases/measure/data
+
 build: TARGET=all
 build: default  ## Build all projects
 
@@ -141,6 +144,7 @@ check: ## Check that the status is consistent with CI
 pre-push: ## Check source files before pushing to the remote repo
 	$(MAKE) check-req
 	$(MAKE) generate
+	$(MAKE) generate-test-cases
 	$(MAKE) lint
 	$(MAKE) check
 	$(MAKE) vuln-check
@@ -222,7 +226,7 @@ release-push-candidate: ## Push release candidate
 	${PUSH_RELEASE_SCRIPTS}
 	
 .PHONY: all $(PROJECTS) clean build  default nuke
-.PHONY: lint check tidy format pre-push
+.PHONY: lint check tidy format pre-push generate-test-cases
 .PHONY: test test-race test-coverage test-ci test-docker
 .PHONY: license-check license-fix license-dep
 .PHONY: release release-binary release-source release-sign release-assembly
diff --git a/pkg/index/inverted/query.go b/pkg/index/inverted/query.go
index add0f2f4c..1221e4643 100644
--- a/pkg/index/inverted/query.go
+++ b/pkg/index/inverted/query.go
@@ -147,9 +147,9 @@ func BuildQuery(criteria *modelv1.Criteria, schema logical.Schema, entityDict ma
 					node:  newMatchAllNode(),
 				}, entities, true, nil
 			}
-			// When one OR branch has no query (entity-only), check if all entities are specific.
-			// If no AnyTagValue is present, the nil branch's entity needs match-all to find its documents.
-			// When AnyTagValue exists, the other branch's query already covers all entities.
+			// When one OR branch is entity-only (nil query), entity-level series lookup already
+			// constrains the scope. If all entities are specific (no AnyTagValue wildcards),
+			// return match-all — no index query is needed.
 			if left == nil || right == nil {
 				allSpecific := true
 				for _, entity := range entities {