filter

package
v1.2.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 16, 2025 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package filter provides utilities for filtering, sorting, and paginating data sets.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ApplyPresetConditions added in v1.0.3

func ApplyPresetConditions(db *gorm.DB, conditions any) *gorm.DB

ApplyPresetConditions applies struct fields as WHERE conditions to the db query. This is a helper to easily apply preset filters from a struct.

Example usage:

type AccountTag struct {
    OrganizationID uint
    BranchID       uint
}

tag := &AccountTag{OrganizationID: 1, BranchID: 2}
db = filter.ApplyPresetConditions(db, tag)
result, err := handler.DataGorm(db, filterRoot, pageIndex, pageSize)

func Sanitize

func Sanitize(input string) string

Types

type DataType

type DataType string

DataType defines the data type being filtered

const (
	DataTypeNumber DataType = "number" // Numeric values
	DataTypeText   DataType = "text"   // Text/string values
	DataTypeBool   DataType = "bool"   // Boolean values
	DataTypeDate   DataType = "date"   // Date values
	DataTypeTime   DataType = "time"   // Time values
)

data type constants define the type of data being filtered

type FieldFilter

type FieldFilter struct {
	Field    string   `json:"field"`    // Field name to filter on
	Value    any      `json:"value"`    // Value to compare against
	Mode     Mode     `json:"mode"`     // Comparison mode
	DataType DataType `json:"dataType"` // Data type of the field
}

represents a single filter condition

type GolangFilteringConfig added in v1.1.0

type GolangFilteringConfig struct {
	MaxDepth *int
}

type Handler

type Handler[T any] struct {
	// contains filtered or unexported fields
}

Handler is the main struct that handles filtering operations for a specific data type T.

func NewFilter

func NewFilter[T any](config GolangFilteringConfig) *Handler[T]

NewFilter creates a new filter handler that automatically generates getters using reflection WARNING: Higher MaxDepth values (>3) can cause significant memory usage and performance issues with deeply nested or circular struct references. Default is 1 (no nested fields). Getters are cached globally to prevent regeneration for the same type.

func (*Handler[T]) DataGorm

func (f *Handler[T]) DataGorm(
	db *gorm.DB,
	filterRoot Root,
	pageIndex int,
	pageSize int,
) (*PaginationResult[T], error)

DataGorm performs database-level filtering using GORM queries. It generates SQL WHERE clauses based on the filter configuration and returns paginated results. The db parameter can have existing WHERE conditions (e.g., organization_id, branch_id), and DataGorm will apply additional filters from filterRoot on top of those.

Example with preset conditions using struct:

type AccountTag struct {
    OrganizationID uint
    BranchID       uint
}
tag := &AccountTag{OrganizationID: user.OrganizationID, BranchID: *user.BranchID}
db = filter.ApplyPresetConditions(db, tag)
result, err := handler.DataGorm(db, filterRoot, pageIndex, pageSize)

Example with preset conditions using Where:

presetDB := db.Where("organization_id = ? AND branch_id = ?", orgID, branchID)
result, err := handler.DataGorm(presetDB, filterRoot, pageIndex, pageSize)

func (*Handler[T]) DataGormNoPage added in v1.2.0

func (f *Handler[T]) DataGormNoPage(
	db *gorm.DB,
	filterRoot Root,
) ([]*T, error)

DataGormNoPage performs database-level filtering using GORM queries without pagination. It generates SQL WHERE clauses based on the filter configuration and returns all matching results as a simple array. The db parameter can have existing WHERE conditions (e.g., organization_id, branch_id), and DataGormNoPage will apply additional filters from filterRoot on top of those.

Example with preset conditions using struct:

type AccountTag struct {
    OrganizationID uint
    BranchID       uint
}
tag := &AccountTag{OrganizationID: user.OrganizationID, BranchID: *user.BranchID}
db = filter.ApplyPresetConditions(db, tag)
results, err := handler.DataGormNoPage(db, filterRoot)

Example with preset conditions using Where:

presetDB := db.Where("organization_id = ? AND branch_id = ?", orgID, branchID)
results, err := handler.DataGormNoPage(presetDB, filterRoot)

func (*Handler[T]) DataGormNoPageWithPreset added in v1.2.0

func (f *Handler[T]) DataGormNoPageWithPreset(
	db *gorm.DB,
	presetConditions any,
	filterRoot Root,
) ([]*T, error)

DataGormNoPageWithPreset is a convenience method that combines ApplyPresetConditions and DataGormNoPage. It accepts preset conditions as a struct and applies them before filtering, returning results without pagination.

