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 2025/08/29 15:33:46 UTC
(skywalking-banyandb) 07/12: Enhance trace filtering logic to return collected tag names alongside filters. Update buildFilter and buildTraceFilter functions to support tag name collection for improved error handling and projection tag management. Implement deduplication of tag names to ensure uniqueness in projections.
This is an automated email from the ASF dual-hosted git repository.
hanahmily pushed a commit to branch trace/query
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git
commit 6e82b414db62172581f96d18019ae970ff035621
Author: Gao Hongtao <ha...@gmail.com>
AuthorDate: Fri Aug 29 15:41:42 2025 +0800
Enhance trace filtering logic to return collected tag names alongside filters. Update buildFilter and buildTraceFilter functions to support tag name collection for improved error handling and projection tag management. Implement deduplication of tag names to ensure uniqueness in projections.
---
pkg/query/logical/trace/index_filter.go | 46 +++++++++++--------
pkg/query/logical/trace/trace_plan_tag_filter.go | 56 ++++++++++++++++++------
2 files changed, 70 insertions(+), 32 deletions(-)
diff --git a/pkg/query/logical/trace/index_filter.go b/pkg/query/logical/trace/index_filter.go
index 77d1e55c..227e162b 100644
--- a/pkg/query/logical/trace/index_filter.go
+++ b/pkg/query/logical/trace/index_filter.go
@@ -33,34 +33,39 @@ import (
// Trace creates explicit index rules for skipping index on all tags that don't belong to entity.
func buildFilter(criteria *modelv1.Criteria, tagNames map[string]bool,
entityDict map[string]int, entity []*modelv1.TagValue,
-) (index.Filter, [][]*modelv1.TagValue, error) {
+) (index.Filter, [][]*modelv1.TagValue, []string, error) {
if criteria == nil {
- return nil, [][]*modelv1.TagValue{entity}, nil
+ return nil, [][]*modelv1.TagValue{entity}, nil, nil
}
+
+ var collectedTagNames []string
switch criteria.GetExp().(type) {
case *modelv1.Criteria_Condition:
cond := criteria.GetCondition()
+ // Collect the tag name
+ collectedTagNames = append(collectedTagNames, cond.Name)
// Check if the tag name exists in the allowed tag names
if !tagNames[cond.Name] {
- return nil, nil, errors.Errorf("tag name '%s' not found in trace schema", cond.Name)
+ return nil, nil, collectedTagNames, errors.Errorf("tag name '%s' not found in trace schema", cond.Name)
}
_, parsedEntity, err := logical.ParseExprOrEntity(entityDict, entity, cond)
if err != nil {
- return nil, nil, err
+ return nil, nil, collectedTagNames, err
}
if parsedEntity != nil {
- return nil, parsedEntity, nil
+ return nil, parsedEntity, collectedTagNames, nil
}
// For trace, all non-entity tags have skipping index
expr, _, err := logical.ParseExprOrEntity(entityDict, entity, cond)
if err != nil {
- return nil, nil, err
+ return nil, nil, collectedTagNames, err
}
- return parseConditionToFilter(cond, entity, expr)
+ filter, entities, err := parseConditionToFilter(cond, entity, expr)
+ return filter, entities, collectedTagNames, err
case *modelv1.Criteria_Le:
le := criteria.GetLe()
if le.GetLeft() == nil && le.GetRight() == nil {
- return nil, nil, errors.WithMessagef(logical.ErrInvalidLogicalExpression, "both sides(left and right) of [%v] are empty", criteria)
+ return nil, nil, nil, errors.WithMessagef(logical.ErrInvalidLogicalExpression, "both sides(left and right) of [%v] are empty", criteria)
}
if le.GetLeft() == nil {
return buildFilter(le.Right, tagNames, entityDict, entity)
@@ -68,32 +73,37 @@ func buildFilter(criteria *modelv1.Criteria, tagNames map[string]bool,
if le.GetRight() == nil {
return buildFilter(le.Left, tagNames, entityDict, entity)
}
- left, leftEntities, err := buildFilter(le.Left, tagNames, entityDict, entity)
+ left, leftEntities, leftTagNames, err := buildFilter(le.Left, tagNames, entityDict, entity)
if err != nil {
- return nil, nil, err
+ return nil, nil, leftTagNames, err
}
- right, rightEntities, err := buildFilter(le.Right, tagNames, entityDict, entity)
+ right, rightEntities, rightTagNames, err := buildFilter(le.Right, tagNames, entityDict, entity)
if err != nil {
- return nil, nil, err
+ return nil, nil, append(leftTagNames, rightTagNames...), err
}
+
+ // Merge tag names from both sides
+ collectedTagNames = append(collectedTagNames, leftTagNames...)
+ collectedTagNames = append(collectedTagNames, rightTagNames...)
+
entities := logical.ParseEntities(le.Op, entity, leftEntities, rightEntities)
if entities == nil {
- return nil, nil, nil
+ return nil, nil, collectedTagNames, nil
}
if left == nil {
- return right, entities, nil
+ return right, entities, collectedTagNames, nil
}
if right == nil {
- return left, entities, nil
+ return left, entities, collectedTagNames, nil
}
switch le.Op {
case modelv1.LogicalExpression_LOGICAL_OP_AND:
- return &traceAndFilter{left: left, right: right}, entities, nil
+ return &traceAndFilter{left: left, right: right}, entities, collectedTagNames, nil
case modelv1.LogicalExpression_LOGICAL_OP_OR:
- return &traceOrFilter{left: left, right: right}, entities, nil
+ return &traceOrFilter{left: left, right: right}, entities, collectedTagNames, nil
}
}
- return nil, nil, logical.ErrInvalidCriteriaType
+ return nil, nil, nil, logical.ErrInvalidCriteriaType
}
func parseConditionToFilter(cond *modelv1.Condition, entity []*modelv1.TagValue, expr logical.LiteralExpr) (index.Filter, [][]*modelv1.TagValue, error) {
diff --git a/pkg/query/logical/trace/trace_plan_tag_filter.go b/pkg/query/logical/trace/trace_plan_tag_filter.go
index 9575c792..af1b78d2 100644
--- a/pkg/query/logical/trace/trace_plan_tag_filter.go
+++ b/pkg/query/logical/trace/trace_plan_tag_filter.go
@@ -51,23 +51,38 @@ func (uis *unresolvedTraceTagFilter) Analyze(s logical.Schema) (logical.Plan, er
entityDict[e] = idx
}
var err error
+ var conditionTagNames []string
// For trace, we only use skipping filter (no inverted filter or entities)
- ctx.skippingFilter, err = buildTraceFilter(uis.criteria, s, entityDict)
+ ctx.skippingFilter, conditionTagNames, err = buildTraceFilter(uis.criteria, s, entityDict)
if err != nil {
return nil, err
}
+ // Initialize projectionTags even if no explicit projection tags are provided
+ ctx.projectionTags = &model.TagProjection{
+ Family: "", // Empty family name for trace
+ Names: make([]string, 0),
+ }
+
+ // Add explicitly requested projection tags
if len(uis.projectionTags) > 0 {
- // For trace, we use a single TagProjection since traces don't have tag families
- ctx.projectionTags = &model.TagProjection{
- Family: "", // Empty family name for trace
- Names: make([]string, 0),
- }
for i := range uis.projectionTags {
for _, tag := range uis.projectionTags[i] {
ctx.projectionTags.Names = append(ctx.projectionTags.Names, tag.GetTagName())
}
}
+ }
+
+ // Add tag names from filter conditions to projection
+ if len(conditionTagNames) > 0 {
+ ctx.projectionTags.Names = append(ctx.projectionTags.Names, conditionTagNames...)
+ }
+
+ // Deduplicate tag names
+ ctx.projectionTags.Names = deduplicateStrings(ctx.projectionTags.Names)
+
+ // Create tag references if we have any projection tags
+ if len(ctx.projectionTags.Names) > 0 {
var errProject error
ctx.projTagsRefs, errProject = s.CreateTagRef(uis.projectionTags...)
if errProject != nil {
@@ -116,11 +131,24 @@ func newTraceAnalyzerContext(s logical.Schema) *traceAnalyzeContext {
}
}
-// buildTraceFilter builds a filter for trace queries.
+// deduplicateStrings removes duplicate strings from a slice while preserving order.
+func deduplicateStrings(strings []string) []string {
+ seen := make(map[string]bool)
+ var result []string
+ for _, str := range strings {
+ if !seen[str] {
+ seen[str] = true
+ result = append(result, str)
+ }
+ }
+ return result
+}
+
+// buildTraceFilter builds a filter for trace queries and returns both the filter and collected tag names.
// Unlike stream, trace only needs skipping filter.
-func buildTraceFilter(criteria *modelv1.Criteria, s logical.Schema, entityDict map[string]int) (index.Filter, error) {
+func buildTraceFilter(criteria *modelv1.Criteria, s logical.Schema, entityDict map[string]int) (index.Filter, []string, error) {
if criteria == nil {
- return nil, nil
+ return nil, nil, nil
}
// Create a map of valid tag names from the schema
tagNames := make(map[string]bool)
@@ -129,8 +157,8 @@ func buildTraceFilter(criteria *modelv1.Criteria, s logical.Schema, entityDict m
tagNames[tagName] = true
}
- filter, _, err := buildFilter(criteria, tagNames, entityDict, nil)
- return filter, err
+ filter, _, collectedTagNames, err := buildFilter(criteria, tagNames, entityDict, nil)
+ return filter, collectedTagNames, err
}
var (
@@ -184,7 +212,7 @@ func (t *traceTagFilterPlan) Execute(ctx context.Context) (model.TraceResult, er
Name: "",
Tags: make([]*modelv1.Tag, 0, len(result.Tags)),
}
-
+
// Build the row by taking the value at rowIdx from each tag (if it exists)
for _, tag := range result.Tags {
if rowIdx < len(tag.Values) {
@@ -194,13 +222,13 @@ func (t *traceTagFilterPlan) Execute(ctx context.Context) (model.TraceResult, er
})
}
}
-
+
tagFamilies := []*modelv1.TagFamily{family}
ok, err := t.tagFilter.Match(logical.TagFamilies(tagFamilies), t.s)
if err != nil {
return model.TraceResult{}, err
}
-
+
// If ANY row matches, return the entire result
if ok {
return result, nil