默认的排序方法
ent支持两种排序方法:
go
users, err := client.User.Query().
Order(ent.Asc(user.FieldName, user.FieldNickName)).
All(ctx)
自v0.12.0开始,支持如下排序方法:
go
// 正序排序
users, err := client.User.Query().
Order(
user.ByName(),
user.ByNickname(),
).
All(ctx)
// 倒序排序
users, err := client.User.Query().
Order(
user.ByNickname(
sql.OrderDesc(),
),
).
All(ctx)
自定义排序方法
分页查询请求参数:
go
package page
import (
"github.com/gin-gonic/gin"
)
const defaultPageSize = 10
type Pagination struct {
PageNo int `form:"pageNo" json:"page_no" binding:"required"`
PageSize int `form:"pageSize" json:"page_size" binding:"required"`
// 排序规则,格式: `a,b,c ASC;d,e DESC;f`, 默认ASC,ASC可以省略。
// 字段名称为小写字母开头的驼峰格式,如userName, 会自动转换为蛇形user_name,
// 如果字段在数据表中不存在,则忽略该排序字段。
OrderBy string `form:"orderBy" json:"order_by"`
}
func NewPagination() *Pagination {
return &Pagination{}
}
// GetTerm return Calculated query conditions
func (p *Pagination) GetTerm() (int, int) {
if p.PageSize == 0 {
p.PageSize = defaultPageSize
}
if p.PageNo <= 0 {
p.PageNo = 1
}
offset := (p.PageNo - 1) * p.PageSize
limit := p.PageSize
return offset, limit
}
func BindPagination(c *gin.Context) (*Pagination, error) {
p := NewPagination()
if err := c.ShouldBindQuery(&p); err != nil {
return nil, err
}
return p, nil
}
使用ent的模板功能,为每个模型都绑定OrderByExpr方法,模板内容如下:
go
{{ define "order_by" }}
// Code generated by ent, DO NOT EDIT.
package entcore
const orderAsc = "ASC"
const orderDesc = "DESC"
// ==========================
// 通用排序解析
// ==========================
type OrderItem struct {
Fields []string
Direction string
}
// camelToSnake 将 CamelCase 转为 snake_case
func camelToSnake(input string) string {
// 正则匹配大写字母前插入下划线
// 特别处理:多个大写字母(如 ID、IP)保持一组处理
re := regexp.MustCompile(`([a-z0-9])([A-Z])`)
snake := re.ReplaceAllString(input, `${1}_${2}`)
// 处理 ID、URL 等缩写后带大写的问题,如:UserIDNumber → user_id_number
re2 := regexp.MustCompile(`([A-Z]+)([A-Z][a-z0-9]+)`)
snake = re2.ReplaceAllString(snake, `${1}_${2}`)
// 全部转小写
return strings.ToLower(snake)
}
// parseSortExpr 解析排序表达式,例如:
// "a,b,c ASC; d,e DESC"
func parseSortExpr(expr string) ([]OrderItem, error) {
items := []OrderItem{}
parts := strings.Split(expr, ";")
for _, part := range parts {
part = strings.TrimSpace(part)
if part == "" {
continue
}
dir := orderAsc
var fields []string
segs := strings.Fields(part)
len := len(segs)
if len == 0 {
continue
} else if len == 1 {
_fields := strings.Split(segs[0], ",")
for i := range _fields {
fields = append(fields, camelToSnake(strings.TrimSpace(_fields[i])))
}
} else if len == 2 {
_fields := strings.Split(segs[0], ",")
for i := range _fields {
fields = append(fields, camelToSnake(strings.TrimSpace(_fields[i])))
}
if strings.ToUpper(segs[1]) == orderDesc {
dir = orderDesc
}
} else {
continue
}
items = append(items, OrderItem{
Fields: fields,
Direction: dir,
})
}
return items, nil
}
// ==========================
// 每个模型的 OrderByExpr 方法
// ==========================
{{ range $n, $t := $.Nodes }}
// OrderByExpr 支持多字段、多方向排序
func (q *{{ $t.Name }}Query) OrderByExpr(expr string) *{{ $t.Name }}Query {
if expr == "" {
return q
}
items, _ := parseSortExpr(expr)
for _, item := range items {
var fields []string
for _, f := range item.Fields {
ok := {{ $t.Name | lower }}.ValidColumn(f)
if !ok {
continue
}
fields = append(fields, f)
}
if len(fields) > 0 {
if item.Direction == orderAsc {
q = q.Order(Asc(fields...))
} else {
q = q.Order(Desc(fields...))
}
}
}
return q
}
// 按ID正序排序
func (q *{{ $t.Name }}Query) OrderByID() *{{ $t.Name }}Query {
return q.Order({{ $t.Name | lower }}.ByID())
}
// 按ID倒序排序
func (q *{{ $t.Name }}Query) OrderByIDDesc() *{{ $t.Name }}Query {
return q.Order({{ $t.Name | lower }}.ByID(sql.OrderDesc()))
}
{{ end }}
{{ end }}
此外,还添加了OrderByID和OrderByIDDesc方法。
生成代码
在生成代码的命令中加入--template选项指定模板:
shell
go run -mod=mod entgo.io/ent/cmd/ent generate --feature intercept,schema/snapshot ./schema --target ./schema/entcode --template glob="./template/*.tmpl"
生成的代码:
go
const orderAsc = "ASC"
const orderDesc = "DESC"
// ==========================
// 通用排序解析
// ==========================
type OrderItem struct {
Fields []string
Direction string
}
// camelToSnake 将 CamelCase 转为 snake_case
func camelToSnake(input string) string {
// 正则匹配大写字母前插入下划线
// 特别处理:多个大写字母(如 ID、IP)保持一组处理
re := regexp.MustCompile(`([a-z0-9])([A-Z])`)
snake := re.ReplaceAllString(input, `${1}_${2}`)
// 处理 ID、URL 等缩写后带大写的问题,如:UserIDNumber → user_id_number
re2 := regexp.MustCompile(`([A-Z]+)([A-Z][a-z0-9]+)`)
snake = re2.ReplaceAllString(snake, `${1}_${2}`)
// 全部转小写
return strings.ToLower(snake)
}
// parseSortExpr 解析排序表达式,例如:
// "a,b,c ASC; d,e DESC"
func parseSortExpr(expr string) ([]OrderItem, error) {
items := []OrderItem{}
parts := strings.Split(expr, ";")
for _, part := range parts {
part = strings.TrimSpace(part)
if part == "" {
continue
}
dir := orderAsc
var fields []string
segs := strings.Fields(part)
len := len(segs)
if len == 0 {
continue
} else if len == 1 {
_fields := strings.Split(segs[0], ",")
for i := range _fields {
fields = append(fields, camelToSnake(strings.TrimSpace(_fields[i])))
}
} else if len == 2 {
_fields := strings.Split(segs[0], ",")
for i := range _fields {
fields = append(fields, camelToSnake(strings.TrimSpace(_fields[i])))
}
if strings.ToUpper(segs[1]) == orderDesc {
dir = orderDesc
}
} else {
continue
}
items = append(items, OrderItem{
Fields: fields,
Direction: dir,
})
}
return items, nil
}
// OrderByExpr 支持多字段、多方向排序
func (q *SysUserQuery) OrderByExpr(expr string) *SysUserQuery {
if expr == "" {
return q
}
items, _ := parseSortExpr(expr)
for _, item := range items {
var fields []string
for _, f := range item.Fields {
ok := sysuser.ValidColumn(f)
if !ok {
continue
}
fields = append(fields, f)
}
if len(fields) > 0 {
if item.Direction == orderAsc {
q = q.Order(Asc(fields...))
} else {
q = q.Order(Desc(fields...))
}
}
}
return q
}
// 按ID正序排序
func (q *SysUserQuery) OrderByID() *SysUserQuery {
return q.Order(sysuser.ByID())
}
// 按ID倒序排序
func (q *SysUserQuery) OrderByIDDesc() *SysUserQuery {
return q.Order(sysuser.ByID(sql.OrderDesc()))
}
排序条件orderBy中的字段为小写字母开头的驼峰格式。会使用camelToSnake方法将字段转为蛇形,并用ValidColumn方法校验,以防字段在数据表中不存在而报错。ValidColumn是ent自动生成的校验字段在数据表中是否存在的方法,如不存在,则该字段会被忽略。