Example usage:

type AccountTag struct {
    OrganizationID uint `gorm:"column:organization_id"`
    BranchID       uint `gorm:"column:branch_id"`
}

tag := &AccountTag{
    OrganizationID: user.OrganizationID,
    BranchID:       *user.BranchID,
}
results, err := handler.DataGormNoPageWithPreset(db, tag, filterRoot)

func (*Handler[T]) DataGormWithPreset added in v1.0.3

func (f *Handler[T]) DataGormWithPreset(
	db *gorm.DB,
	presetConditions any,
	filterRoot Root,
	pageIndex int,
	pageSize int,
) (*PaginationResult[T], error)

DataGormWithPreset is a convenience method that combines ApplyPresetConditions and DataGorm. It accepts preset conditions as a struct and applies them before filtering.

Example usage:

type AccountTag struct {
    OrganizationID uint `gorm:"column:organization_id"`
    BranchID       uint `gorm:"column:branch_id"`
}

tag := &AccountTag{
    OrganizationID: user.OrganizationID,
    BranchID:       *user.BranchID,
}
result, err := handler.DataGormWithPreset(db, tag, filterRoot, pageIndex, pageSize)

func (*Handler[T]) DataHybridNoPage added in v1.2.0

func (f *Handler[T]) DataHybridNoPage(
	db *gorm.DB,
	threshold int,
	filterRoot Root,
) ([]*T, error)

DataHybridNoPage intelligently chooses between in-memory (DataQueryNoPage) and database (DataGormNoPage) filtering based on estimated table size, returning results without pagination.

IMPORTANT: Respects pre-existing WHERE conditions on the db parameter. - If DataQueryNoPage is chosen (small dataset): fetches data using existing conditions, then filters in-memory - If DataGormNoPage is chosen (large dataset): combines existing conditions with filterRoot filters in SQL

Example with pre-existing conditions:

db := gormDB.Where("organization_id = ? AND branch_id = ?", orgID, branchID)
results, err := handler.DataHybridNoPage(db, 10000, filterRoot)
// DataQueryNoPage path: SELECT * FROM table WHERE organization_id = ? AND branch_id = ? (fetch all, filter in-memory)
// DataGormNoPage path: SELECT * FROM table WHERE organization_id = ? AND branch_id = ? AND [filterRoot conditions]

func (*Handler[T]) DataQuery

func (f *Handler[T]) DataQuery(
	data []*T,
	filterRoot Root,
	pageIndex int,
	pageSize int,
) (*PaginationResult[T], error)

DataQuery performs in-memory filtering with parallel processing. It filters the provided data slice based on the filter configuration and returns paginated results.

func (*Handler[T]) DataQueryNoPage added in v1.2.0

func (f *Handler[T]) DataQueryNoPage(
	data []*T,
	filterRoot Root,
) ([]*T, error)

DataQueryNoPage performs in-memory filtering with parallel processing without pagination. It filters the provided data slice based on the filter configuration and returns all matching results as a simple array.

func (*Handler[T]) DataQueryNoPageCSV added in v1.2.1

func (f *Handler[T]) DataQueryNoPageCSV(
	data []*T,
	filterRoot Root,
) ([]byte, error)

DataQueryNoPageCSV performs in-memory filtering with parallel processing and returns results as CSV bytes. It filters the provided data slice based on the filter configuration and exports all matching results as CSV format. Field names are automatically used as CSV headers.

func (*Handler[T]) DataQueryNoPageCSVCustom added in v1.2.2

func (f *Handler[T]) DataQueryNoPageCSVCustom(
	data []*T,
	filterRoot Root,
	customGetter func(*T) map[string]any,
) ([]byte, error)

DataQueryNoPageCSVCustom performs in-memory filtering with parallel processing and returns results as CSV bytes. It uses a custom callback function to allow users to define exactly what fields and values to include in the CSV output. This provides full control over CSV structure and field mapping on the user side.

Parameters:

  • data: slice of pointers to the data type T to filter
  • filterRoot: filter configuration defining conditions, logic, and sorting
  • customGetter: callback function that takes a data item and returns a map[string]any where keys are column headers and values are the corresponding data

Returns CSV bytes with headers from the customGetter map keys, sorted alphabetically for deterministic ordering.

Example usage:

csvData, err := handler.DataQueryNoPageCSVCustom(users, filterRoot, func(user *User) map[string]any {
    return map[string]any{
        "Full Name": user.FirstName + " " + user.LastName,
        "Email": user.Email,
        "Status": user.IsActive,
        "Department": user.Department.Name, // Access nested fields
    }
})

