mirror of
https://github.com/SigNoz/signoz.git
synced 2026-08-06 05:00:42 +01:00
* fix: convert key not found to warnings * fix(logs): has-family body-only errors, not-found warnings, and condition-builder cleanup - has/hasAny/hasAll/hasToken on a non-body key now return "supports only body JSON search" (both modes); a not-found body path warns and queries the underlying data instead of 400 (JSON mode) - capture the two has-family 500s (hasToken separator/whitespace needle; hasAny/hasAll quoted int >= 2^32) as regression tests - use telemetrytypes.NewTelemetryFieldKey for derived keys (fresh identity-only keys, no stale resolved metadata) instead of struct copies of the field key - rename ConditionForKeys -> ConditionFor and inline conditionsForKeys into it across the builders; rename private conditionFor -> conditionForResolvedKey - move the trace_noise fixture from queriertraces/conftest.py to fixtures/traces.py * fix: convert trace_noise fixture to util
101 lines
3.8 KiB
Go
101 lines
3.8 KiB
Go
package querybuilder
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
schema "github.com/SigNoz/signoz-otel-collector/cmd/signozschemamigrator/schema_migrator"
|
|
"github.com/SigNoz/signoz/pkg/errors"
|
|
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
|
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
|
)
|
|
|
|
// ExistsExpression renders the existence predicate for a key resolved to the given
|
|
// columns (negated when exists is false). Comparisons are against constants rendered
|
|
// as literals, so the expression carries no bind args and can guard column expressions
|
|
// directly. Signal-specific presence checks (body JSON paths, label maps) are handled
|
|
// by the field mappers before falling through to this.
|
|
func ExistsExpression(columns []*schema.Column, key *telemetrytypes.TelemetryFieldKey, tsStart, tsEnd uint64, fieldExpression string, exists bool) (string, error) {
|
|
newColumns, evolutionsEntries, err := qbtypes.SelectEvolutionsForColumns(columns, key.Evolutions, tsStart, tsEnd)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(newColumns) == 0 {
|
|
return "", errors.NewInvalidInputf(errors.CodeInvalidInput, "no valid evolution found for field %s in the given time range", key.Name)
|
|
}
|
|
|
|
comparison := func(operator string, value string) string {
|
|
return fmt.Sprintf("%s %s %s", fieldExpression, operator, value)
|
|
}
|
|
|
|
if len(newColumns) > 1 {
|
|
if exists {
|
|
return fieldExpression + " IS NOT NULL", nil
|
|
}
|
|
return fieldExpression + " IS NULL", nil
|
|
}
|
|
|
|
column := newColumns[0]
|
|
switch column.Type.GetType() {
|
|
case schema.ColumnTypeEnumJSON:
|
|
// the ::String cast in the value expression folds NULL to '', so the
|
|
// presence check must address the raw JSON path
|
|
columnName := column.Name
|
|
if len(evolutionsEntries) > 0 && evolutionsEntries[0] != nil {
|
|
columnName = evolutionsEntries[0].ColumnName
|
|
}
|
|
rawPath := fmt.Sprintf("%s.`%s`", columnName, key.Name)
|
|
if exists {
|
|
return rawPath + " IS NOT NULL", nil
|
|
}
|
|
return rawPath + " IS NULL", nil
|
|
case schema.ColumnTypeEnumString,
|
|
schema.ColumnTypeEnumFixedString,
|
|
schema.ColumnTypeEnumDateTime64:
|
|
if exists {
|
|
return comparison("<>", "''"), nil
|
|
}
|
|
return comparison("=", "''"), nil
|
|
case schema.ColumnTypeEnumLowCardinality:
|
|
switch elementType := column.Type.(schema.LowCardinalityColumnType).ElementType; elementType.GetType() {
|
|
case schema.ColumnTypeEnumString:
|
|
if exists {
|
|
return comparison("<>", "''"), nil
|
|
}
|
|
return comparison("=", "''"), nil
|
|
default:
|
|
return "", errors.NewInvalidInputf(errors.CodeInvalidInput, "exists operator is not supported for low cardinality column type %s", elementType)
|
|
}
|
|
case schema.ColumnTypeEnumUInt64,
|
|
schema.ColumnTypeEnumUInt32,
|
|
schema.ColumnTypeEnumUInt8,
|
|
schema.ColumnTypeEnumInt8,
|
|
schema.ColumnTypeEnumInt16,
|
|
schema.ColumnTypeEnumBool:
|
|
if exists {
|
|
return comparison("<>", "0"), nil
|
|
}
|
|
return comparison("=", "0"), nil
|
|
case schema.ColumnTypeEnumMap:
|
|
keyType := column.Type.(schema.MapColumnType).KeyType
|
|
if _, ok := keyType.(schema.LowCardinalityColumnType); !ok {
|
|
return "", errors.NewInvalidInputf(errors.CodeInvalidInput, "key type %s is not supported for map column type %s", keyType, column.Type)
|
|
}
|
|
|
|
switch valueType := column.Type.(schema.MapColumnType).ValueType; valueType.GetType() {
|
|
case schema.ColumnTypeEnumString, schema.ColumnTypeEnumBool, schema.ColumnTypeEnumFloat64:
|
|
leftOperand := fmt.Sprintf("mapContains(%s, '%s')", column.Name, key.Name)
|
|
if key.Materialized {
|
|
leftOperand = telemetrytypes.FieldKeyToMaterializedColumnNameForExists(key)
|
|
}
|
|
if exists {
|
|
return leftOperand, nil
|
|
}
|
|
return "NOT " + leftOperand, nil
|
|
default:
|
|
return "", errors.NewInvalidInputf(errors.CodeInvalidInput, "exists operator is not supported for map column type %s", valueType)
|
|
}
|
|
default:
|
|
return "", errors.NewInvalidInputf(errors.CodeInvalidInput, "exists operator is not supported for column type %s", column.Type)
|
|
}
|
|
}
|