From 5243a5c1001b0e856c89499eb89e69be5a8fc236 Mon Sep 17 00:00:00 2001 From: Erik Dubbelboer Date: Mon, 30 Jun 2025 06:46:51 +0200 Subject: [PATCH] Improve numeric scalar support Make sure to include ::numeric when comparing a jsonb field to a numeric scalar. This prevents us from getting: ERROR: operator does not exist: text = numeric --- filter/converter.go | 16 +++++++++++++++- filter/converter_test.go | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/filter/converter.go b/filter/converter.go index c3ef104..ed89c33 100644 --- a/filter/converter.go +++ b/filter/converter.go @@ -294,6 +294,15 @@ func (c *Converter) convertFilter(filter map[string]any, paramIndex int) (string return "", nil, fmt.Errorf("invalid comparison value (must be a primitive): %v", value) } + // If we aren't comparing columns, and the field is a numeric scalar, we also see = ($eq) and != ($ne) as numeric operators. + // This way we can use ::numeric on jsonb values to prevent getting postgres errors like: + // ERROR: operator does not exist: text = numeric + if isNumeric(value) && !isNumericOperator { + if op == "=" || op == "!=" { + isNumericOperator = true + } + } + if isNumericOperator && isNumeric(value) && c.isNestedColumn(key) { inner = append(inner, fmt.Sprintf("((%s)::numeric %s $%d)", c.columnName(key), op, paramIndex)) } else { @@ -330,7 +339,12 @@ func (c *Converter) convertFilter(filter map[string]any, paramIndex int) (string if !isScalar(value) { return "", nil, fmt.Errorf("invalid comparison value (must be a primitive): %v", value) } - conditions = append(conditions, fmt.Sprintf("(%s = $%d)", c.columnName(key), paramIndex)) + if isNumeric(value) && c.isNestedColumn(key) { + // If the value is numeric and the column is a nested JSONB column, we need to cast the column to numeric. + conditions = append(conditions, fmt.Sprintf("((%s)::numeric = $%d)", c.columnName(key), paramIndex)) + } else { + conditions = append(conditions, fmt.Sprintf("(%s = $%d)", c.columnName(key), paramIndex)) + } paramIndex++ values = append(values, value) } diff --git a/filter/converter_test.go b/filter/converter_test.go index 59e9f48..9916744 100644 --- a/filter/converter_test.go +++ b/filter/converter_test.go @@ -425,6 +425,22 @@ func TestConverter_Convert(t *testing.T) { nil, fmt.Errorf("invalid value for $eq operator (must be object with $field key only): map[foo:bar]"), }, + { + "numeric comparison with nested jsonb", + []filter.Option{filter.WithNestedJSONB("meta")}, + `{"foo": 12}`, + `(("meta"->>'foo')::numeric = $1)`, + []any{float64(12)}, + nil, + }, + { + "numeric comparison with nested jsonb with $eq", + []filter.Option{filter.WithNestedJSONB("meta")}, + `{"foo": { "$eq": 12 }}`, + `(("meta"->>'foo')::numeric = $1)`, + []any{float64(12)}, + nil, + }, } for _, tt := range tests {