mirror of
https://github.com/SigNoz/signoz.git
synced 2026-08-17 18:30:31 +01:00
A referenced name now resolves to logical fields (LogicalField, #12499). A slice shows ambiguity: each logical field gets its own condition. A group shows a semantic-convention family: one field with more than one spelling. Compilers merge a family with current-wins precedence, and the response keeps the spelling that the request used. - FieldMapper gets one new method: ExistsFor, the per-key existence primitive. Each mapper already had this knowledge; two rename a private method and five expose logic from their condition builders. - LogicalValueExpr and LogicalExistsExpr build all family SQL in one place, only from FieldFor and ExistsFor. No signal implements family logic. - Statement builders prefetch sibling spellings with ExpandKeySelectorsForFamilies. The metadata store stays family-blind, and autocomplete stays literal. - The traces and resource-filter condition builders compile per logical field. The traces group-by path upgrades legacy candidates to their family in a post-pass; the legacy candidate flow does not change. - Logs, metrics, audit, metadata, and rule-state-history flatten to single keys; their SQL does not change. The resolve_semconv_families feature flag (default: disabled) gates all family behavior. The components that resolve names hold the flagger and evaluate the flag at the point of use, with the org from the call, the same way the logs mapper uses use_json_body. A nil flagger means off, so resolution stays literal on any path without one. With the flag off, the generated SQL is the same as main, and tests pin this. The legacy compatibility-key map stays in place for the same reason. Assisted-by: Claude Fable 5
145 lines
5.1 KiB
Go
145 lines
5.1 KiB
Go
package tracesstatementbuilder
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
|
|
"github.com/SigNoz/signoz/pkg/errors"
|
|
"github.com/SigNoz/signoz/pkg/factory"
|
|
"github.com/SigNoz/signoz/pkg/flagger"
|
|
"github.com/SigNoz/signoz/pkg/querybuilder"
|
|
"github.com/SigNoz/signoz/pkg/statementbuilder"
|
|
"github.com/SigNoz/signoz/pkg/statementbuilder/resourcefilter"
|
|
"github.com/SigNoz/signoz/pkg/telemetryschema/tracestelemetryschema"
|
|
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
|
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
|
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
|
"github.com/SigNoz/signoz/pkg/valuer"
|
|
)
|
|
|
|
type traceOperatorStatementBuilder struct {
|
|
logger *slog.Logger
|
|
metadataStore telemetrytypes.MetadataStore
|
|
fm qbtypes.FieldMapper
|
|
cb qbtypes.ConditionBuilder
|
|
traceStmtBuilder qbtypes.StatementBuilder[qbtypes.TraceAggregation]
|
|
resourceFilterStmtBuilder qbtypes.StatementBuilder[qbtypes.TraceAggregation]
|
|
aggExprRewriter qbtypes.AggExprRewriter
|
|
fl flagger.Flagger
|
|
}
|
|
|
|
var _ qbtypes.TraceOperatorStatementBuilder = (*traceOperatorStatementBuilder)(nil)
|
|
|
|
// NewOperatorFactory returns a provider factory for the trace-operator statement
|
|
// builder. The operator delegates sub-query construction to a trace query statement
|
|
// builder, so it builds its own internally — mirroring how the meter factory builds
|
|
// its own metrics builder.
|
|
func NewOperatorFactory(
|
|
telemetryStore telemetrystore.TelemetryStore,
|
|
metadataStore telemetrytypes.MetadataStore,
|
|
fl flagger.Flagger,
|
|
) factory.ProviderFactory[qbtypes.TraceOperatorStatementBuilder, statementbuilder.Config] {
|
|
return factory.NewProviderFactory(
|
|
factory.MustNewName("traceoperator"),
|
|
func(_ context.Context, settings factory.ProviderSettings, cfg statementbuilder.Config) (qbtypes.TraceOperatorStatementBuilder, error) {
|
|
fm := tracestelemetryschema.NewFieldMapper(fl)
|
|
cb := tracestelemetryschema.NewConditionBuilder(fm, fl)
|
|
aggExprRewriter := querybuilder.NewAggExprRewriter(settings, nil, fm, cb, fl)
|
|
traceStmtBuilder := NewTraceQueryStatementBuilder(
|
|
settings, metadataStore, fm, cb, aggExprRewriter, telemetryStore, fl,
|
|
cfg.SkipResourceFingerprint.Enabled, cfg.SkipResourceFingerprint.Threshold,
|
|
)
|
|
return NewTraceOperatorStatementBuilder(
|
|
settings, metadataStore, fm, cb, traceStmtBuilder, aggExprRewriter, fl,
|
|
), nil
|
|
},
|
|
)
|
|
}
|
|
|
|
func NewTraceOperatorStatementBuilder(
|
|
settings factory.ProviderSettings,
|
|
metadataStore telemetrytypes.MetadataStore,
|
|
fieldMapper qbtypes.FieldMapper,
|
|
conditionBuilder qbtypes.ConditionBuilder,
|
|
traceStmtBuilder qbtypes.StatementBuilder[qbtypes.TraceAggregation],
|
|
aggExprRewriter qbtypes.AggExprRewriter,
|
|
flagger flagger.Flagger,
|
|
) *traceOperatorStatementBuilder {
|
|
tracesSettings := factory.NewScopedProviderSettings(settings, "github.com/SigNoz/signoz/pkg/telemetryschema/tracestelemetryschema")
|
|
|
|
resourceFilterStmtBuilder := resourcefilter.New[qbtypes.TraceAggregation](
|
|
settings,
|
|
tracestelemetryschema.DBName,
|
|
tracestelemetryschema.TracesResourceV3TableName,
|
|
telemetrytypes.SignalTraces,
|
|
telemetrytypes.SourceUnspecified,
|
|
metadataStore,
|
|
nil,
|
|
flagger,
|
|
)
|
|
|
|
return &traceOperatorStatementBuilder{
|
|
logger: tracesSettings.Logger(),
|
|
metadataStore: metadataStore,
|
|
fm: fieldMapper,
|
|
cb: conditionBuilder,
|
|
traceStmtBuilder: traceStmtBuilder,
|
|
resourceFilterStmtBuilder: resourceFilterStmtBuilder,
|
|
aggExprRewriter: aggExprRewriter,
|
|
fl: flagger,
|
|
}
|
|
}
|
|
|
|
// Build builds a SQL query based on the given parameters.
|
|
func (b *traceOperatorStatementBuilder) Build(
|
|
ctx context.Context,
|
|
orgID valuer.UUID,
|
|
start uint64,
|
|
end uint64,
|
|
requestType qbtypes.RequestType,
|
|
query qbtypes.QueryBuilderTraceOperator,
|
|
compositeQuery *qbtypes.CompositeQuery,
|
|
) (*qbtypes.Statement, error) {
|
|
|
|
start = querybuilder.ToNanoSecs(start)
|
|
end = querybuilder.ToNanoSecs(end)
|
|
|
|
// Parse the expression if not already parsed
|
|
if query.ParsedExpression == nil {
|
|
if err := query.ParseExpression(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Validate compositeQuery parameter
|
|
if compositeQuery == nil {
|
|
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "compositeQuery cannot be nil")
|
|
}
|
|
|
|
b.logger.DebugContext(ctx, "Building trace operator query",
|
|
slog.String("expression", query.Expression),
|
|
slog.Any("request_type", requestType))
|
|
|
|
// Build the CTE-based query
|
|
builder := &traceOperatorCTEBuilder{
|
|
start: start,
|
|
end: end,
|
|
orgID: orgID,
|
|
operator: &query,
|
|
stmtBuilder: b,
|
|
queries: make(map[string]*qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]),
|
|
ctes: []cteNode{}, // Use slice to maintain order
|
|
cteNameToIndex: make(map[string]int),
|
|
queryToCTEName: make(map[string]string),
|
|
compositeQuery: compositeQuery, // Now passed as explicit parameter
|
|
}
|
|
|
|
// Collect all referenced queries
|
|
if err := builder.collectQueries(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Build the query
|
|
return builder.build(ctx, requestType)
|
|
}
|