func (*Handler[T]) ExportGetters added in v1.2.7

func (h *Handler[T]) ExportGetters() map[string]func(*T) any

ExportGetters returns the getters map for inspection/debugging

func (*Handler[T]) GormNoPaginationCSV added in v1.2.1

func (f *Handler[T]) GormNoPaginationCSV(
	db *gorm.DB,
	filterRoot Root,
) ([]byte, error)

GormNoPaginationCSV performs database-level filtering using GORM queries and returns results as CSV bytes. It generates SQL WHERE clauses based on the filter configuration and exports all matching results as CSV format. Field names are automatically used as CSV headers. The db parameter can have existing WHERE conditions (e.g., organization_id, branch_id), and GormNoPaginationCSV will apply additional filters from filterRoot on top of those.

Example with preset conditions using struct:

type AccountTag struct {
    OrganizationID uint
    BranchID       uint
}
tag := &AccountTag{OrganizationID: user.OrganizationID, BranchID: *user.BranchID}
db = filter.ApplyPresetConditions(db, tag)
csvData, err := handler.GormNoPaginationCSV(db, filterRoot)

Example with preset conditions using Where:

presetDB := db.Where("organization_id = ? AND branch_id = ?", orgID, branchID)
csvData, err := handler.GormNoPaginationCSV(presetDB, filterRoot)

func (*Handler[T]) GormNoPaginationCSVCustom added in v1.2.2

func (f *Handler[T]) GormNoPaginationCSVCustom(
	db *gorm.DB,
	filterRoot Root,
	customGetter func(*T) map[string]any,
) ([]byte, error)

GormNoPaginationCSVCustom performs database-level filtering and returns results as CSV bytes. It uses a custom callback function to allow users to define exactly what fields and values to include in the CSV output. This provides full control over CSV structure and field mapping on the user side.

Parameters:

  • db: GORM database instance with any preset conditions
  • filterRoot: filter configuration defining conditions, logic, and sorting
  • customGetter: callback function that takes a data item and returns a map[string]any where keys are column headers and values are the corresponding data

Returns CSV bytes with headers from the customGetter map keys, sorted alphabetically for deterministic ordering.

Example usage:

csvData, err := handler.GormNoPaginationCSVCustom(db, filterRoot, func(user *User) map[string]any {
    return map[string]any{
        "Full Name": user.FirstName + " " + user.LastName,
        "Email": user.Email,
        "Status": user.IsActive,
        "Department": user.Department.Name, // Access nested fields if preloaded
    }
})

func (*Handler[T]) GormNoPaginationCSVCustomWithPreset added in v1.2.2

func (f *Handler[T]) GormNoPaginationCSVCustomWithPreset(
	db *gorm.DB,
	presetConditions any,
	filterRoot Root,
	customGetter func(*T) map[string]any,
) ([]byte, error)

GormNoPaginationCSVCustomWithPreset is a convenience method that combines preset conditions with GormNoPaginationCSVCustom. It applies preset conditions to the database query before filtering and CSV export using a custom getter function.

Parameters:

  • db: GORM database instance
  • presetConditions: struct or map with preset WHERE conditions to apply before filtering
  • filterRoot: filter configuration defining conditions, logic, and sorting
  • customGetter: callback function that defines custom CSV field mapping

Example usage:

type UserFilter struct {
    OrganizationID uint
    BranchID       uint
}

presetConditions := &UserFilter{
    OrganizationID: user.OrganizationID,
    BranchID:       *user.BranchID,
}

csvData, err := handler.GormNoPaginationCSVCustomWithPreset(db, presetConditions, filterRoot, func(user *User) map[string]any {
    return map[string]any{
        "Employee ID": user.ID,
        "Full Name":   fmt.Sprintf("%s %s", user.FirstName, user.LastName),
        "Department":  user.Department.Name,
    }
})

func (*Handler[T]) GormNoPaginationCSVWithPreset added in v1.2.1

func (f *Handler[T]) GormNoPaginationCSVWithPreset(
	db *gorm.DB,
	presetConditions any,
	filterRoot Root,
) ([]byte, error)

GormNoPaginationCSVWithPreset is a convenience method that combines ApplyPresetConditions and GormNoPaginationCSV. It accepts preset conditions as a struct and applies them before filtering, returning results as CSV without pagination.

Example usage:

type AccountTag struct {
    OrganizationID uint `gorm:"column:organization_id"`
    BranchID       uint `gorm:"column:branch_id"`
}

