305 lines
9.1 KiB
Go
305 lines
9.1 KiB
Go
package common
|
|
|
|
import (
|
|
"fmt"
|
|
"git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/model/common/sql"
|
|
. "github.com/ahmetb/go-linq"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
FilterTypeLikeSuffix = "__like"
|
|
FilterTypeEqualSuffix = "__eq"
|
|
FilterTypeNotEqualSuffix = "__neq"
|
|
FilterTypeInSuffix = "__in"
|
|
)
|
|
|
|
const (
|
|
FilterTypeLike = 0
|
|
FilterTypeEqual = 1
|
|
FilterTypeNotEqual = 2
|
|
FilterTypeIn = 3
|
|
)
|
|
|
|
type FilterType int
|
|
|
|
// PaginationQuery defines
|
|
type PaginationQuery struct {
|
|
From int
|
|
Size int
|
|
}
|
|
|
|
// NoPagination is the option for no pigination, currently a very large page
|
|
var NoPagination = NewPaginationQuery(-1, -1)
|
|
|
|
// DefaultPagination is the default options for pagination
|
|
var DefaultPagination = NewPaginationQuery(0, 10)
|
|
|
|
// NewPaginationQuery creates a new PaginationQuery
|
|
func NewPaginationQuery(from, size int) *PaginationQuery {
|
|
return &PaginationQuery{
|
|
From: from,
|
|
Size: size,
|
|
}
|
|
}
|
|
|
|
// IsValid checks if a PaginationQuery is valid
|
|
func (pq *PaginationQuery) IsValid() bool {
|
|
if pq == nil {
|
|
return false
|
|
}
|
|
if pq.From == -1 && pq.Size == -1 {
|
|
return true
|
|
}
|
|
return pq.From >= 0 && pq.Size > 0
|
|
}
|
|
|
|
func (pq *PaginationQuery) String() string {
|
|
if pq == nil {
|
|
return ""
|
|
}
|
|
if pq.From == -1 || pq.Size == -1 {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf(" LIMIT %d OFFSET %d ", pq.Size, pq.From)
|
|
}
|
|
|
|
// SortQuery holds options for sort functionality of data select.
|
|
type SortQuery struct {
|
|
SortByList []SortBy
|
|
}
|
|
|
|
// SortBy holds the name of the property that should be sorted and whether order should be ascending or descending.
|
|
type SortBy struct {
|
|
Property string
|
|
Ascending bool
|
|
}
|
|
|
|
// NoSort is as option for no sort.
|
|
var NoSort = &SortQuery{
|
|
SortByList: []SortBy{},
|
|
}
|
|
|
|
// NewSortQuery takes raw sort options list and returns SortQuery object. For example:
|
|
// ["a", "parameter1", "d", "parameter2"] - means that the data should be sorted by
|
|
// parameter1 (ascending) and later - for results that return equal under parameter 1 sort - by parameter2 (descending)
|
|
func NewSortQuery(sortByListRaw []string) *SortQuery {
|
|
if sortByListRaw == nil || len(sortByListRaw)%2 == 1 {
|
|
return &SortQuery{SortByList: []SortBy{}}
|
|
}
|
|
sortByList := []SortBy{}
|
|
for i := 0; i+1 < len(sortByListRaw); i += 2 {
|
|
// parse order option
|
|
var ascending bool
|
|
orderOption := sortByListRaw[i]
|
|
if orderOption == "a" {
|
|
ascending = true
|
|
} else if orderOption == "d" {
|
|
ascending = false
|
|
} else {
|
|
// Invalid order option. Only ascending (a), descending (d) options are supported
|
|
return &SortQuery{SortByList: []SortBy{}}
|
|
}
|
|
|
|
// parse property name
|
|
propertyName := sortByListRaw[i+1]
|
|
sortBy := SortBy{
|
|
Property: propertyName,
|
|
Ascending: ascending,
|
|
}
|
|
// Add to the sort options.
|
|
sortByList = append(sortByList, sortBy)
|
|
}
|
|
return &SortQuery{
|
|
SortByList: sortByList,
|
|
}
|
|
}
|
|
|
|
func (sq *SortQuery) String() string {
|
|
if sq == nil {
|
|
return ""
|
|
}
|
|
if len(sq.SortByList) == 0 {
|
|
return ""
|
|
}
|
|
res := fmt.Sprintf(" ORDER BY %s ", sq.SortByList[0].Property)
|
|
if sq.SortByList[0].Ascending == false {
|
|
res += "desc "
|
|
}
|
|
return res
|
|
}
|
|
|
|
// FilterQuery holds options for filter functionality of data select.
|
|
// currently it only supports string match
|
|
type FilterQuery struct {
|
|
FilterByList []FilterBy
|
|
Extend string
|
|
}
|
|
|
|
// FilterBy holds the name of the property that should be .
|
|
type FilterBy struct {
|
|
Property string
|
|
Type FilterType
|
|
Value string
|
|
values []string
|
|
}
|
|
|
|
// NoFilter is as option for no filter.
|
|
var NoFilter = &FilterQuery{
|
|
FilterByList: []FilterBy{},
|
|
}
|
|
|
|
// NewFilterQuery takes raw filter options list and returns FilterQuery object. For example:
|
|
// ["user_name", "test"] - means that the data should be filtered by
|
|
// user_name (%test%), means user_name contains 'test'
|
|
func NewFilterQuery(filterByListRaw []string) *FilterQuery {
|
|
if filterByListRaw == nil || len(filterByListRaw)%2 == 1 {
|
|
return &FilterQuery{FilterByList: []FilterBy{}}
|
|
}
|
|
filterByList := parseFilter(filterByListRaw)
|
|
return &FilterQuery{
|
|
FilterByList: filterByList,
|
|
}
|
|
}
|
|
|
|
func (fq *FilterQuery) Add(filterByListRaw []string) {
|
|
filterByList := parseFilter(filterByListRaw)
|
|
fq.FilterByList = append(fq.FilterByList, filterByList...)
|
|
}
|
|
func parseFilter(filterByListRaw []string) []FilterBy {
|
|
filterByList := []FilterBy{}
|
|
for i := 0; i+1 < len(filterByListRaw); i += 2 {
|
|
// parse property name and value
|
|
propertyName := filterByListRaw[i]
|
|
propertyValue := filterByListRaw[i+1]
|
|
propertyValueArray := strings.Split(propertyValue, " ")
|
|
filterBy := FilterBy{
|
|
Property: propertyName,
|
|
Type: FilterTypeLike,
|
|
Value: strings.Join(propertyValueArray, ","),
|
|
}
|
|
if strings.HasSuffix(propertyName, FilterTypeEqualSuffix) {
|
|
filterBy.Type = FilterTypeEqual
|
|
filterBy.Property = strings.TrimSuffix(filterBy.Property, FilterTypeEqualSuffix)
|
|
} else if strings.HasSuffix(propertyName, FilterTypeNotEqualSuffix) {
|
|
filterBy.Type = FilterTypeNotEqual
|
|
filterBy.Property = strings.TrimSuffix(filterBy.Property, FilterTypeNotEqualSuffix)
|
|
} else if strings.HasSuffix(propertyName, FilterTypeInSuffix) {
|
|
filterBy.Type = FilterTypeIn
|
|
filterBy.Property = strings.TrimSuffix(filterBy.Property, FilterTypeInSuffix)
|
|
} else {
|
|
filterBy.Property = strings.TrimSuffix(filterBy.Property, FilterTypeLikeSuffix)
|
|
}
|
|
|
|
// Add to the sort options.
|
|
filterByList = append(filterByList, filterBy)
|
|
}
|
|
|
|
return filterByList
|
|
}
|
|
func (fq *FilterQuery) String() string {
|
|
if fq == nil || len(fq.FilterByList) == 0 {
|
|
return fmt.Sprintf(" '1' = '1' %s ", fq.Extend)
|
|
}
|
|
|
|
type index struct {
|
|
property string
|
|
operator FilterType
|
|
}
|
|
|
|
indexed := make(map[index][]*FilterBy)
|
|
From(fq.FilterByList).
|
|
GroupByT(
|
|
func(fb FilterBy) index { return index{property: fb.Property, operator: fb.Type} },
|
|
func(fb FilterBy) *FilterBy { return &fb }).
|
|
ToMapByT(&indexed,
|
|
func(g Group) index { return g.Key.(index) },
|
|
func(g Group) []*FilterBy {
|
|
var fbs []*FilterBy
|
|
From(g.Group).ToSlice(&fbs)
|
|
return fbs
|
|
})
|
|
var list []*FilterBy
|
|
From(indexed).SelectT(func(kv KeyValue) *FilterBy {
|
|
k := kv.Key.(index)
|
|
v := kv.Value.([]*FilterBy)
|
|
var values []string
|
|
From(v).SelectT(func(fb *FilterBy) string { return fb.Value }).ToSlice(&values)
|
|
return &FilterBy{
|
|
Property: k.property,
|
|
Type: k.operator,
|
|
values: values,
|
|
}
|
|
}).ToSlice(&list)
|
|
|
|
var items []string
|
|
for _, filterBy := range list {
|
|
var constraints []string
|
|
if filterBy.Type == FilterTypeEqual {
|
|
From(filterBy.values).SelectT(func(v string) string {
|
|
return fmt.Sprintf(" %s='%s' ", filterBy.Property, sql.EscapeStringBackslash(v))
|
|
}).ToSlice(&constraints)
|
|
items = append(items, fmt.Sprintf(" (%s) ", strings.Join(constraints, " or ")))
|
|
} else if filterBy.Type == FilterTypeNotEqual {
|
|
From(filterBy.values).SelectT(func(v string) string {
|
|
return fmt.Sprintf(" %s!='%s' ", filterBy.Property, sql.EscapeStringBackslash(v))
|
|
}).ToSlice(&constraints)
|
|
items = append(items, strings.Join(constraints, " and "))
|
|
} else if filterBy.Type == FilterTypeIn {
|
|
From(filterBy.values).SelectT(func(v string) string {
|
|
return fmt.Sprintf("%s", strings.Replace(sql.EscapeStringBackslash(v), ",", "','", -1))
|
|
}).ToSlice(&constraints)
|
|
items = append(items, fmt.Sprintf(` (%s in ('%s')) `, filterBy.Property, strings.Join(constraints, ",")))
|
|
} else {
|
|
From(filterBy.values).SelectT(func(v string) string {
|
|
if v == "%" {
|
|
v = "\\%"
|
|
}
|
|
return fmt.Sprintf(" %s like '%%%s%%' ", filterBy.Property, sql.EscapeUnderlineInLikeStatement(v))
|
|
}).ToSlice(&constraints)
|
|
items = append(items, fmt.Sprintf(" (%s) ", strings.Join(constraints, " or ")))
|
|
}
|
|
}
|
|
|
|
return fmt.Sprintf("%s %s", strings.Join(items, "and"), fq.Extend)
|
|
}
|
|
|
|
// DataSelectQuery currently included only Pagination and Sort options.
|
|
type DataSelectQuery struct {
|
|
PaginationQuery *PaginationQuery
|
|
SortQuery *SortQuery
|
|
FilterQuery *FilterQuery
|
|
}
|
|
|
|
// NoDataSelect fetches all items with no sort.
|
|
var NoDataSelect = NewDataSelectQuery(NoPagination, NoSort, NoFilter)
|
|
|
|
// DefaultDataSelect fetches first 10 items from page 1 with no sort.
|
|
var DefaultDataSelect = NewDataSelectQuery(DefaultPagination, NoSort, NoFilter)
|
|
|
|
// NewDataSelectQuery creates DataSelectQuery object from simpler data select queries.
|
|
func NewDataSelectQuery(paginationQuery *PaginationQuery, sortQuery *SortQuery, filterQuery *FilterQuery) *DataSelectQuery {
|
|
dataselect := &DataSelectQuery{
|
|
PaginationQuery: &PaginationQuery{From: 0, Size: 10},
|
|
SortQuery: &SortQuery{SortByList: []SortBy{}},
|
|
FilterQuery: &FilterQuery{FilterByList: []FilterBy{}},
|
|
}
|
|
if paginationQuery != nil {
|
|
pq := *paginationQuery
|
|
dataselect.PaginationQuery = &pq
|
|
}
|
|
if sortQuery != nil && len(sortQuery.SortByList) > 0 {
|
|
dataselect.SortQuery.SortByList = append(dataselect.SortQuery.SortByList, sortQuery.SortByList...)
|
|
}
|
|
if filterQuery != nil && len(filterQuery.FilterByList) > 0 {
|
|
dataselect.FilterQuery.FilterByList = append(dataselect.FilterQuery.FilterByList, filterQuery.FilterByList...)
|
|
}
|
|
if filterQuery != nil {
|
|
dataselect.FilterQuery.Extend = filterQuery.Extend
|
|
if len(filterQuery.FilterByList) > 0 {
|
|
dataselect.FilterQuery.FilterByList = append(dataselect.FilterQuery.FilterByList, filterQuery.FilterByList...)
|
|
}
|
|
}
|
|
return dataselect
|
|
}
|