feat: entgo query .

This commit is contained in:
tx7do
2023-10-25 12:43:46 +08:00
parent 5b2a7ab922
commit 36a096df76
9 changed files with 32 additions and 83 deletions

322
entgo/query/filter.go Normal file
View File

@@ -0,0 +1,322 @@
package entgo
import (
"encoding/json"
"strings"
"entgo.io/ent/dialect/sql"
"github.com/go-kratos/kratos/v2/encoding"
"github.com/tx7do/kratos-utils/stringcase"
)
const (
FilterNot = "not" // 不是
FilterIn = "in" // 检查字段值是否在传递列表中
FilterNotIn = "not_in" // 不在列表中
FilterGTE = "gte" // 大于或等于传递的值
FilterGT = "gt" // 大于传递值
FilterLTE = "lte" // 低于或等于传递值
FilterLT = "lt" // 低于传递值
FilterRange = "range" // 介于和给定的两个值之间
FilterIsNull = "isnull" // 字段为空
FilterNotIsNull = "not_isnull" // 字段不为空
FilterContains = "contains" // 字段包含指定的子字符串
FilterInsensitiveContains = "icontains" // 不区分大小写,字段包含指定的子字符串
FilterStartsWith = "startswith" // 如果字段以值开头
FilterInsensitiveStartsWith = "istartswith" // 不区分大小写,如果字段以值开头
FilterEndsWith = "endswith" // 如果字段以值结尾
FilterInsensitiveEndsWith = "iendswith" // 不区分大小写,如果字段以值结尾
FilterExact = "exact" // 等于
FilterInsensitiveExact = "iexact" // 不区分大小写等于
FilterSearch = "search" // 全文搜索
)
const (
FilterDatePartYear = "year" // 年
FilterDatePartQuarter = "quarter" // 季度
FilterDatePartMonth = "month" // 月
FilterDatePartWeek = "week" // 星期
FilterDatePartDay = "day" // 日
FilterDatePartHour = "hour" // 小时
FilterDatePartMinute = "minute" // 分钟
FilterDatePartSecond = "second" // 秒
FilterDatePartMicrosecond = "microsecond" // 微秒
)
// QueryCommandToWhereConditions 查询命令转换为选择条件
func QueryCommandToWhereConditions(strJson string, isOr bool) (error, func(s *sql.Selector)) {
if len(strJson) == 0 {
return nil, nil
}
codec := encoding.GetCodec("json")
queryMap := make(map[string]string)
var queryMapArray []map[string]string
if err1 := codec.Unmarshal([]byte(strJson), &queryMap); err1 != nil {
if err2 := codec.Unmarshal([]byte(strJson), &queryMapArray); err2 != nil {
return err2, nil
}
}
return nil, func(s *sql.Selector) {
var ps []*sql.Predicate
ps = append(ps, processQueryMap(s, queryMap)...)
for _, v := range queryMapArray {
ps = append(ps, processQueryMap(s, v)...)
}
if isOr {
s.Where(sql.Or(ps...))
} else {
s.Where(sql.And(ps...))
}
}
}
func processQueryMap(s *sql.Selector, queryMap map[string]string) []*sql.Predicate {
var ps []*sql.Predicate
for k, v := range queryMap {
key := stringcase.ToSnakeCase(k)
keys := strings.Split(key, "__")
if cond := oneFieldFilter(s, keys, v); cond != nil {
ps = append(ps, cond)
}
}
return ps
}
func BuildFilterSelector(andFilterJsonString, orFilterJsonString string) (error, []func(s *sql.Selector)) {
var err error
var queryConditions []func(s *sql.Selector)
var andSelector func(s *sql.Selector)
err, andSelector = QueryCommandToWhereConditions(andFilterJsonString, false)
if err != nil {
return err, nil
}
if andSelector != nil {
queryConditions = append(queryConditions, andSelector)
}
var orSelector func(s *sql.Selector)
err, orSelector = QueryCommandToWhereConditions(orFilterJsonString, true)
if err != nil {
return err, nil
}
if orSelector != nil {
queryConditions = append(queryConditions, orSelector)
}
return nil, queryConditions
}
func oneFieldFilter(s *sql.Selector, keys []string, value string) *sql.Predicate {
var cond *sql.Predicate
if len(keys) == 1 {
field := keys[0]
cond = filterEqual(s, field, value)
} else if len(keys) == 2 {
if len(keys[0]) == 0 {
return nil
}
field := keys[0]
op := strings.ToLower(keys[1])
switch op {
case FilterNot:
cond = filterNot(s, field, value)
case FilterIn:
cond = filterIn(s, field, value)
case FilterNotIn:
cond = filterNotIn(s, field, value)
case FilterGTE:
cond = filterGTE(s, field, value)
case FilterGT:
cond = filterGT(s, field, value)
case FilterLTE:
cond = filterLTE(s, field, value)
case FilterLT:
cond = filterLT(s, field, value)
case FilterRange:
cond = filterRange(s, field, value)
case FilterIsNull:
cond = filterIsNull(s, field, value)
case FilterNotIsNull:
cond = filterNotIsNull(s, field, value)
case FilterContains:
cond = filterContains(s, field, value)
case FilterInsensitiveContains:
cond = filterInsensitiveContains(s, field, value)
case FilterStartsWith:
cond = filterStartsWith(s, field, value)
case FilterInsensitiveStartsWith:
cond = filterInsensitiveStartsWith(s, field, value)
case FilterEndsWith:
cond = filterEndsWith(s, field, value)
case FilterInsensitiveEndsWith:
cond = filterInsensitiveEndsWith(s, field, value)
case FilterInsensitiveExact:
cond = filterInsensitiveExact(s, field, value)
case FilterExact:
cond = filterExact(s, field, value)
case FilterSearch:
cond = filterSearch(s, field, value)
default:
cond = filterDatePart(s, op, field, value)
}
}
return cond
}
// filterEqual 相等 WHERE "name" = $1
func filterEqual(s *sql.Selector, field, value string) *sql.Predicate {
return sql.EQ(s.C(field), value)
}
// filterNot NOT操作 WHERE NOT ("name" = $1 AND "age" = $2)
func filterNot(s *sql.Selector, field, value string) *sql.Predicate {
return sql.Not(sql.EQ(s.C(field), value))
}
// filterIn IN操作
func filterIn(s *sql.Selector, field, value string) *sql.Predicate {
var strs []string
if err := json.Unmarshal([]byte(value), &strs); err == nil {
return sql.In(s.C(field), strs)
}
var float64s []float64
if err := json.Unmarshal([]byte(value), &float64s); err == nil {
return sql.In(s.C(field), strs)
}
return nil
}
// filterNotIn 操作
func filterNotIn(s *sql.Selector, field, value string) *sql.Predicate {
var strs []string
if err := json.Unmarshal([]byte(value), &strs); err == nil {
return sql.NotIn(s.C(field), strs)
}
var float64s []float64
if err := json.Unmarshal([]byte(value), &float64s); err == nil {
return sql.NotIn(s.C(field), strs)
}
return nil
}
// filterGTE 操作
func filterGTE(s *sql.Selector, field, value string) *sql.Predicate {
return sql.GTE(s.C(field), value)
}
// filterGT 操作
func filterGT(s *sql.Selector, field, value string) *sql.Predicate {
return sql.GT(s.C(field), value)
}
// filterLTE 操作
func filterLTE(s *sql.Selector, field, value string) *sql.Predicate {
return sql.LTE(s.C(field), value)
}
// filterLT 操作
func filterLT(s *sql.Selector, field, value string) *sql.Predicate {
return sql.LT(s.C(field), value)
}
// filterRange 操作
func filterRange(s *sql.Selector, field, value string) *sql.Predicate {
var strs []string
if err := json.Unmarshal([]byte(value), &strs); err == nil {
if len(strs) != 2 {
return nil
}
return sql.And(
sql.GTE(s.C(field), strs[0]),
sql.LTE(s.C(field), strs[1]),
)
}
var float64s []float64
if err := json.Unmarshal([]byte(value), &float64s); err == nil {
if len(float64s) != 2 {
return nil
}
return sql.And(
sql.GTE(s.C(field), float64s[0]),
sql.LTE(s.C(field), float64s[1]),
)
}
return nil
}
// filterIsNull 操作
func filterIsNull(s *sql.Selector, field, _ string) *sql.Predicate {
return sql.IsNull(s.C(field))
}
// filterNotIsNull 操作
func filterNotIsNull(s *sql.Selector, field, _ string) *sql.Predicate {
return sql.Not(sql.IsNull(s.C(field)))
}
// filterContains 前后模糊查询 WHERE city LIKE '%L%';
func filterContains(s *sql.Selector, field, value string) *sql.Predicate {
return sql.Contains(s.C(field), value)
}
// filterInsensitiveContains 前后模糊查询 WHERE city ILIKE '%L%';
func filterInsensitiveContains(s *sql.Selector, field, value string) *sql.Predicate {
return sql.ContainsFold(s.C(field), value)
}
// filterStartsWith 前缀+模糊查询 WHERE CustomerName LIKE 'La%';
func filterStartsWith(s *sql.Selector, field, value string) *sql.Predicate {
return sql.HasPrefix(s.C(field), value)
}
// filterInsensitiveStartsWith 前缀+模糊查询 WHERE CustomerName ILIKE 'La%';
func filterInsensitiveStartsWith(s *sql.Selector, field, value string) *sql.Predicate {
return sql.EqualFold(s.C(field), value+"%")
}
// filterEndsWith 后缀+模糊查询 WHERE CustomerName LIKE '%a';
func filterEndsWith(s *sql.Selector, field, value string) *sql.Predicate {
return sql.HasSuffix(s.C(field), value)
}
// filterInsensitiveEndsWith 后缀+模糊查询 WHERE CustomerName ILIKE '%a';
func filterInsensitiveEndsWith(s *sql.Selector, field, value string) *sql.Predicate {
return sql.EqualFold(s.C(field), "%"+value)
}
// filterInsensitiveExact 操作 WHERE CustomerName ILIKE 'a';
func filterInsensitiveExact(s *sql.Selector, field, value string) *sql.Predicate {
return sql.EqualFold(s.C(field), value)
}
// filterInsensitiveExact 操作 WHERE CustomerName LIKE 'a';
func filterExact(s *sql.Selector, field, value string) *sql.Predicate {
return sql.Like(s.C(field), value)
}
// filterSearch 全文搜索
func filterSearch(s *sql.Selector, _, _ string) *sql.Predicate {
return nil
}
// filterDatePart 时间戳提取日期 select extract(quarter from timestamp '2018-08-15 12:10:10');
func filterDatePart(s *sql.Selector, datePart, field, value string) *sql.Predicate {
return nil
}

