diff --git a/engine/filters.go b/engine/filters.go index bf73ee9c2..846ac37fd 100644 --- a/engine/filters.go +++ b/engine/filters.go @@ -876,6 +876,11 @@ func (fltr *FilterRule) FilterToSQLQuery() (conditions []string) { var restOfItems string // Excluding ~*req, hold the rest of the items past the first one. If only 1 item in all element, holds that item. e.g. "Charges[0].RatingID" out of ~*req.cost_details.Charges[0].RatingID or "answer_time" out of ~*req.answer_time not := strings.HasPrefix(fltr.Type, utils.MetaNot) elementItems := fltr.ElementItems()[1:] // exclude first item: ~*req + for i := range elementItems { // encapsulate with "" strings starting with * + if strings.HasPrefix(elementItems[i], utils.Meta) { + elementItems[i] = "\"" + elementItems[i] + "\"" + } + } if len(elementItems) > 1 { firstItem = elementItems[0] restOfItems = strings.Join(elementItems[1:], utils.NestingSep) @@ -892,7 +897,11 @@ func (fltr *FilterRule) FilterToSQLQuery() (conditions []string) { conditions = append(conditions, fmt.Sprintf("%s IS NULL", restOfItems)) return } - conditions = append(conditions, fmt.Sprintf("JSON_VALUE(%s, '$.%s') IS NULL", firstItem, restOfItems)) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') IS NULL", firstItem, restOfItems) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + conditions = append(conditions, queryPart) return } // existing means Column IS NOT NULL @@ -900,21 +909,33 @@ func (fltr *FilterRule) FilterToSQLQuery() (conditions []string) { conditions = append(conditions, fmt.Sprintf("%s IS NOT NULL", restOfItems)) return } - conditions = append(conditions, fmt.Sprintf("JSON_VALUE(%s, '$.%s') IS NOT NULL", firstItem, restOfItems)) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') IS NOT NULL", firstItem, restOfItems) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + conditions = append(conditions, queryPart) case utils.MetaEmpty, utils.MetaNotEmpty: if not { if firstItem == utils.EmptyString { conditions = append(conditions, fmt.Sprintf("%s != ''", restOfItems)) return } - conditions = append(conditions, fmt.Sprintf("JSON_VALUE(%s, '$.%s') != ''", firstItem, restOfItems)) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') != ''", firstItem, restOfItems) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + conditions = append(conditions, queryPart) return } if firstItem == utils.EmptyString { conditions = append(conditions, fmt.Sprintf("%s == ''", restOfItems)) return } - conditions = append(conditions, fmt.Sprintf("JSON_VALUE(%s, '$.%s') == ''", firstItem, restOfItems)) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') == ''", firstItem, restOfItems) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + conditions = append(conditions, queryPart) } return } @@ -934,14 +955,22 @@ func (fltr *FilterRule) FilterToSQLQuery() (conditions []string) { conditions = append(conditions, fmt.Sprintf("%s != '%s'", restOfItems, value)) continue } - conditions = append(conditions, fmt.Sprintf("JSON_VALUE(%s, '$.%s') != '%s'", - firstItem, restOfItems, value)) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') != '%s'", + firstItem, restOfItems, value) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + conditions = append(conditions, queryPart) continue } if firstItem == utils.EmptyString { singleCond = fmt.Sprintf("%s = '%s'", restOfItems, value) } else { - singleCond = fmt.Sprintf("JSON_VALUE(%s, '$.%s') = '%s'", firstItem, restOfItems, value) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') = '%s'", firstItem, restOfItems, value) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + singleCond = queryPart } case utils.MetaLessThan, utils.MetaLessOrEqual, utils.MetaGreaterThan, utils.MetaGreaterOrEqual: parsedValAny := utils.StringToInterface(value) @@ -950,25 +979,41 @@ func (fltr *FilterRule) FilterToSQLQuery() (conditions []string) { if firstItem == utils.EmptyString { singleCond = fmt.Sprintf("%s >= '%v'", restOfItems, parsedValAny) } else { - singleCond = fmt.Sprintf("JSON_VALUE(%s, '$.%s') >= '%v'", firstItem, restOfItems, parsedValAny) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') >= '%v'", firstItem, restOfItems, parsedValAny) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + singleCond = queryPart } case utils.MetaGreaterThan: if firstItem == utils.EmptyString { singleCond = fmt.Sprintf("%s > '%v'", restOfItems, parsedValAny) } else { - singleCond = fmt.Sprintf("JSON_VALUE(%s, '$.%s') > '%v'", firstItem, restOfItems, parsedValAny) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') > '%v'", firstItem, restOfItems, parsedValAny) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + singleCond = queryPart } case utils.MetaLessOrEqual: if firstItem == utils.EmptyString { singleCond = fmt.Sprintf("%s <= '%v'", restOfItems, parsedValAny) } else { - singleCond = fmt.Sprintf("JSON_VALUE(%s, '$.%s') <= '%v'", firstItem, restOfItems, parsedValAny) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') <= '%v'", firstItem, restOfItems, parsedValAny) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + singleCond = queryPart } case utils.MetaLessThan: if firstItem == utils.EmptyString { singleCond = fmt.Sprintf("%s < '%v'", restOfItems, parsedValAny) } else { - singleCond = fmt.Sprintf("JSON_VALUE(%s, '$.%s') < '%v'", firstItem, restOfItems, parsedValAny) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') < '%v'", firstItem, restOfItems, parsedValAny) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + singleCond = queryPart } } case utils.MetaPrefix, utils.MetaNotPrefix: @@ -977,13 +1022,21 @@ func (fltr *FilterRule) FilterToSQLQuery() (conditions []string) { conditions = append(conditions, fmt.Sprintf("%s NOT LIKE '%s%%'", restOfItems, value)) continue } - conditions = append(conditions, fmt.Sprintf("JSON_VALUE(%s, '$.%s') NOT LIKE '%s%%'", firstItem, restOfItems, value)) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') NOT LIKE '%s%%'", firstItem, restOfItems, value) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + conditions = append(conditions, queryPart) continue } if firstItem == utils.EmptyString { singleCond = fmt.Sprintf("%s LIKE '%s%%'", restOfItems, value) } else { - singleCond = fmt.Sprintf("JSON_VALUE(%s, '$.%s') LIKE '%s%%'", firstItem, restOfItems, value) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') LIKE '%s%%'", firstItem, restOfItems, value) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + singleCond = queryPart } case utils.MetaSuffix, utils.MetaNotSuffix: if not { @@ -991,13 +1044,21 @@ func (fltr *FilterRule) FilterToSQLQuery() (conditions []string) { conditions = append(conditions, fmt.Sprintf("%s NOT LIKE '%%%s'", restOfItems, value)) continue } - conditions = append(conditions, fmt.Sprintf("JSON_VALUE(%s, '$.%s') NOT LIKE '%%%s'", firstItem, restOfItems, value)) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') NOT LIKE '%%%s'", firstItem, restOfItems, value) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + conditions = append(conditions, queryPart) continue } if firstItem == utils.EmptyString { singleCond = fmt.Sprintf("%s LIKE '%%%s'", restOfItems, value) } else { - singleCond = fmt.Sprintf("JSON_VALUE(%s, '$.%s') LIKE '%%%s'", firstItem, restOfItems, value) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') LIKE '%%%s'", firstItem, restOfItems, value) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + singleCond = queryPart } case utils.MetaRegex, utils.MetaNotRegex: if not { @@ -1005,13 +1066,21 @@ func (fltr *FilterRule) FilterToSQLQuery() (conditions []string) { conditions = append(conditions, fmt.Sprintf("%s NOT REGEXP '%s'", restOfItems, value)) continue } - conditions = append(conditions, fmt.Sprintf("JSON_VALUE(%s, '$.%s') NOT REGEXP '%s'", firstItem, restOfItems, value)) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') NOT REGEXP '%s'", firstItem, restOfItems, value) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + conditions = append(conditions, queryPart) continue } if firstItem == utils.EmptyString { singleCond = fmt.Sprintf("%s REGEXP '%s'", restOfItems, value) } else { - singleCond = fmt.Sprintf("JSON_VALUE(%s, '$.%s') REGEXP '%s'", firstItem, restOfItems, value) + queryPart := fmt.Sprintf("JSON_VALUE(%s, '$.%s') REGEXP '%s'", firstItem, restOfItems, value) + if strings.HasPrefix(restOfItems, `"*`) { + queryPart = fmt.Sprintf("JSON_UNQUOTE(%s)", queryPart) + } + singleCond = queryPart } } conditions = append(conditions, singleCond) diff --git a/engine/filters_test.go b/engine/filters_test.go index ff3d7f1ad..4ec11124f 100644 --- a/engine/filters_test.go +++ b/engine/filters_test.go @@ -3324,6 +3324,15 @@ func TestFilterToSQLQueryValidations(t *testing.T) { }, expected: []string{"JSON_VALUE(data, '$.name') LIKE 'prefix%'"}, }, + { + name: "Prefix LIKE with * column with JSON_VALUE", + fltrRule: FilterRule{ + Type: utils.MetaPrefix, + Element: "~*req.data.*name", + Values: []string{"prefix"}, + }, + expected: []string{`JSON_UNQUOTE(JSON_VALUE(data, '$."*name"') LIKE 'prefix%')`}, + }, { name: "Suffix NOT LIKE with empty beforeSep", fltrRule: FilterRule{ @@ -3342,6 +3351,15 @@ func TestFilterToSQLQueryValidations(t *testing.T) { }, expected: []string{"JSON_VALUE(data, '$.name') LIKE '%suffix'"}, }, + { + name: "Suffix LIKE with * column with JSON_VALUE", + fltrRule: FilterRule{ + Type: utils.MetaSuffix, + Element: "~*req.data.*name", + Values: []string{"suffix"}, + }, + expected: []string{`JSON_UNQUOTE(JSON_VALUE(data, '$."*name"') LIKE '%suffix')`}, + }, { name: "Regex NOT REGEXP with empty beforeSep", fltrRule: FilterRule{ @@ -3360,7 +3378,15 @@ func TestFilterToSQLQueryValidations(t *testing.T) { }, expected: []string{"JSON_VALUE(data, '$.pattern') REGEXP '[0-9]+'"}, }, - + { + name: "Regex REGEXP with * column with JSON_VALUE", + fltrRule: FilterRule{ + Type: utils.MetaRegex, + Element: "~*req.data.*pattern", + Values: []string{"[0-9]+"}, + }, + expected: []string{`JSON_UNQUOTE(JSON_VALUE(data, '$."*pattern"') REGEXP '[0-9]+')`}, + }, { name: "Not equal with empty beforeSep", fltrRule: FilterRule{ @@ -3379,6 +3405,15 @@ func TestFilterToSQLQueryValidations(t *testing.T) { }, expected: []string{"JSON_VALUE(data, '$.status') = 'active'"}, }, + { + name: "Equal condition with * column with JSON_VALUE", + fltrRule: FilterRule{ + Type: utils.MetaString, + Element: "~*req.data.*status", + Values: []string{"active"}, + }, + expected: []string{`JSON_UNQUOTE(JSON_VALUE(data, '$."*status"') = 'active')`}, + }, { name: "Greater than condition with JSON_VALUE", fltrRule: FilterRule{ @@ -3388,6 +3423,15 @@ func TestFilterToSQLQueryValidations(t *testing.T) { }, expected: []string{"JSON_VALUE(data, '$.score') > '50'"}, }, + { + name: "Greater than condition with * column with JSON_VALUE", + fltrRule: FilterRule{ + Type: utils.MetaGreaterThan, + Element: "~*req.data.*score", + Values: []string{"50"}, + }, + expected: []string{`JSON_UNQUOTE(JSON_VALUE(data, '$."*score"') > '50')`}, + }, { name: "Less than or equal condition with JSON_VALUE", fltrRule: FilterRule{ @@ -3397,6 +3441,15 @@ func TestFilterToSQLQueryValidations(t *testing.T) { }, expected: []string{"JSON_VALUE(data, '$.score') <= '30'"}, }, + { + name: "Less than or equal condition with * column with JSON_VALUE", + fltrRule: FilterRule{ + Type: utils.MetaLessOrEqual, + Element: "~*req.data.*score", + Values: []string{"30"}, + }, + expected: []string{`JSON_UNQUOTE(JSON_VALUE(data, '$."*score"') <= '30')`}, + }, { name: "Less than condition with JSON_VALUE", fltrRule: FilterRule{ @@ -3406,7 +3459,15 @@ func TestFilterToSQLQueryValidations(t *testing.T) { }, expected: []string{"JSON_VALUE(data, '$.score') < '20'"}, }, - + { + name: "Less than condition with * column with JSON_VALUE", + fltrRule: FilterRule{ + Type: utils.MetaLessThan, + Element: "~*req.data.*score", + Values: []string{"20"}, + }, + expected: []string{`JSON_UNQUOTE(JSON_VALUE(data, '$."*score"') < '20')`}, + }, { name: "MetaExists with no values", fltrRule: FilterRule{ @@ -3425,6 +3486,15 @@ func TestFilterToSQLQueryValidations(t *testing.T) { }, expected: []string{"JSON_VALUE(json_field, '$.key') IS NULL"}, }, + { + name: "MetaNotExists with *column with no values", + fltrRule: FilterRule{ + Type: utils.MetaNotExists, + Element: "~*req.json_field.*key", + Values: nil, + }, + expected: []string{`JSON_UNQUOTE(JSON_VALUE(json_field, '$."*key"') IS NULL)`}, + }, { name: "MetaString with values", fltrRule: FilterRule{ @@ -3443,6 +3513,15 @@ func TestFilterToSQLQueryValidations(t *testing.T) { }, expected: []string{"JSON_VALUE(json_field, '$.key') NOT LIKE 'prefix1%'"}, }, + { + name: "MetaPrefix with *column name NOT condition", + fltrRule: FilterRule{ + Type: utils.MetaNotPrefix, + Element: "~*req.json_field.*key", + Values: []string{"prefix1"}, + }, + expected: []string{`JSON_UNQUOTE(JSON_VALUE(json_field, '$."*key"') NOT LIKE 'prefix1%')`}, + }, { name: "MetaRegex with multiple values", fltrRule: FilterRule{