mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-10-13 16:56:28 +00:00
feat(restheadspec): match NULL when x-fieldfilter compares a numeric column to 0
Adds a SetNullAsZero handler option: on by default in restheadspec (for x-fieldfilter only), off by default in resolvespec. When enabled, an eq filter of 0 on a numeric column renders (col = 0 OR col IS NULL).
This commit is contained in:
@@ -34,6 +34,7 @@ type Handler struct {
|
||||
openAPIGenerator func() (string, error)
|
||||
defaultSort map[string][]common.SortOption
|
||||
disallowNulls bool
|
||||
nullAsZero bool
|
||||
}
|
||||
|
||||
// NewHandler creates a new API handler with database and registry abstractions
|
||||
@@ -42,6 +43,8 @@ func NewHandler(db common.Database, registry common.ModelRegistry) *Handler {
|
||||
db: db,
|
||||
registry: registry,
|
||||
hooks: NewHookRegistry(),
|
||||
// x-fieldfilter on a numeric column with 0 also matches NULLs by default
|
||||
nullAsZero: true,
|
||||
}
|
||||
// Initialize nested processor
|
||||
handler.nestedProcessor = common.NewNestedCUDProcessor(db, registry, handler)
|
||||
@@ -68,6 +71,12 @@ func (h *Handler) SetDisallowNulls(disallow bool) {
|
||||
h.disallowNulls = disallow
|
||||
}
|
||||
|
||||
// SetNullAsZero controls whether an x-fieldfilter equality against 0 on a numeric
|
||||
// column also matches NULLs: (col = 0 OR col IS NULL). Enabled by default.
|
||||
func (h *Handler) SetNullAsZero(enabled bool) {
|
||||
h.nullAsZero = enabled
|
||||
}
|
||||
|
||||
// SetFallbackHandler sets a fallback handler to be called when no model is found
|
||||
// If not set, the handler will simply return (pass through to next route)
|
||||
func (h *Handler) SetFallbackHandler(fallback FallbackHandler) {
|
||||
@@ -2518,6 +2527,9 @@ func (h *Handler) applyFilter(query common.SelectQuery, filter common.FilterOpti
|
||||
|
||||
switch strings.ToLower(filter.Operator) {
|
||||
case "eq", "equals":
|
||||
if h.nullAsZeroApplies(&filter, model, needsCast) {
|
||||
return applyWhere(fmt.Sprintf("(%s = ? OR %s IS NULL)", rawQualifiedColumn, rawQualifiedColumn), filter.Value)
|
||||
}
|
||||
return applyWhere(fmt.Sprintf("%s = ?", qualifiedColumn), filter.Value)
|
||||
case "neq", "not_equals", "ne":
|
||||
return applyWhere(fmt.Sprintf("%s != ?", qualifiedColumn), filter.Value)
|
||||
@@ -2588,6 +2600,16 @@ func (h *Handler) applyFilter(query common.SelectQuery, filter common.FilterOpti
|
||||
}
|
||||
}
|
||||
|
||||
// nullAsZeroApplies reports whether an eq filter should also match NULLs, i.e.
|
||||
// (col = 0 OR col IS NULL). This is the case for x-fieldfilter on a numeric
|
||||
// column compared against zero, where a NULL is treated the same as 0.
|
||||
func (h *Handler) nullAsZeroApplies(filter *common.FilterOption, model interface{}, needsCast bool) bool {
|
||||
if !h.nullAsZero || !filter.NullAsZero || needsCast {
|
||||
return false
|
||||
}
|
||||
return common.IsNumericZeroFilter(model, filter.Column, filter.Value)
|
||||
}
|
||||
|
||||
// applyOrFilterGroup applies a group of OR filters as a single grouped condition
|
||||
// This ensures OR conditions are properly grouped with parentheses to prevent OR logic from escaping
|
||||
func (h *Handler) applyOrFilterGroup(query common.SelectQuery, filters []*common.FilterOption, castInfo []ColumnCastInfo, tableName string, model interface{}) common.SelectQuery {
|
||||
@@ -2627,6 +2649,11 @@ func (h *Handler) applyOrFilterGroup(query common.SelectQuery, filters []*common
|
||||
}
|
||||
|
||||
// Build the condition based on operator
|
||||
if strings.EqualFold(filter.Operator, "eq") && h.nullAsZeroApplies(filter, model, castInfo[i].NeedsCast) {
|
||||
conditions = append(conditions, fmt.Sprintf("(%s = ? OR %s IS NULL)", rawQualifiedColumn, rawQualifiedColumn))
|
||||
args = append(args, filter.Value)
|
||||
continue
|
||||
}
|
||||
condition, filterArgs := h.buildFilterCondition(qualifiedColumn, filter, tableName)
|
||||
if condition != "" {
|
||||
conditions = append(conditions, condition)
|
||||
|
||||
Reference in New Issue
Block a user