123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- package qs
- import (
- "errors"
- "net/http"
- "net/url"
- "regexp"
- "strconv"
- )
- // Query error.
- var (
- ErrInvalidFilter = errors.New("invalid filter")
- ErrTooManyFilters = errors.New("too many filters")
- )
- var filterRegexp = regexp.MustCompile("^([A-z0-9]+) (eq|neq|gt|gte|lt|lte|in|not in|like|not like) (.+)$")
- // Filter represents a filter as used in, most likely, a database query.
- type Filter struct {
- Field string `json:"field"` // Field to filter on.
- Operator string `json:"operator"` // Filter operator, e.g. eq, gt...
- Value string `json:"value"` // Value to filter by.
- }
- // BoolValue retrieves the filter value as a bool.
- func (filter Filter) BoolValue() (bool, error) {
- return strconv.ParseBool(filter.Value)
- }
- // Float32Value retrieves the filter value as a float32.
- func (filter Filter) Float32Value() (float32, error) {
- value, err := strconv.ParseFloat(filter.Value, 32)
- return float32(value), err
- }
- // Float64Value retrieves the filter value as a float64.
- func (filter Filter) Float64Value() (float64, error) {
- return strconv.ParseFloat(filter.Value, 64)
- }
- // IntValue retrieves the filter value as an int.
- func (filter Filter) IntValue() (int, error) {
- return strconv.Atoi(filter.Value)
- }
- // Filters is a slice of Filter structs.
- type Filters []Filter
- // Field returns a new Filters slice containing only filters for the specified field.
- // The original order of filters is preserved.
- func (filters Filters) Field(field string) Filters {
- ff := Filters{}
- for _, filter := range filters {
- if filter.Field == field {
- ff = append(ff, filter)
- }
- }
- return ff
- }
- // Fields returns a new Filters slice containing filters for any of the specified fields.
- // The original order of filters is preserved.
- func (filters Filters) Fields(fields ...string) Filters {
- ff := Filters{}
- for _, filter := range filters {
- for _, field := range fields {
- if filter.Field == field {
- ff = append(ff, filter)
- }
- }
- }
- return ff
- }
- // HasField returns true if the Filters slice includes any filters for the specified field.
- func (filters Filters) HasField(field string) bool {
- for _, filter := range filters {
- if filter.Field == field {
- return true
- }
- }
- return false
- }
- // ReadFiltersOptions configures the behaviour of ReadFilters.
- type ReadFiltersOptions struct {
- Key string // Query string key. The default value is "filter"
- MaxFilters int // If this is > 0, a maximum number of filters is imposed
- }
- // ReadFilters parses URL values into a slice of filters.
- // This function returns nil if no filters are found.
- func ReadFilters(values url.Values, opt *ReadFiltersOptions) (Filters, error) {
- opt = initFiltersOptions(opt)
- if !values.Has(opt.Key) {
- return nil, nil
- }
- if opt.MaxFilters > 0 && len(values[opt.Key]) > opt.MaxFilters {
- return nil, ErrTooManyFilters
- }
- filters := Filters{}
- for _, filterStr := range values[opt.Key] {
- match := filterRegexp.FindStringSubmatch(filterStr)
- if match == nil {
- return nil, ErrInvalidFilter
- }
- filter := Filter{
- Field: match[1],
- Operator: match[2],
- Value: match[3],
- }
- filters = append(filters, filter)
- }
- return filters, nil
- }
- // ReadRequestFilters parses a request's query string into a slice of filters.
- // This function returns nil if no filters are found.
- func ReadRequestFilters(req *http.Request, opt *ReadFiltersOptions) (Filters, error) {
- return ReadFilters(req.URL.Query(), opt)
- }
- // ReadStringFilters parses a query string literal into a slice of filters.
- // This function returns nil if no filters are found.
- func ReadStringFilters(qs string, opt *ReadFiltersOptions) (Filters, error) {
- values, err := url.ParseQuery(qs)
- if err != nil {
- return nil, err
- }
- return ReadFilters(values, opt)
- }
- func initFiltersOptions(opt *ReadFiltersOptions) *ReadFiltersOptions {
- def := &ReadFiltersOptions{
- Key: "filter",
- }
- if opt != nil {
- if len(opt.Key) > 0 {
- def.Key = opt.Key
- }
- if opt.MaxFilters > def.MaxFilters {
- def.MaxFilters = opt.MaxFilters
- }
- }
- return def
- }
|