tag := &AccountTag{
    OrganizationID: user.OrganizationID,
    BranchID:       *user.BranchID,
}
csvData, err := handler.GormNoPaginationCSVWithPreset(db, tag, filterRoot)

func (*Handler[T]) Hybrid

func (f *Handler[T]) Hybrid(
	db *gorm.DB,
	threshold int,
	filterRoot Root,
	pageIndex int,
	pageSize int,
) (*PaginationResult[T], error)

Hybrid intelligently chooses between in-memory (DataQuery) and database (DataGorm) filtering based on estimated table size.

IMPORTANT: Respects pre-existing WHERE conditions on the db parameter. - If DataQuery is chosen (small dataset): fetches data using existing conditions, then filters in-memory - If DataGorm is chosen (large dataset): combines existing conditions with filterRoot filters in SQL

Example with pre-existing conditions:

db := gormDB.Where("organization_id = ? AND branch_id = ?", orgID, branchID)
result, err := handler.Hybrid(db, 10000, filterRoot, pageIndex, pageSize)
// DataQuery path: SELECT * FROM table WHERE organization_id = ? AND branch_id = ? (fetch all, filter in-memory)
// DataGorm path: SELECT * FROM table WHERE organization_id = ? AND branch_id = ? AND [filterRoot conditions]

func (*Handler[T]) HybridCSV added in v1.2.1

func (f *Handler[T]) HybridCSV(
	db *gorm.DB,
	threshold int,
	filterRoot Root,
) ([]byte, error)

HybridCSV intelligently chooses between in-memory (DataQueryNoPageCSV) and database (GormNoPaginationCSV) filtering based on estimated table size, returning results as CSV bytes.

IMPORTANT: Respects pre-existing WHERE conditions on the db parameter. - If DataQueryNoPageCSV is chosen (small dataset): fetches data using existing conditions, then filters in-memory and exports to CSV - If GormNoPaginationCSV is chosen (large dataset): combines existing conditions with filterRoot filters in SQL and exports to CSV

Example with pre-existing conditions:

db := gormDB.Where("organization_id = ? AND branch_id = ?", orgID, branchID)
csvData, err := handler.HybridCSV(db, 10000, filterRoot)
// DataQueryNoPageCSV path: SELECT * FROM table WHERE organization_id = ? AND branch_id = ? (fetch all, filter in-memory, export CSV)
// GormNoPaginationCSV path: SELECT * FROM table WHERE organization_id = ? AND branch_id = ? AND [filterRoot conditions] (export CSV)

func (*Handler[T]) HybridCSVCustom added in v1.2.2

func (f *Handler[T]) HybridCSVCustom(
	db *gorm.DB,
	threshold int,
	filterRoot Root,
	customGetter func(*T) map[string]any,
) ([]byte, error)

HybridCSVCustom intelligently chooses between in-memory (DataQueryNoPageCSVCustom) and database (GormNoPaginationCSVCustom) approaches for CSV export based on estimated table size, using a custom callback function for field mapping. For small tables (below threshold), it uses in-memory filtering with full dataset retrieval. For large tables (above threshold), it uses database-level filtering to minimize memory usage.

Parameters:

  • db: GORM database instance with any preset conditions
  • threshold: row count threshold for switching between in-memory and database strategies
  • filterRoot: filter configuration defining conditions, logic, and sorting
  • customGetter: callback function that defines custom CSV field mapping

Strategy Selection:

  • If estimated table rows <= threshold: DataQueryNoPageCSVCustom (in-memory processing)
  • If estimated table rows > threshold: GormNoPaginationCSVCustom (database processing)
  • If estimation fails: Falls back to GormNoPaginationCSVCustom (database processing)

Example usage:

csvData, err := handler.HybridCSVCustom(db, 10000, filterRoot, func(user *User) map[string]any {
    return map[string]any{
        "Employee Name": fmt.Sprintf("%s %s", user.FirstName, user.LastName),
        "Contact Email": user.Email,
        "Department": user.Department.Name,
        "Join Date": user.CreatedAt.Format("2006-01-02"),
    }
})

func (*Handler[T]) HybridCSVCustomWithPreset added in v1.2.2

func (f *Handler[T]) HybridCSVCustomWithPreset(
	db *gorm.DB,
	presetConditions any,
	threshold int,
	filterRoot Root,
	customGetter func(*T) map[string]any,
) ([]byte, error)

HybridCSVCustomWithPreset is a convenience method that combines preset conditions with HybridCSVCustom. It applies preset conditions to the database query before intelligent strategy selection and CSV export.

