mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-17 23:47:12 +00:00
fix: exception on resource filters with numeric values (#9028)
This commit is contained in:
parent
ae58915020
commit
a686941880
@ -22,21 +22,20 @@ func NewConditionBuilder(fm qbtypes.FieldMapper) *defaultConditionBuilder {
|
||||
|
||||
func valueForIndexFilter(op qbtypes.FilterOperator, key *telemetrytypes.TelemetryFieldKey, value any) any {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
if op == qbtypes.FilterOperatorEqual || op == qbtypes.FilterOperatorNotEqual {
|
||||
return fmt.Sprintf(`%%%s":"%s%%`, key.Name, v)
|
||||
}
|
||||
return fmt.Sprintf(`%%%s%%%s%%`, key.Name, v)
|
||||
case []any:
|
||||
// assuming array will always be for in and not in
|
||||
values := make([]string, 0, len(v))
|
||||
for _, v := range v {
|
||||
values = append(values, fmt.Sprintf(`%%%s":"%s%%`, key.Name, v))
|
||||
values = append(values, fmt.Sprintf(`%%%s":"%s%%`, key.Name, querybuilder.FormatValueForContains(v)))
|
||||
}
|
||||
return values
|
||||
default:
|
||||
// format to string for anything else as we store resource values as string
|
||||
if op == qbtypes.FilterOperatorEqual || op == qbtypes.FilterOperatorNotEqual {
|
||||
return fmt.Sprintf(`%%%s":"%s%%`, key.Name, querybuilder.FormatValueForContains(v))
|
||||
}
|
||||
return fmt.Sprintf(`%%%s%%%s%%`, key.Name, querybuilder.FormatValueForContains(v))
|
||||
}
|
||||
// resource table expects string value
|
||||
return fmt.Sprintf(`%%%v%%`, value)
|
||||
}
|
||||
|
||||
func keyIndexFilter(key *telemetrytypes.TelemetryFieldKey) any {
|
||||
@ -55,15 +54,9 @@ func (b *defaultConditionBuilder) ConditionFor(
|
||||
return "true", nil
|
||||
}
|
||||
|
||||
switch op {
|
||||
case qbtypes.FilterOperatorContains,
|
||||
qbtypes.FilterOperatorNotContains,
|
||||
qbtypes.FilterOperatorILike,
|
||||
qbtypes.FilterOperatorNotILike,
|
||||
qbtypes.FilterOperatorLike,
|
||||
qbtypes.FilterOperatorNotLike:
|
||||
value = querybuilder.FormatValueForContains(value)
|
||||
}
|
||||
// except for in, not in, between, not between all other operators should have formatted value
|
||||
// as we store resource values as string
|
||||
formattedValue := querybuilder.FormatValueForContains(value)
|
||||
|
||||
column, err := b.fm.ColumnFor(ctx, key)
|
||||
if err != nil {
|
||||
@ -81,34 +74,34 @@ func (b *defaultConditionBuilder) ConditionFor(
|
||||
switch op {
|
||||
case qbtypes.FilterOperatorEqual:
|
||||
return sb.And(
|
||||
sb.E(fieldName, value),
|
||||
sb.E(fieldName, formattedValue),
|
||||
keyIdxFilter,
|
||||
sb.Like(column.Name, valueForIndexFilter),
|
||||
), nil
|
||||
case qbtypes.FilterOperatorNotEqual:
|
||||
return sb.And(
|
||||
sb.NE(fieldName, value),
|
||||
sb.NE(fieldName, formattedValue),
|
||||
sb.NotLike(column.Name, valueForIndexFilter),
|
||||
), nil
|
||||
case qbtypes.FilterOperatorGreaterThan:
|
||||
return sb.And(sb.GT(fieldName, value), keyIdxFilter), nil
|
||||
return sb.And(sb.GT(fieldName, formattedValue), keyIdxFilter), nil
|
||||
case qbtypes.FilterOperatorGreaterThanOrEq:
|
||||
return sb.And(sb.GE(fieldName, value), keyIdxFilter), nil
|
||||
return sb.And(sb.GE(fieldName, formattedValue), keyIdxFilter), nil
|
||||
case qbtypes.FilterOperatorLessThan:
|
||||
return sb.And(sb.LT(fieldName, value), keyIdxFilter), nil
|
||||
return sb.And(sb.LT(fieldName, formattedValue), keyIdxFilter), nil
|
||||
case qbtypes.FilterOperatorLessThanOrEq:
|
||||
return sb.And(sb.LE(fieldName, value), keyIdxFilter), nil
|
||||
return sb.And(sb.LE(fieldName, formattedValue), keyIdxFilter), nil
|
||||
|
||||
case qbtypes.FilterOperatorLike, qbtypes.FilterOperatorILike:
|
||||
return sb.And(
|
||||
sb.ILike(fieldName, value),
|
||||
sb.ILike(fieldName, formattedValue),
|
||||
keyIdxFilter,
|
||||
sb.ILike(column.Name, valueForIndexFilter),
|
||||
), nil
|
||||
case qbtypes.FilterOperatorNotLike, qbtypes.FilterOperatorNotILike:
|
||||
// no index filter: as cannot apply `not contains x%y` as y can be somewhere else
|
||||
return sb.And(
|
||||
sb.NotILike(fieldName, value),
|
||||
sb.NotILike(fieldName, formattedValue),
|
||||
), nil
|
||||
|
||||
case qbtypes.FilterOperatorBetween:
|
||||
@ -119,7 +112,7 @@ func (b *defaultConditionBuilder) ConditionFor(
|
||||
if len(values) != 2 {
|
||||
return "", qbtypes.ErrBetweenValues
|
||||
}
|
||||
return sb.And(keyIdxFilter, sb.Between(fieldName, values[0], values[1])), nil
|
||||
return sb.And(keyIdxFilter, sb.Between(fieldName, querybuilder.FormatValueForContains(values[0]), querybuilder.FormatValueForContains(values[1]))), nil
|
||||
case qbtypes.FilterOperatorNotBetween:
|
||||
values, ok := value.([]any)
|
||||
if !ok {
|
||||
@ -128,7 +121,7 @@ func (b *defaultConditionBuilder) ConditionFor(
|
||||
if len(values) != 2 {
|
||||
return "", qbtypes.ErrBetweenValues
|
||||
}
|
||||
return sb.And(sb.NotBetween(fieldName, values[0], values[1])), nil
|
||||
return sb.And(sb.NotBetween(fieldName, querybuilder.FormatValueForContains(values[0]), querybuilder.FormatValueForContains(values[1]))), nil
|
||||
|
||||
case qbtypes.FilterOperatorIn:
|
||||
values, ok := value.([]any)
|
||||
@ -137,7 +130,7 @@ func (b *defaultConditionBuilder) ConditionFor(
|
||||
}
|
||||
inConditions := make([]string, 0, len(values))
|
||||
for _, v := range values {
|
||||
inConditions = append(inConditions, sb.E(fieldName, v))
|
||||
inConditions = append(inConditions, sb.E(fieldName, querybuilder.FormatValueForContains(v)))
|
||||
}
|
||||
mainCondition := sb.Or(inConditions...)
|
||||
valConditions := make([]string, 0, len(values))
|
||||
@ -156,7 +149,7 @@ func (b *defaultConditionBuilder) ConditionFor(
|
||||
}
|
||||
notInConditions := make([]string, 0, len(values))
|
||||
for _, v := range values {
|
||||
notInConditions = append(notInConditions, sb.NE(fieldName, v))
|
||||
notInConditions = append(notInConditions, sb.NE(fieldName, querybuilder.FormatValueForContains(v)))
|
||||
}
|
||||
mainCondition := sb.And(notInConditions...)
|
||||
valConditions := make([]string, 0, len(values))
|
||||
@ -180,24 +173,24 @@ func (b *defaultConditionBuilder) ConditionFor(
|
||||
|
||||
case qbtypes.FilterOperatorRegexp:
|
||||
return sb.And(
|
||||
fmt.Sprintf("match(%s, %s)", fieldName, sb.Var(value)),
|
||||
fmt.Sprintf("match(%s, %s)", fieldName, sb.Var(formattedValue)),
|
||||
keyIdxFilter,
|
||||
), nil
|
||||
case qbtypes.FilterOperatorNotRegexp:
|
||||
return sb.And(
|
||||
fmt.Sprintf("NOT match(%s, %s)", fieldName, sb.Var(value)),
|
||||
fmt.Sprintf("NOT match(%s, %s)", fieldName, sb.Var(formattedValue)),
|
||||
), nil
|
||||
|
||||
case qbtypes.FilterOperatorContains:
|
||||
return sb.And(
|
||||
sb.ILike(fieldName, fmt.Sprintf(`%%%s%%`, value)),
|
||||
sb.ILike(fieldName, fmt.Sprintf(`%%%s%%`, formattedValue)),
|
||||
keyIdxFilter,
|
||||
sb.ILike(column.Name, valueForIndexFilter),
|
||||
), nil
|
||||
case qbtypes.FilterOperatorNotContains:
|
||||
// no index filter: as cannot apply `not contains x%y` as y can be somewhere else
|
||||
return sb.And(
|
||||
sb.NotILike(fieldName, fmt.Sprintf(`%%%s%%`, value)),
|
||||
sb.NotILike(fieldName, fmt.Sprintf(`%%%s%%`, formattedValue)),
|
||||
), nil
|
||||
}
|
||||
return "", qbtypes.ErrUnsupportedOperator
|
||||
|
||||
@ -143,6 +143,61 @@ func TestConditionBuilder(t *testing.T) {
|
||||
expected: "simpleJSONHas(labels, 'k8s.namespace.name') <> ?",
|
||||
expectedArgs: []any{true},
|
||||
},
|
||||
{
|
||||
name: "number_equals",
|
||||
key: &telemetrytypes.TelemetryFieldKey{
|
||||
Name: "test_num",
|
||||
FieldContext: telemetrytypes.FieldContextResource,
|
||||
},
|
||||
op: querybuildertypesv5.FilterOperatorEqual,
|
||||
value: 1,
|
||||
expected: "simpleJSONExtractString(labels, 'test_num') = ? AND labels LIKE ? AND labels LIKE ?",
|
||||
expectedArgs: []any{"1", "%test_num%", "%test_num\":\"1%"},
|
||||
},
|
||||
{
|
||||
name: "number_gt",
|
||||
key: &telemetrytypes.TelemetryFieldKey{
|
||||
Name: "test_num",
|
||||
FieldContext: telemetrytypes.FieldContextResource,
|
||||
},
|
||||
op: querybuildertypesv5.FilterOperatorGreaterThan,
|
||||
value: 1,
|
||||
expected: "simpleJSONExtractString(labels, 'test_num') > ? AND labels LIKE ?",
|
||||
expectedArgs: []any{"1", "%test_num%"},
|
||||
},
|
||||
{
|
||||
name: "number_in",
|
||||
key: &telemetrytypes.TelemetryFieldKey{
|
||||
Name: "test_num",
|
||||
FieldContext: telemetrytypes.FieldContextResource,
|
||||
},
|
||||
op: querybuildertypesv5.FilterOperatorIn,
|
||||
value: []any{1, 2},
|
||||
expected: "(simpleJSONExtractString(labels, 'test_num') = ? OR simpleJSONExtractString(labels, 'test_num') = ?) AND labels LIKE ? AND (labels LIKE ? OR labels LIKE ?)",
|
||||
expectedArgs: []any{"1", "2", "%test_num%", "%test_num\":\"1%", "%test_num\":\"2%"},
|
||||
},
|
||||
{
|
||||
name: "number_between",
|
||||
key: &telemetrytypes.TelemetryFieldKey{
|
||||
Name: "test_num",
|
||||
FieldContext: telemetrytypes.FieldContextResource,
|
||||
},
|
||||
op: querybuildertypesv5.FilterOperatorBetween,
|
||||
value: []any{1, 2},
|
||||
expected: "labels LIKE ? AND simpleJSONExtractString(labels, 'test_num') BETWEEN ? AND ?",
|
||||
expectedArgs: []any{"%test_num%", "1", "2"},
|
||||
},
|
||||
{
|
||||
name: "string_regexp",
|
||||
key: &telemetrytypes.TelemetryFieldKey{
|
||||
Name: "k8s.namespace.name",
|
||||
FieldContext: telemetrytypes.FieldContextResource,
|
||||
},
|
||||
op: querybuildertypesv5.FilterOperatorRegexp,
|
||||
value: "ban.*",
|
||||
expected: "match(simpleJSONExtractString(labels, 'k8s.namespace.name'), ?) AND labels LIKE ?",
|
||||
expectedArgs: []any{"ban.*", "%k8s.namespace.name%"},
|
||||
},
|
||||
}
|
||||
|
||||
fm := NewFieldMapper()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user