liuhaijun e94826ce29 add server
Change-Id: I0760f17f6a01c0121b59fcbfafc666032dbc30af
2024-09-19 09:44:15 +00:00

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
}