BuildEntSelectorFromJSONFilter constructs an Ent SQL predicate for JSON filtering.
(jsonFilter *JSONFilter)
| 72 | |
| 73 | // BuildEntSelectorFromJSONFilter constructs an Ent SQL predicate for JSON filtering. |
| 74 | func BuildEntSelectorFromJSONFilter(jsonFilter *JSONFilter) (*entsql.Predicate, error) { |
| 75 | if jsonFilter.Column == "" || jsonFilter.Operator == "" { |
| 76 | return nil, errors.New("invalid filter: column and operator are required") |
| 77 | } |
| 78 | |
| 79 | // Validate the column before it reaches the SQL builder. Like the field |
| 80 | // path, the column is emitted into the query by the ent sqljson helpers |
| 81 | // without reliable escaping, so an unsafe value would allow SQL injection. |
| 82 | if err := validateColumn(jsonFilter.Column); err != nil { |
| 83 | return nil, err |
| 84 | } |
| 85 | |
| 86 | // Validate the field path before it reaches the SQL builder. The ent |
| 87 | // sqljson helpers concatenate the path segments unescaped into the query, |
| 88 | // so an unsafe value would allow SQL injection. |
| 89 | if err := validateFieldPath(jsonFilter.FieldPath); err != nil { |
| 90 | return nil, err |
| 91 | } |
| 92 | |
| 93 | // Convert the dot notation to the path that Ent expects. |
| 94 | dotPath := sqljson.DotPath(jsonFilter.FieldPath) |
| 95 | |
| 96 | // Build the predicate based on the operator. |
| 97 | switch jsonFilter.Operator { |
| 98 | case OpEQ: |
| 99 | return sqljson.ValueEQ(jsonFilter.Column, jsonFilter.Value, dotPath), nil |
| 100 | case OpNEQ: |
| 101 | return sqljson.ValueNEQ(jsonFilter.Column, jsonFilter.Value, dotPath), nil |
| 102 | case OpIN: |
| 103 | // Parse the jsonFilter.Value into a string |
| 104 | castedValue, ok := jsonFilter.Value.(string) |
| 105 | if !ok { |
| 106 | return nil, errors.New("invalid value for 'in' operator: must be a slice of strings") |
| 107 | } |
| 108 | |
| 109 | // Split the string into a slice of strings by comma. |
| 110 | values := strings.Split(castedValue, ",") |
| 111 | |
| 112 | // Trim the spaces from each value and transform them into a slice of any needed for the predicate. |
| 113 | inputValue := make([]any, 0, len(values)) |
| 114 | for _, value := range values { |
| 115 | inputValue = append(inputValue, strings.TrimSpace(value)) |
| 116 | } |
| 117 | |
| 118 | // Create the predicate. |
| 119 | return sqljson.ValueIn(jsonFilter.Column, inputValue, dotPath), nil |
| 120 | default: |
| 121 | return nil, fmt.Errorf("unsupported operator: %s", jsonFilter.Operator) |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | // validateColumn ensures the JSON column is a bare SQL identifier before it is |
| 126 | // emitted into the query by the ent sqljson helpers. Callers are expected to set |