45
entgo/query/order.go Normal file
View File

@@ -0,0 +1,45 @@
package entgo
import (
"strings"
"entgo.io/ent/dialect/sql"
)
// QueryCommandToOrderConditions 查询命令转换为排序条件
func QueryCommandToOrderConditions(orderBys []string) (error, func(s *sql.Selector)) {
if len(orderBys) == 0 {
return nil, nil
}
return nil, func(s *sql.Selector) {
for _, v := range orderBys {
if strings.HasPrefix(v, "-") {
// 降序
key := v[1:]
if len(key) == 0 {
continue
}
s.OrderBy(sql.Desc(s.C(key)))
} else {
// 升序
if len(v) == 0 {
continue
}
s.OrderBy(sql.Asc(s.C(v)))
}
}
}
}
func BuildOrderSelector(orderBys []string, defaultOrderField string) (error, func(s *sql.Selector)) {
if len(orderBys) == 0 {
return nil, func(s *sql.Selector) {
s.OrderBy(sql.Desc(s.C(defaultOrderField)))
}
} else {
return QueryCommandToOrderConditions(orderBys)
}
}

26
entgo/query/pagination.go Normal file
View File

@@ -0,0 +1,26 @@
package entgo
import (
"entgo.io/ent/dialect/sql"
"github.com/tx7do/kratos-utils/pagination"
)
func BuildPaginationSelector(page, pageSize int32, noPaging bool) func(*sql.Selector) {
if noPaging {
return nil
}
if page == 0 {
page = DefaultPage
}
if pageSize == 0 {
pageSize = DefaultPageSize
}
return func(s *sql.Selector) {
s.Offset(pagination.GetPageOffset(page, pageSize)).
Limit(int(pageSize))
}
}