Parameters:

  • db: GORM database instance
  • presetConditions: struct or map with preset WHERE conditions to apply before filtering
  • threshold: row count threshold for strategy selection
  • filterRoot: filter configuration defining conditions, logic, and sorting
  • customGetter: callback function that defines custom CSV field mapping

Example usage:

type OrganizationFilter struct {
    OrganizationID uint
}

presetConditions := &OrganizationFilter{OrganizationID: user.OrganizationID}

csvData, err := handler.HybridCSVCustomWithPreset(db, presetConditions, 10000, filterRoot, func(user *User) map[string]any {
    return map[string]any{
        "ID": user.ID,
        "Name": user.Name,
        "Email": user.Email,
    }
})

func (*Handler[T]) HybridCSVWithPreset added in v1.2.1

func (f *Handler[T]) HybridCSVWithPreset(
	db *gorm.DB,
	presetConditions any,
	threshold int,
	filterRoot Root,
) ([]byte, error)

HybridCSVWithPreset is a convenience method that combines preset conditions with HybridCSV. It accepts preset conditions as a struct and applies them before filtering, returning CSV results using hybrid strategy.

Example usage:

type AccountTag struct {
    OrganizationID uint `gorm:"column:organization_id"`
    BranchID       uint `gorm:"column:branch_id"`
}

tag := &AccountTag{
    OrganizationID: user.OrganizationID,
    BranchID:       *user.BranchID,
}
csvData, err := handler.HybridCSVWithPreset(db, tag, 10000, filterRoot)

type Logic

type Logic string

Logic defines how multiple filters are combined

const (
	LogicAnd Logic = "and" // All filters must match
	LogicOr  Logic = "or"  // Any filter can match
)

logic constants define how to combine multiple filters

type Mode

type Mode string

Mode defines the type of comparison operation to perform

const (
	ModeEqual       Mode = "equal"       // Exact match
	ModeNotEqual    Mode = "notEqual"    // Not equal
	ModeContains    Mode = "contains"    // Contains substring
	ModeNotContains Mode = "notContains" // Does not contain substring
	ModeStartsWith  Mode = "startsWith"  // Starts with prefix
	ModeEndsWith    Mode = "endsWith"    // Ends with suffix
	ModeIsEmpty     Mode = "isEmpty"     // Is empty or null
	ModeIsNotEmpty  Mode = "isNotEmpty"  // Is not empty
	ModeGT          Mode = "gt"          // Greater than
	ModeGTE         Mode = "gte"         // Greater than or equal
	ModeLT          Mode = "lt"          // Less than
	ModeLTE         Mode = "lte"         // Less than or equal
	ModeRange       Mode = "range"       // Between two values
	ModeBefore      Mode = "before"      // Before (date/time)
	ModeAfter       Mode = "after"       // After (date/time)
)

mode constants define available comparison operations

type PaginationResult

type PaginationResult[T any] struct {
	Data      []*T `json:"data"`      // Current page data
	TotalSize int  `json:"totalSize"` // Total matching records
	TotalPage int  `json:"totalPage"` // Total number of pages
	PageIndex int  `json:"pageIndex"` // Current page index (0-based)
	PageSize  int  `json:"pageSize"`  // Records per page
}

PaginationResult contains filtered and paginated results

type Range

type Range struct {
	From any `json:"from"` // Start of range
	To   any `json:"to"`   // End of range
}

Range represents a range of values for filtering

type RangeDate

type RangeDate struct {
	From time.Time // Start date
	To   time.Time // End date
}

RangeDate represents a date range

type RangeNumber

type RangeNumber struct {
	From float64 // Start of numeric range
	To   float64 // End of numeric range
}

RangeNumber represents a numeric range

type Root

type Root struct {
	FieldFilters []FieldFilter `json:"filters"`    // List of filter conditions
	SortFields   []SortField   `json:"sortFields"` // List of sort fields
	Logic        Logic         `json:"logic"`      // How to combine filters (AND/OR)
	Preload      []string      `json:"preload"`    // List of related entities to preload (only applicable for GORM)
}

Root represents the root filter configuration

type SortField

type SortField struct {
	Field string    `json:"field"` // Field name to sort by
	Order SortOrder `json:"order"` // Sort direction
}

SortField represents a field to sort by

type SortOrder

type SortOrder string

SortOrder defines the sort direction

const (
	SortOrderAsc  SortOrder = "asc"  // Ascending order
	SortOrderDesc SortOrder = "desc" // Descending order
)

Sort order constants define ascending or descending order

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL