mirror of
https://github.com/SigNoz/signoz.git
synced 2026-08-23 21:20:30 +01:00
Some checks failed
build-staging / prepare (push) Has been cancelled
build-staging / js-build (push) Has been cancelled
build-staging / go-build (push) Has been cancelled
build-staging / staging (push) Has been cancelled
cacheci / tests (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
#### Description - A referenced name in a trace query now resolves to a `LogicalField` (#12499): one field, addressed by the requested spelling, backed by its physical member keys. A semantic-convention family (`deployment.environment.name` / `deployment.environment`) merges into one expression with current-wins precedence; the response keeps the requested spelling. - `FieldMapper` gets one new method, `ExistsFor` (the per-key presence primitive). `LogicalValueExpr` and `LogicalExistsExpr` build all family SQL in one place from `FieldFor` and `ExistsFor`; no signal implements family logic. - Statement builders prefetch sibling spellings; the metadata store stays family-blind and autocomplete stays literal. Traces and the resource filter compile per logical field; logs, metrics, and the other signals keep their SQL unchanged. - The `resolve_semconv_families` feature flag (default: disabled) gates all family behavior. With the flag off, the generated SQL is the same as main; tests pin this. Part of #6143. #### Additional Information - Stack: #12441 (merged) → **#12442** → #12443 → #12444 → #12445 → #12446 → #12447. This layer bases on main. - Rollback: turn the flag off; stored telemetry is untouched.
96 lines
4.2 KiB
Go
96 lines
4.2 KiB
Go
package querybuilder
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
schema "github.com/SigNoz/signoz-otel-collector/cmd/signozschemamigrator/schema_migrator"
|
|
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
|
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
|
"github.com/SigNoz/signoz/pkg/valuer"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// stubFieldMapper provides just the two per-key primitives the shared
|
|
// composition builds on; the remaining FieldMapper methods are unused here.
|
|
type stubFieldMapper struct{}
|
|
|
|
func (stubFieldMapper) FieldFor(_ context.Context, _ valuer.UUID, _, _ uint64, key *telemetrytypes.TelemetryFieldKey) (string, error) {
|
|
return "value(" + key.Name + ")", nil
|
|
}
|
|
|
|
func (stubFieldMapper) ExistsFor(_ context.Context, _ valuer.UUID, _, _ uint64, key *telemetrytypes.TelemetryFieldKey, exists bool) (string, error) {
|
|
if exists {
|
|
return "has(" + key.Name + ")", nil
|
|
}
|
|
return "NOT has(" + key.Name + ")", nil
|
|
}
|
|
|
|
func (stubFieldMapper) ColumnFor(context.Context, valuer.UUID, uint64, uint64, *telemetrytypes.TelemetryFieldKey) ([]*schema.Column, error) {
|
|
return nil, qbtypes.ErrColumnNotFound
|
|
}
|
|
|
|
func (stubFieldMapper) ColumnExpressionFor(context.Context, valuer.UUID, uint64, uint64, *telemetrytypes.TelemetryFieldKey, telemetrytypes.FieldDataType, map[string][]*telemetrytypes.TelemetryFieldKey) (string, error) {
|
|
return "", qbtypes.ErrColumnNotFound
|
|
}
|
|
|
|
func (stubFieldMapper) CandidateKeys(context.Context, valuer.UUID, *telemetrytypes.TelemetryFieldKey, any, map[string][]*telemetrytypes.TelemetryFieldKey) []*telemetrytypes.TelemetryFieldKey {
|
|
return nil
|
|
}
|
|
|
|
func stringFamily(names ...string) *telemetrytypes.LogicalField {
|
|
members := make([]*telemetrytypes.TelemetryFieldKey, 0, len(names))
|
|
for _, name := range names {
|
|
members = append(members, &telemetrytypes.TelemetryFieldKey{Name: name, FieldDataType: telemetrytypes.FieldDataTypeString})
|
|
}
|
|
return &telemetrytypes.LogicalField{Name: names[0], FieldDataType: telemetrytypes.FieldDataTypeString, Members: members}
|
|
}
|
|
|
|
func TestLogicalValueExprSingleMemberDelegatesToFieldFor(t *testing.T) {
|
|
logical := telemetrytypes.SingleLogicalField("a", &telemetrytypes.TelemetryFieldKey{Name: "a"})
|
|
expr, err := LogicalValueExpr(context.Background(), valuer.UUID{}, 0, 0, stubFieldMapper{}, logical)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "value(a)", expr)
|
|
}
|
|
|
|
func TestLogicalValueExprStringFamilyMergesCurrentFirst(t *testing.T) {
|
|
expr, err := LogicalValueExpr(context.Background(), valuer.UUID{}, 0, 0, stubFieldMapper{}, stringFamily("current", "old"))
|
|
require.NoError(t, err)
|
|
// The trailing '' preserves keyless-row semantics for negative operators.
|
|
assert.Equal(t, "COALESCE(NULLIF(value(current), ''), NULLIF(value(old), ''), '')", expr)
|
|
}
|
|
|
|
func TestLogicalValueExprNumericFamilyGuardsEveryMember(t *testing.T) {
|
|
logical := &telemetrytypes.LogicalField{
|
|
Name: "current",
|
|
FieldDataType: telemetrytypes.FieldDataTypeNumber,
|
|
Members: []*telemetrytypes.TelemetryFieldKey{
|
|
{Name: "current", FieldDataType: telemetrytypes.FieldDataTypeNumber},
|
|
{Name: "old", FieldDataType: telemetrytypes.FieldDataTypeNumber},
|
|
},
|
|
}
|
|
expr, err := LogicalValueExpr(context.Background(), valuer.UUID{}, 0, 0, stubFieldMapper{}, logical)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "multiIf(has(current), value(current), has(old), value(old), NULL)", expr)
|
|
}
|
|
|
|
func TestLogicalExistsExprSingleMemberDelegatesToExistsFor(t *testing.T) {
|
|
logical := telemetrytypes.SingleLogicalField("a", &telemetrytypes.TelemetryFieldKey{Name: "a"})
|
|
expr, err := LogicalExistsExpr(context.Background(), valuer.UUID{}, 0, 0, stubFieldMapper{}, logical, false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "NOT has(a)", expr)
|
|
}
|
|
|
|
func TestLogicalExistsExprFamilyIsAnyMemberPresence(t *testing.T) {
|
|
family := stringFamily("current", "old")
|
|
|
|
expr, err := LogicalExistsExpr(context.Background(), valuer.UUID{}, 0, 0, stubFieldMapper{}, family, true)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "(has(current) OR has(old))", expr)
|
|
|
|
expr, err = LogicalExistsExpr(context.Background(), valuer.UUID{}, 0, 0, stubFieldMapper{}, family, false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "NOT (has(current) OR has(old))", expr)
|
|
}
|