Merge pull request #4294 from jianzhiyao/frt/supports_for_4144

Frt/supports for issue 4144
This commit is contained in:
Ming Deng 2021-01-10 12:08:16 +08:00 committed by GitHub
commit 0139564cf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 352 additions and 19 deletions

View File

@ -4,4 +4,5 @@
- Remove `duration` from prometheus labels. [4391](https://github.com/beego/beego/pull/4391)
- Fix `unknown escape sequence` in generated code. [4385](https://github.com/beego/beego/pull/4385)
- Using fixed name `commentRouter.go` as generated file name. [4385](https://github.com/beego/beego/pull/4385)
- Fix 4383: ORM Adapter produces panic when using orm.RegisterModelWithPrefix. [4386](https://github.com/beego/beego/pull/4386)
- Fix 4383: ORM Adapter produces panic when using orm.RegisterModelWithPrefix. [4386](https://github.com/beego/beego/pull/4386)
- Support 4144: Add new api for order by for supporting multiple way to query [4294](https://github.com/beego/beego/pull/4294)

View File

@ -0,0 +1,6 @@
package clauses
const (
ExprSep = "__"
ExprDot = "."
)

View File

@ -0,0 +1,103 @@
package order_clause
import (
"github.com/beego/beego/v2/client/orm/clauses"
"strings"
)
type Sort int8
const (
None Sort = 0
Ascending Sort = 1
Descending Sort = 2
)
type Option func(order *Order)
type Order struct {
column string
sort Sort
isRaw bool
}
func Clause(options ...Option) *Order {
o := &Order{}
for _, option := range options {
option(o)
}
return o
}
func (o *Order) GetColumn() string {
return o.column
}
func (o *Order) GetSort() Sort {
return o.sort
}
func (o *Order) SortString() string {
switch o.GetSort() {
case Ascending:
return "ASC"
case Descending:
return "DESC"
}
return ``
}
func (o *Order) IsRaw() bool {
return o.isRaw
}
func ParseOrder(expressions ...string) []*Order {
var orders []*Order
for _, expression := range expressions {
sort := Ascending
column := strings.ReplaceAll(expression, clauses.ExprSep, clauses.ExprDot)
if column[0] == '-' {
sort = Descending
column = column[1:]
}
orders = append(orders, &Order{
column: column,
sort: sort,
})
}
return orders
}
func Column(column string) Option {
return func(order *Order) {
order.column = strings.ReplaceAll(column, clauses.ExprSep, clauses.ExprDot)
}
}
func sort(sort Sort) Option {
return func(order *Order) {
order.sort = sort
}
}
func SortAscending() Option {
return sort(Ascending)
}
func SortDescending() Option {
return sort(Descending)
}
func SortNone() Option {
return sort(None)
}
func Raw() Option {
return func(order *Order) {
order.isRaw = true
}
}

View File

@ -0,0 +1,144 @@
package order_clause
import (
"testing"
)
func TestClause(t *testing.T) {
var (
column = `a`
)
o := Clause(
Column(column),
)
if o.GetColumn() != column {
t.Error()
}
}
func TestSortAscending(t *testing.T) {
o := Clause(
SortAscending(),
)
if o.GetSort() != Ascending {
t.Error()
}
}
func TestSortDescending(t *testing.T) {
o := Clause(
SortDescending(),
)
if o.GetSort() != Descending {
t.Error()
}
}
func TestSortNone(t *testing.T) {
o1 := Clause(
SortNone(),
)
if o1.GetSort() != None {
t.Error()
}
o2 := Clause()
if o2.GetSort() != None {
t.Error()
}
}
func TestRaw(t *testing.T) {
o1 := Clause()
if o1.IsRaw() {
t.Error()
}
o2 := Clause(
Raw(),
)
if !o2.IsRaw() {
t.Error()
}
}
func TestColumn(t *testing.T) {
o1 := Clause(
Column(`aaa`),
)
if o1.GetColumn() != `aaa` {
t.Error()
}
}
func TestParseOrder(t *testing.T) {
orders := ParseOrder(
`-user__status`,
`status`,
`user__status`,
)
t.Log(orders)
if orders[0].GetSort() != Descending {
t.Error()
}
if orders[0].GetColumn() != `user.status` {
t.Error()
}
if orders[1].GetColumn() != `status` {
t.Error()
}
if orders[1].GetSort() != Ascending {
t.Error()
}
if orders[2].GetColumn() != `user.status` {
t.Error()
}
}
func TestOrder_GetColumn(t *testing.T) {
o := Clause(
Column(`user__id`),
)
if o.GetColumn() != `user.id` {
t.Error()
}
}
func TestOrder_GetSort(t *testing.T) {
o := Clause(
SortDescending(),
)
if o.GetSort() != Descending {
t.Error()
}
}
func TestOrder_IsRaw(t *testing.T) {
o1 := Clause()
if o1.IsRaw() {
t.Error()
}
o2 := Clause(
Raw(),
)
if !o2.IsRaw() {
t.Error()
}
}

View File

@ -16,6 +16,8 @@ package orm
import (
"fmt"
"github.com/beego/beego/v2/client/orm/clauses"
"github.com/beego/beego/v2/client/orm/clauses/order_clause"
"strings"
"time"
)
@ -421,7 +423,7 @@ func (t *dbTables) getGroupSQL(groups []string) (groupSQL string) {
}
// generate order sql.
func (t *dbTables) getOrderSQL(orders []string) (orderSQL string) {
func (t *dbTables) getOrderSQL(orders []*order_clause.Order) (orderSQL string) {
if len(orders) == 0 {
return
}
@ -430,19 +432,25 @@ func (t *dbTables) getOrderSQL(orders []string) (orderSQL string) {
orderSqls := make([]string, 0, len(orders))
for _, order := range orders {
asc := "ASC"
if order[0] == '-' {
asc = "DESC"
order = order[1:]
}
exprs := strings.Split(order, ExprSep)
column := order.GetColumn()
clause := strings.Split(column, clauses.ExprDot)
index, _, fi, suc := t.parseExprs(t.mi, exprs)
if !suc {
panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(exprs, ExprSep)))
}
if order.IsRaw() {
if len(clause) == 2 {
orderSqls = append(orderSqls, fmt.Sprintf("%s.%s%s%s %s", clause[0], Q, clause[1], Q, order.SortString()))
} else if len(clause) == 1 {
orderSqls = append(orderSqls, fmt.Sprintf("%s%s%s %s", Q, clause[0], Q, order.SortString()))
} else {
panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(clause, ExprSep)))
}
} else {
index, _, fi, suc := t.parseExprs(t.mi, clause)
if !suc {
panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(clause, ExprSep)))
}
orderSqls = append(orderSqls, fmt.Sprintf("%s.%s%s%s %s", index, Q, fi.column, Q, asc))
orderSqls = append(orderSqls, fmt.Sprintf("%s.%s%s%s %s", index, Q, fi.column, Q, order.SortString()))
}
}
orderSQL = fmt.Sprintf("ORDER BY %s ", strings.Join(orderSqls, ", "))

View File

@ -58,6 +58,7 @@ import (
"database/sql"
"errors"
"fmt"
"github.com/beego/beego/v2/client/orm/clauses/order_clause"
"os"
"reflect"
"time"
@ -351,7 +352,7 @@ func (o *ormBase) LoadRelatedWithCtx(ctx context.Context, md interface{}, name s
qs.relDepth = relDepth
if len(order) > 0 {
qs.orders = []string{order}
qs.orders = order_clause.ParseOrder(order)
}
find := ind.FieldByIndex(fi.fieldIndex)

View File

@ -16,12 +16,13 @@ package orm
import (
"fmt"
"github.com/beego/beego/v2/client/orm/clauses"
"strings"
)
// ExprSep define the expression separation
const (
ExprSep = "__"
ExprSep = clauses.ExprSep
)
type condValue struct {

View File

@ -17,8 +17,8 @@ package orm
import (
"context"
"fmt"
"github.com/beego/beego/v2/client/orm/hints"
"github.com/beego/beego/v2/client/orm/clauses/order_clause"
)
type colValue struct {
@ -71,7 +71,7 @@ type querySet struct {
limit int64
offset int64
groups []string
orders []string
orders []*order_clause.Order
distinct bool
forUpdate bool
useIndex int
@ -140,8 +140,20 @@ func (o querySet) GroupBy(exprs ...string) QuerySeter {
// add ORDER expression.
// "column" means ASC, "-column" means DESC.
func (o querySet) OrderBy(exprs ...string) QuerySeter {
o.orders = exprs
func (o querySet) OrderBy(expressions ...string) QuerySeter {
if len(expressions) <= 0 {
return &o
}
o.orders = order_clause.ParseOrder(expressions...)
return &o
}
// add ORDER expression.
func (o querySet) OrderClauses(orders ...*order_clause.Order) QuerySeter {
if len(orders) <= 0 {
return &o
}
o.orders = orders
return &o
}

View File

@ -21,6 +21,7 @@ import (
"context"
"database/sql"
"fmt"
"github.com/beego/beego/v2/client/orm/clauses/order_clause"
"io/ioutil"
"math"
"os"
@ -1146,6 +1147,26 @@ func TestOrderBy(t *testing.T) {
num, err = qs.OrderBy("-profile__age").Filter("user_name", "astaxie").Count()
throwFail(t, err)
throwFail(t, AssertIs(num, 1))
num, err = qs.OrderClauses(
order_clause.Clause(
order_clause.Column(`profile__age`),
order_clause.SortDescending(),
),
).Filter("user_name", "astaxie").Count()
throwFail(t, err)
throwFail(t, AssertIs(num, 1))
if IsMysql {
num, err = qs.OrderClauses(
order_clause.Clause(
order_clause.Column(`rand()`),
order_clause.Raw(),
),
).Filter("user_name", "astaxie").Count()
throwFail(t, err)
throwFail(t, AssertIs(num, 1))
}
}
func TestAll(t *testing.T) {
@ -1232,6 +1253,19 @@ func TestValues(t *testing.T) {
throwFail(t, AssertIs(maps[2]["Profile"], nil))
}
num, err = qs.OrderClauses(
order_clause.Clause(
order_clause.Column("Id"),
order_clause.SortAscending(),
),
).Values(&maps)
throwFail(t, err)
throwFail(t, AssertIs(num, 3))
if num == 3 {
throwFail(t, AssertIs(maps[0]["UserName"], "slene"))
throwFail(t, AssertIs(maps[2]["Profile"], nil))
}
num, err = qs.OrderBy("Id").Values(&maps, "UserName", "Profile__Age")
throwFail(t, err)
throwFail(t, AssertIs(num, 3))

View File

@ -17,6 +17,7 @@ package orm
import (
"context"
"database/sql"
"github.com/beego/beego/v2/client/orm/clauses/order_clause"
"reflect"
"time"
@ -289,6 +290,28 @@ type QuerySeter interface {
// for example:
// qs.OrderBy("-status")
OrderBy(exprs ...string) QuerySeter
// add ORDER expression by order clauses
// for example:
// OrderClauses(
// order_clause.Clause(
// order.Column("Id"),
// order.SortAscending(),
// ),
// order_clause.Clause(
// order.Column("status"),
// order.SortDescending(),
// ),
// )
// OrderClauses(order_clause.Clause(
// order_clause.Column(`user__status`),
// order_clause.SortDescending(),//default None
// ))
// OrderClauses(order_clause.Clause(
// order_clause.Column(`random()`),
// order_clause.SortNone(),//default None
// order_clause.Raw(),//default false.if true, do not check field is valid or not
// ))
OrderClauses(orders ...*order_clause.Order) QuerySeter
// add FORCE INDEX expression.
// for example:
// qs.ForceIndex(`idx_name1`,`idx_name2`)