mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-02-12 03:26:08 +00:00
feat(resolvespec): add OR logic support in filters
* Introduce `logic_operator` field to combine filters with OR logic.
* Implement grouping for consecutive OR filters to ensure proper SQL precedence.
* Add support for custom SQL operators in filter conditions.
* Enhance `fetch_row_number` functionality to return specific record with its position.
* Update tests to cover new filter logic and grouping behavior.
Features Implemented:
1. OR Logic Filter Support (SearchOr)
- Added to resolvespec, restheadspec, and websocketspec
- Consecutive OR filters are automatically grouped with parentheses
- Prevents SQL logic errors: (A OR B OR C) AND D instead of A OR B OR C AND D
2. CustomOperators
- Allows arbitrary SQL conditions in resolvespec
- Properly integrated with filter logic
3. FetchRowNumber
- Uses SQL window functions: ROW_NUMBER() OVER (ORDER BY ...)
- Returns only the specific record (not all records)
- Available in resolvespec and restheadspec
- Perfect for "What's my rank?" queries
4. RowNumber Field Auto-Population
- Now available in all three packages: resolvespec, restheadspec, and websocketspec
- Uses simple offset-based math: offset + index + 1
- Automatically populates RowNumber int64 field if it exists on models
- Perfect for displaying paginated lists with sequential numbering
This commit is contained in:
@@ -2602,21 +2602,8 @@ func (h *Handler) FetchRowNumber(ctx context.Context, tableName string, pkName s
|
||||
sortSQL = fmt.Sprintf("%s.%s ASC", tableName, pkName)
|
||||
}
|
||||
|
||||
// Build WHERE clauses from filters
|
||||
whereClauses := make([]string, 0)
|
||||
for i := range options.Filters {
|
||||
filter := &options.Filters[i]
|
||||
whereClause := h.buildFilterSQL(filter, tableName)
|
||||
if whereClause != "" {
|
||||
whereClauses = append(whereClauses, fmt.Sprintf("(%s)", whereClause))
|
||||
}
|
||||
}
|
||||
|
||||
// Combine WHERE clauses
|
||||
whereSQL := ""
|
||||
if len(whereClauses) > 0 {
|
||||
whereSQL = "WHERE " + strings.Join(whereClauses, " AND ")
|
||||
}
|
||||
// Build WHERE clause from filters with proper OR grouping
|
||||
whereSQL := h.buildWhereClauseWithORGrouping(options.Filters, tableName)
|
||||
|
||||
// Add custom SQL WHERE if provided
|
||||
if options.CustomSQLWhere != "" {
|
||||
@@ -2677,6 +2664,67 @@ func (h *Handler) FetchRowNumber(ctx context.Context, tableName string, pkName s
|
||||
}
|
||||
|
||||
// buildFilterSQL converts a filter to SQL WHERE clause string
|
||||
// buildWhereClauseWithORGrouping builds a WHERE clause from filters with proper OR grouping
|
||||
// Groups consecutive OR filters together to ensure proper SQL precedence
|
||||
// Example: [A, B(OR), C(OR), D(AND)] => WHERE (A OR B OR C) AND D
|
||||
func (h *Handler) buildWhereClauseWithORGrouping(filters []common.FilterOption, tableName string) string {
|
||||
if len(filters) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var groups []string
|
||||
i := 0
|
||||
|
||||
for i < len(filters) {
|
||||
// Check if this starts an OR group (next filter has OR logic)
|
||||
startORGroup := i+1 < len(filters) && strings.EqualFold(filters[i+1].LogicOperator, "OR")
|
||||
|
||||
if startORGroup {
|
||||
// Collect all consecutive filters that are OR'd together
|
||||
orGroup := []string{}
|
||||
|
||||
// Add current filter
|
||||
filterSQL := h.buildFilterSQL(&filters[i], tableName)
|
||||
if filterSQL != "" {
|
||||
orGroup = append(orGroup, filterSQL)
|
||||
}
|
||||
|
||||
// Collect remaining OR filters
|
||||
j := i + 1
|
||||
for j < len(filters) && strings.EqualFold(filters[j].LogicOperator, "OR") {
|
||||
filterSQL := h.buildFilterSQL(&filters[j], tableName)
|
||||
if filterSQL != "" {
|
||||
orGroup = append(orGroup, filterSQL)
|
||||
}
|
||||
j++
|
||||
}
|
||||
|
||||
// Group OR filters with parentheses
|
||||
if len(orGroup) > 0 {
|
||||
if len(orGroup) == 1 {
|
||||
groups = append(groups, orGroup[0])
|
||||
} else {
|
||||
groups = append(groups, "("+strings.Join(orGroup, " OR ")+")")
|
||||
}
|
||||
}
|
||||
i = j
|
||||
} else {
|
||||
// Single filter with AND logic (or first filter)
|
||||
filterSQL := h.buildFilterSQL(&filters[i], tableName)
|
||||
if filterSQL != "" {
|
||||
groups = append(groups, filterSQL)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
if len(groups) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return "WHERE " + strings.Join(groups, " AND ")
|
||||
}
|
||||
|
||||
func (h *Handler) buildFilterSQL(filter *common.FilterOption, tableName string) string {
|
||||
qualifiedColumn := h.qualifyColumnName(filter.Column, tableName)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user