Merge pull request #4294 from jianzhiyao/frt/supports_for_4144
Frt/supports for issue 4144
This commit is contained in:
		
						commit
						0139564cf0
					
				| @ -5,3 +5,4 @@ | ||||
| - 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) | ||||
| - Support 4144: Add new api for order by for supporting multiple way to query [4294](https://github.com/beego/beego/pull/4294) | ||||
							
								
								
									
										6
									
								
								client/orm/clauses/const.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								client/orm/clauses/const.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| package clauses | ||||
| 
 | ||||
| const ( | ||||
| 	ExprSep                = "__" | ||||
| 	ExprDot                = "." | ||||
| ) | ||||
							
								
								
									
										103
									
								
								client/orm/clauses/order_clause/order.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								client/orm/clauses/order_clause/order.go
									
									
									
									
									
										Normal 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 | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										144
									
								
								client/orm/clauses/order_clause/order_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								client/orm/clauses/order_clause/order_test.go
									
									
									
									
									
										Normal 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() | ||||
| 	} | ||||
| } | ||||
| @ -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 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(exprs, ExprSep))) | ||||
| 				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, ", ")) | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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 | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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)) | ||||
|  | ||||
| @ -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`) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user