64
entgo/query/query.go Normal file
View File

@@ -0,0 +1,64 @@
package entgo
import (
"entgo.io/ent/dialect/sql"
"github.com/go-kratos/kratos/v2/encoding"
_ "github.com/go-kratos/kratos/v2/encoding/json"
)
const (
DefaultPage = 1
DefaultPageSize = 10
)
// parseJsonMap 解析JSON字符串里面的MAP包含Array形式的Map
func parseJsonMap(strJson []byte, retMap *map[string]string) error {
codec := encoding.GetCodec("json")
var err error
if err = codec.Unmarshal(strJson, retMap); err != nil {
var retArray []map[string]string
if err1 := codec.Unmarshal(strJson, &retArray); err1 == nil {
for _, itemA := range retArray {
for k, v := range itemA {
(*retMap)[k] = v
}
}
} else {
return err
}
}
return nil
}
// BuildQuerySelector 构建分页查询选择器
func BuildQuerySelector(andFilterJsonString, orFilterJsonString string,
page, pageSize int32, noPaging bool,
orderBys []string, defaultOrderField string) (err error, whereSelectors []func(s *sql.Selector), querySelectors []func(s *sql.Selector)) {
err, whereSelectors = BuildFilterSelector(andFilterJsonString, orFilterJsonString)
if err != nil {
return err, nil, nil
}
var orderSelector func(s *sql.Selector)
err, orderSelector = BuildOrderSelector(orderBys, defaultOrderField)
if err != nil {
return err, nil, nil
}
pageSelector := BuildPaginationSelector(page, pageSize, noPaging)
if len(whereSelectors) > 0 {
querySelectors = append(querySelectors, whereSelectors...)
}
if orderSelector != nil {
querySelectors = append(querySelectors, orderSelector)
}
if pageSelector != nil {
querySelectors = append(querySelectors, pageSelector)
}
return
}

