mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-23 18:36:16 +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 {
|
func valueForIndexFilter(op qbtypes.FilterOperator, key *telemetrytypes.TelemetryFieldKey, value any) any {
|
||||||
switch v := value.(type) {
|
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:
|
case []any:
|
||||||
// assuming array will always be for in and not in
|
// assuming array will always be for in and not in
|
||||||
values := make([]string, 0, len(v))
|
values := make([]string, 0, len(v))
|
||||||
for _, v := range 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
|
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 {
|
func keyIndexFilter(key *telemetrytypes.TelemetryFieldKey) any {
|
||||||
@ -55,15 +54,9 @@ func (b *defaultConditionBuilder) ConditionFor(
|
|||||||
return "true", nil
|
return "true", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch op {
|
// except for in, not in, between, not between all other operators should have formatted value
|
||||||
case qbtypes.FilterOperatorContains,
|
// as we store resource values as string
|
||||||
qbtypes.FilterOperatorNotContains,
|
formattedValue := querybuilder.FormatValueForContains(value)
|
||||||
qbtypes.FilterOperatorILike,
|
|
||||||
qbtypes.FilterOperatorNotILike,
|
|
||||||
qbtypes.FilterOperatorLike,
|
|
||||||
qbtypes.FilterOperatorNotLike:
|
|
||||||
value = querybuilder.FormatValueForContains(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
column, err := b.fm.ColumnFor(ctx, key)
|
column, err := b.fm.ColumnFor(ctx, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -81,34 +74,34 @@ func (b *defaultConditionBuilder) ConditionFor(
|
|||||||
switch op {
|
switch op {
|
||||||
case qbtypes.FilterOperatorEqual:
|
case qbtypes.FilterOperatorEqual:
|
||||||
return sb.And(
|
return sb.And(
|
||||||
sb.E(fieldName, value),
|
sb.E(fieldName, formattedValue),
|
||||||
keyIdxFilter,
|
keyIdxFilter,
|
||||||
sb.Like(column.Name, valueForIndexFilter),
|
sb.Like(column.Name, valueForIndexFilter),
|
||||||
), nil
|
), nil
|
||||||
case qbtypes.FilterOperatorNotEqual:
|
case qbtypes.FilterOperatorNotEqual:
|
||||||
return sb.And(
|
return sb.And(
|
||||||
sb.NE(fieldName, value),
|
sb.NE(fieldName, formattedValue),
|
||||||
sb.NotLike(column.Name, valueForIndexFilter),
|
sb.NotLike(column.Name, valueForIndexFilter),
|
||||||
), nil
|
), nil
|
||||||
case qbtypes.FilterOperatorGreaterThan:
|
case qbtypes.FilterOperatorGreaterThan:
|
||||||
return sb.And(sb.GT(fieldName, value), keyIdxFilter), nil
|
return sb.And(sb.GT(fieldName, formattedValue), keyIdxFilter), nil
|
||||||
case qbtypes.FilterOperatorGreaterThanOrEq:
|
case qbtypes.FilterOperatorGreaterThanOrEq:
|
||||||
return sb.And(sb.GE(fieldName, value), keyIdxFilter), nil
|
return sb.And(sb.GE(fieldName, formattedValue), keyIdxFilter), nil
|
||||||
case qbtypes.FilterOperatorLessThan:
|
case qbtypes.FilterOperatorLessThan:
|
||||||
return sb.And(sb.LT(fieldName, value), keyIdxFilter), nil
|
return sb.And(sb.LT(fieldName, formattedValue), keyIdxFilter), nil
|
||||||
case qbtypes.FilterOperatorLessThanOrEq:
|
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:
|
case qbtypes.FilterOperatorLike, qbtypes.FilterOperatorILike:
|
||||||
return sb.And(
|
return sb.And(
|
||||||
sb.ILike(fieldName, value),
|
sb.ILike(fieldName, formattedValue),
|
||||||
keyIdxFilter,
|
keyIdxFilter,
|
||||||
sb.ILike(column.Name, valueForIndexFilter),
|
sb.ILike(column.Name, valueForIndexFilter),
|
||||||
), nil
|
), nil
|
||||||
case qbtypes.FilterOperatorNotLike, qbtypes.FilterOperatorNotILike:
|
case qbtypes.FilterOperatorNotLike, qbtypes.FilterOperatorNotILike:
|
||||||
// no index filter: as cannot apply `not contains x%y` as y can be somewhere else
|
// no index filter: as cannot apply `not contains x%y` as y can be somewhere else
|
||||||
return sb.And(
|
return sb.And(
|
||||||
sb.NotILike(fieldName, value),
|
sb.NotILike(fieldName, formattedValue),
|
||||||
), nil
|
), nil
|
||||||
|
|
||||||
case qbtypes.FilterOperatorBetween:
|
case qbtypes.FilterOperatorBetween:
|
||||||
@ -119,7 +112,7 @@ func (b *defaultConditionBuilder) ConditionFor(
|
|||||||
if len(values) != 2 {
|
if len(values) != 2 {
|
||||||
return "", qbtypes.ErrBetweenValues
|
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:
|
case qbtypes.FilterOperatorNotBetween:
|
||||||
values, ok := value.([]any)
|
values, ok := value.([]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -128,7 +121,7 @@ func (b *defaultConditionBuilder) ConditionFor(
|
|||||||
if len(values) != 2 {
|
if len(values) != 2 {
|
||||||
return "", qbtypes.ErrBetweenValues
|
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:
|
case qbtypes.FilterOperatorIn:
|
||||||
values, ok := value.([]any)
|
values, ok := value.([]any)
|
||||||
@ -137,7 +130,7 @@ func (b *defaultConditionBuilder) ConditionFor(
|
|||||||
}
|
}
|
||||||
inConditions := make([]string, 0, len(values))
|
inConditions := make([]string, 0, len(values))
|
||||||
for _, v := range 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...)
|
mainCondition := sb.Or(inConditions...)
|
||||||
valConditions := make([]string, 0, len(values))
|
valConditions := make([]string, 0, len(values))
|
||||||
@ -156,7 +149,7 @@ func (b *defaultConditionBuilder) ConditionFor(
|
|||||||
}
|
}
|
||||||
notInConditions := make([]string, 0, len(values))
|
notInConditions := make([]string, 0, len(values))
|
||||||
for _, v := range 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...)
|
mainCondition := sb.And(notInConditions...)
|
||||||
valConditions := make([]string, 0, len(values))
|
valConditions := make([]string, 0, len(values))
|
||||||
@ -180,24 +173,24 @@ func (b *defaultConditionBuilder) ConditionFor(
|
|||||||
|
|
||||||
case qbtypes.FilterOperatorRegexp:
|
case qbtypes.FilterOperatorRegexp:
|
||||||
return sb.And(
|
return sb.And(
|
||||||
fmt.Sprintf("match(%s, %s)", fieldName, sb.Var(value)),
|
fmt.Sprintf("match(%s, %s)", fieldName, sb.Var(formattedValue)),
|
||||||
keyIdxFilter,
|
keyIdxFilter,
|
||||||
), nil
|
), nil
|
||||||
case qbtypes.FilterOperatorNotRegexp:
|
case qbtypes.FilterOperatorNotRegexp:
|
||||||
return sb.And(
|
return sb.And(
|
||||||
fmt.Sprintf("NOT match(%s, %s)", fieldName, sb.Var(value)),
|
fmt.Sprintf("NOT match(%s, %s)", fieldName, sb.Var(formattedValue)),
|
||||||
), nil
|
), nil
|
||||||
|
|
||||||
case qbtypes.FilterOperatorContains:
|
case qbtypes.FilterOperatorContains:
|
||||||
return sb.And(
|
return sb.And(
|
||||||
sb.ILike(fieldName, fmt.Sprintf(`%%%s%%`, value)),
|
sb.ILike(fieldName, fmt.Sprintf(`%%%s%%`, formattedValue)),
|
||||||
keyIdxFilter,
|
keyIdxFilter,
|
||||||
sb.ILike(column.Name, valueForIndexFilter),
|
sb.ILike(column.Name, valueForIndexFilter),
|
||||||
), nil
|
), nil
|
||||||
case qbtypes.FilterOperatorNotContains:
|
case qbtypes.FilterOperatorNotContains:
|
||||||
// no index filter: as cannot apply `not contains x%y` as y can be somewhere else
|
// no index filter: as cannot apply `not contains x%y` as y can be somewhere else
|
||||||
return sb.And(
|
return sb.And(
|
||||||
sb.NotILike(fieldName, fmt.Sprintf(`%%%s%%`, value)),
|
sb.NotILike(fieldName, fmt.Sprintf(`%%%s%%`, formattedValue)),
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
return "", qbtypes.ErrUnsupportedOperator
|
return "", qbtypes.ErrUnsupportedOperator
|
||||||
|
|||||||
@ -143,6 +143,61 @@ func TestConditionBuilder(t *testing.T) {
|
|||||||
expected: "simpleJSONHas(labels, 'k8s.namespace.name') <> ?",
|
expected: "simpleJSONHas(labels, 'k8s.namespace.name') <> ?",
|
||||||
expectedArgs: []any{true},
|
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()
|
fm := NewFieldMapper()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user