fix: issues with like and ilike fixed in v4 qb (#6018)

This commit is contained in:
Nityananda Gohain 2024-09-19 21:20:57 +05:30 committed by GitHub
parent 2f7d208eb5
commit 8eb2cf144e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 48 additions and 16 deletions

View File

@ -146,6 +146,7 @@ func buildAttributeFilter(item v3.FilterItem) (string, error) {
return fmt.Sprintf(logsOp, keyName, fmtVal), nil return fmt.Sprintf(logsOp, keyName, fmtVal), nil
case v3.FilterOperatorContains, v3.FilterOperatorNotContains: case v3.FilterOperatorContains, v3.FilterOperatorNotContains:
// we also want to treat %, _ as literals for contains
val := utils.QuoteEscapedStringForContains(fmt.Sprintf("%s", item.Value)) val := utils.QuoteEscapedStringForContains(fmt.Sprintf("%s", item.Value))
// for body the contains is case insensitive // for body the contains is case insensitive
if keyName == BODY { if keyName == BODY {
@ -153,14 +154,15 @@ func buildAttributeFilter(item v3.FilterItem) (string, error) {
} else { } else {
return fmt.Sprintf("%s %s '%%%s%%'", keyName, logsOp, val), nil return fmt.Sprintf("%s %s '%%%s%%'", keyName, logsOp, val), nil
} }
default: case v3.FilterOperatorLike, v3.FilterOperatorNotLike:
// for use lower for like and ilike // for body use lower for like and ilike
if op == v3.FilterOperatorLike || op == v3.FilterOperatorNotLike { val := utils.QuoteEscapedString(fmt.Sprintf("%s", item.Value))
if keyName == BODY { if keyName == BODY {
keyName = fmt.Sprintf("lower(%s)", keyName) return fmt.Sprintf("lower(%s) %s lower('%s')", keyName, logsOp, val), nil
fmtVal = fmt.Sprintf("lower(%s)", fmtVal) } else {
} return fmt.Sprintf("%s %s '%s'", keyName, logsOp, val), nil
} }
default:
return fmt.Sprintf("%s %s %s", keyName, logsOp, fmtVal), nil return fmt.Sprintf("%s %s %s", keyName, logsOp, fmtVal), nil
} }
} else { } else {

View File

@ -277,10 +277,10 @@ func Test_buildAttributeFilter(t *testing.T) {
Type: v3.AttributeKeyTypeResource, Type: v3.AttributeKeyTypeResource,
}, },
Operator: v3.FilterOperatorLike, Operator: v3.FilterOperatorLike,
Value: "test", Value: "test%",
}, },
}, },
want: "resources_string['service.name'] LIKE 'test'", want: "resources_string['service.name'] LIKE 'test%'",
}, },
{ {
name: "build attribute filter like-body", name: "build attribute filter like-body",

View File

@ -23,8 +23,13 @@ func buildResourceFilter(logsOp string, key string, op v3.FilterOperator, value
return fmt.Sprintf(logsOp, searchKey, chFmtVal) return fmt.Sprintf(logsOp, searchKey, chFmtVal)
case v3.FilterOperatorContains, v3.FilterOperatorNotContains: case v3.FilterOperatorContains, v3.FilterOperatorNotContains:
// this is required as clickhouseFormattedValue add's quotes to the string // this is required as clickhouseFormattedValue add's quotes to the string
// we also want to treat %, _ as literals for contains
escapedStringValue := utils.QuoteEscapedStringForContains(fmt.Sprintf("%s", value)) escapedStringValue := utils.QuoteEscapedStringForContains(fmt.Sprintf("%s", value))
return fmt.Sprintf("%s %s '%%%s%%'", searchKey, logsOp, escapedStringValue) return fmt.Sprintf("%s %s '%%%s%%'", searchKey, logsOp, escapedStringValue)
case v3.FilterOperatorLike, v3.FilterOperatorNotLike:
// this is required as clickhouseFormattedValue add's quotes to the string
escapedStringValue := utils.QuoteEscapedString(fmt.Sprintf("%s", value))
return fmt.Sprintf("%s %s '%s'", searchKey, logsOp, escapedStringValue)
default: default:
return fmt.Sprintf("%s %s %s", searchKey, logsOp, chFmtVal) return fmt.Sprintf("%s %s %s", searchKey, logsOp, chFmtVal)
} }
@ -74,13 +79,19 @@ func buildIndexFilterForInOperator(key string, op v3.FilterOperator, value inter
// example:= x like '%john%' = labels like '%x%john%' // example:= x like '%john%' = labels like '%x%john%'
func buildResourceIndexFilter(key string, op v3.FilterOperator, value interface{}) string { func buildResourceIndexFilter(key string, op v3.FilterOperator, value interface{}) string {
// not using clickhouseFormattedValue as we don't wan't the quotes // not using clickhouseFormattedValue as we don't wan't the quotes
formattedValueEscaped := utils.QuoteEscapedStringForContains(fmt.Sprintf("%s", value)) strVal := fmt.Sprintf("%s", value)
formattedValueEscapedForContains := utils.QuoteEscapedStringForContains(strVal)
formattedValueEscaped := utils.QuoteEscapedString(strVal)
// add index filters // add index filters
switch op { switch op {
case v3.FilterOperatorContains, v3.FilterOperatorEqual, v3.FilterOperatorLike: case v3.FilterOperatorContains:
return fmt.Sprintf("labels like '%%%s%%%s%%'", key, formattedValueEscapedForContains)
case v3.FilterOperatorNotContains:
return fmt.Sprintf("labels not like '%%%s%%%s%%'", key, formattedValueEscapedForContains)
case v3.FilterOperatorLike, v3.FilterOperatorEqual:
return fmt.Sprintf("labels like '%%%s%%%s%%'", key, formattedValueEscaped) return fmt.Sprintf("labels like '%%%s%%%s%%'", key, formattedValueEscaped)
case v3.FilterOperatorNotContains, v3.FilterOperatorNotEqual, v3.FilterOperatorNotLike: case v3.FilterOperatorNotLike, v3.FilterOperatorNotEqual:
return fmt.Sprintf("labels not like '%%%s%%%s%%'", key, formattedValueEscaped) return fmt.Sprintf("labels not like '%%%s%%%s%%'", key, formattedValueEscaped)
case v3.FilterOperatorNotRegex: case v3.FilterOperatorNotRegex:
return fmt.Sprintf("labels not like '%%%s%%'", key) return fmt.Sprintf("labels not like '%%%s%%'", key)

View File

@ -61,9 +61,9 @@ func Test_buildResourceFilter(t *testing.T) {
logsOp: "=", logsOp: "=",
key: "service.name", key: "service.name",
op: v3.FilterOperatorEqual, op: v3.FilterOperatorEqual,
value: "Application", value: "Application%",
}, },
want: `simpleJSONExtractString(labels, 'service.name') = 'Application'`, want: `simpleJSONExtractString(labels, 'service.name') = 'Application%'`,
}, },
{ {
name: "test value with quotes", name: "test value with quotes",
@ -75,6 +75,16 @@ func Test_buildResourceFilter(t *testing.T) {
}, },
want: `simpleJSONExtractString(labels, 'service.name') = 'Application\'s'`, want: `simpleJSONExtractString(labels, 'service.name') = 'Application\'s'`,
}, },
{
name: "test like",
args: args{
logsOp: "LIKE",
key: "service.name",
op: v3.FilterOperatorLike,
value: "Application%_",
},
want: `simpleJSONExtractString(labels, 'service.name') LIKE 'Application%_'`,
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@ -119,9 +129,9 @@ func Test_buildIndexFilterForInOperator(t *testing.T) {
args: args{ args: args{
key: "service.name", key: "service.name",
op: v3.FilterOperatorIn, op: v3.FilterOperatorIn,
value: "application", value: "application%",
}, },
want: `(labels like '%"service.name":"application"%')`, want: `(labels like '%"service.name":"application\%"%')`,
}, },
{ {
name: "test nin string", name: "test nin string",
@ -180,6 +190,15 @@ func Test_buildResourceIndexFilter(t *testing.T) {
}, },
want: `labels not like '%service.name%application\%\_test%'`, want: `labels not like '%service.name%application\%\_test%'`,
}, },
{
name: "test like with % and _",
args: args{
key: "service.name",
op: v3.FilterOperatorLike,
value: "application%_test",
},
want: `labels like '%service.name%application%_test%'`,
},
{ {
name: "test not regex", name: "test not regex",
args: args{ args: args{