110
entgo/query/query_test.go Normal file
View File

@@ -0,0 +1,110 @@
package entgo
import (
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/go-kratos/kratos/v2/encoding"
_ "github.com/go-kratos/kratos/v2/encoding/json"
)
func TestKratosJsonCodec(t *testing.T) {
var req struct {
Query map[string]string `json:"query,omitempty"`
}
req.Query = make(map[string]string)
req.Query["key1"] = "val1"
req.Query["key2"] = "val2"
codec := encoding.GetCodec("json")
var err error
var data []byte
data, err = codec.Marshal(req)
assert.Nil(t, err)
fmt.Println(string(data))
err = codec.Unmarshal(data, &req)
assert.Nil(t, err)
data1 := `{"query":{"key1":"val1","key2":"val2"}}`
err = codec.Unmarshal([]byte(data1), &req)
assert.Nil(t, err)
//data2 := `{"query":[{"key1":"val1"},{"key2":"val2"}]}`
//err = codec.Unmarshal([]byte(data2), &req)
//assert.Nil(t, err)
}
func TestJsonCodec(t *testing.T) {
var req struct {
Query map[string]string `json:"query,omitempty"`
}
req.Query = make(map[string]string)
req.Query["key1"] = "val1"
req.Query["key2"] = "val2"
var err error
var data []byte
data, err = json.Marshal(req)
assert.Nil(t, err)
fmt.Println(string(data))
err = json.Unmarshal(data, &req)
assert.Nil(t, err)
data1 := `{"query":{"key1":"val1","key2":"val2"}}`
err = json.Unmarshal([]byte(data1), &req)
assert.Nil(t, err)
data2 := `[1.0,2,3]`
var float64s []float64
err = json.Unmarshal([]byte(data2), &float64s)
assert.Nil(t, err)
fmt.Println(float64s)
data3 := `["1.0","2","3"]`
var strs []string
err = json.Unmarshal([]byte(data3), &strs)
assert.Nil(t, err)
fmt.Println(strs)
data4 := `{"key1":"val1", "key1":"val2", "key2":"val2"}`
var mapstrs map[string]string
err = json.Unmarshal([]byte(data4), &mapstrs)
assert.Nil(t, err)
fmt.Println(mapstrs)
}
func TestParseJsonMap(t *testing.T) {
var err error
req1 := make(map[string]string)
data1 := `{"key1":"val1", "key1":"val2", "key2":"val2"}`
err = parseJsonMap([]byte(data1), &req1)
assert.Nil(t, err)
fmt.Println(req1)
req2 := make(map[string]string)
data2 := `[{"key1":"val1"},{"key2":"val2"}]`
err = parseJsonMap([]byte(data2), &req2)
assert.Nil(t, err)
fmt.Println(req1)
}
func TestSplitQuery(t *testing.T) {
var keys []string
keys = strings.Split("id", "__")
assert.Equal(t, len(keys), 1)
assert.Equal(t, keys[0], "id")
keys = strings.Split("id__not", "__")
assert.Equal(t, len(keys), 2)
assert.Equal(t, keys[0], "id")
assert.Equal(t, keys[1], "not")
}