Skip to content

Commit 2aef540

Browse files
committed
Add OrderByAsc and OrderByDesc methods to all builders
Implement new OrderByAsc(col) and OrderByDesc(col) methods for SelectBuilder, UnionBuilder, UpdateBuilder, and DeleteBuilder. These methods allow chaining multiple ORDER BY columns with different sort directions, addressing the limitation where the old OrderBy/Asc/Desc pattern could only apply a single direction to all columns. Key changes: - Add OrderByAsc(col string) and OrderByDesc(col string) to all four builders - Each method appends the column with explicit ASC/DESC suffix - Support chaining for complex ordering: OrderByDesc("score").OrderByAsc("name") - Deprecate OrderBy/Asc/Desc methods with clear migration guidance - Add comprehensive tests (TestSelectBuilder_OrderByAscDesc) - Add example functions demonstrating usage - Update all existing examples to use new API - Update README.md with new "Build ORDER BY clause" section This design is inspired by MyBatis-Plus and provides a more intuitive and flexible API for handling multiple ORDER BY columns with different sorting directions. fix #214
1 parent aaf0f2c commit 2aef540

File tree

9 files changed

+249
-9
lines changed

9 files changed

+249
-9
lines changed

README.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- [Pre-defined SQL builders](#pre-defined-sql-builders)
1212
- [Build `WHERE` clause](#build-where-clause)
1313
- [Share `WHERE` clause among builders](#share-where-clause-among-builders)
14+
- [Build `ORDER BY` clause](#build-order-by-clause)
1415
- [Build SQL for different systems](#build-sql-for-different-systems)
1516
- [Using `Struct` as a light weight ORM](#using-struct-as-a-light-weight-orm)
1617
- [Nested SQL](#nested-sql)
@@ -203,6 +204,26 @@ fmt.Println(ub)
203204

204205
Refer to the [WhereClause](https://pkg.go.dev/github.com/huandu/go-sqlbuilder#WhereClause) examples to learn its usage.
205206

207+
### Build `ORDER BY` clause
208+
209+
The `ORDER BY` clause is commonly used to sort query results. This package provides convenient methods to build `ORDER BY` clauses with proper ordering directions.
210+
211+
For scenarios where you need to order by multiple columns with different directions (ASC/DESC), use `OrderByAsc` and `OrderByDesc` methods. These methods can be chained to add multiple columns with their specific ordering.
212+
213+
```go
214+
sb := sqlbuilder.NewSelectBuilder()
215+
sb.Select("id", "name", "score").From("users")
216+
sb.OrderByDesc("score").OrderByAsc("name")
217+
218+
sql, args := sb.Build()
219+
fmt.Println(sql)
220+
221+
// Output:
222+
// SELECT id, name, score FROM users ORDER BY score DESC, name ASC
223+
```
224+
225+
The older `OrderBy` method combined with `Asc`/`Desc` is still available but deprecated, as it only supports a single ordering direction for all columns. The new `OrderByAsc` and `OrderByDesc` methods provide more flexibility and clarity when working with multiple columns.
226+
206227
### Build SQL for different systems
207228

208229
SQL syntax and parameter placeholders can differ across systems. To address these variations, this package introduces a concept termed "flavor".
@@ -412,7 +433,7 @@ var baseUserSelect = sqlbuilder.NewSelectBuilder().
412433

413434
func ListActiveUsers(limit, offset int) (string, []interface{}) {
414435
sb := baseUserSelect.Clone() // independent copy
415-
sb.OrderBy("id").Asc()
436+
sb.OrderByAsc("id")
416437
sb.Limit(limit).Offset(offset)
417438
return sb.Build()
418439
}

cte_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func ExampleCTEBuilder() {
6868
sb.Where(
6969
sb.LessEqualThan("orders.price", 200),
7070
"valid_users.level < orders.min_level",
71-
).OrderBy("orders.price").Desc()
71+
).OrderByDesc("orders.price")
7272

7373
sql, args := sb.Build()
7474
fmt.Println(sql)

delete.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,20 +151,49 @@ func (db *DeleteBuilder) AddWhereClause(whereClause *WhereClause) *DeleteBuilder
151151
}
152152

153153
// OrderBy sets columns of ORDER BY in DELETE.
154+
//
155+
// Deprecated: Use OrderByAsc or OrderByDesc instead for better support of multiple ORDER BY columns with different directions.
156+
// OrderBy combined with Asc/Desc only supports a single direction for all columns.
154157
func (db *DeleteBuilder) OrderBy(col ...string) *DeleteBuilder {
155158
db.orderByCols = col
156159
db.marker = deleteMarkerAfterOrderBy
157160
return db
158161
}
159162

163+
// OrderByAsc sets a column of ORDER BY in DELETE with ASC order.
164+
// It supports chaining multiple calls to add multiple ORDER BY columns with different directions.
165+
//
166+
// db.OrderByAsc("name").OrderByDesc("id")
167+
// // Generates: ORDER BY name ASC, id DESC
168+
func (db *DeleteBuilder) OrderByAsc(col string) *DeleteBuilder {
169+
db.orderByCols = append(db.orderByCols, col+" ASC")
170+
db.marker = deleteMarkerAfterOrderBy
171+
return db
172+
}
173+
174+
// OrderByDesc sets a column of ORDER BY in DELETE with DESC order.
175+
// It supports chaining multiple calls to add multiple ORDER BY columns with different directions.
176+
//
177+
// db.OrderByDesc("id").OrderByAsc("name")
178+
// // Generates: ORDER BY id DESC, name ASC
179+
func (db *DeleteBuilder) OrderByDesc(col string) *DeleteBuilder {
180+
db.orderByCols = append(db.orderByCols, col+" DESC")
181+
db.marker = deleteMarkerAfterOrderBy
182+
return db
183+
}
184+
160185
// Asc sets order of ORDER BY to ASC.
186+
//
187+
// Deprecated: Use OrderByAsc instead. Asc only supports a single direction for all ORDER BY columns.
161188
func (db *DeleteBuilder) Asc() *DeleteBuilder {
162189
db.order = "ASC"
163190
db.marker = deleteMarkerAfterOrderBy
164191
return db
165192
}
166193

167194
// Desc sets order of ORDER BY to DESC.
195+
//
196+
// Deprecated: Use OrderByDesc instead. Desc only supports a single direction for all ORDER BY columns.
168197
func (db *DeleteBuilder) Desc() *DeleteBuilder {
169198
db.order = "DESC"
170199
db.marker = deleteMarkerAfterOrderBy

select.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,20 +245,49 @@ func (sb *SelectBuilder) GroupBy(col ...string) *SelectBuilder {
245245
}
246246

247247
// OrderBy sets columns of ORDER BY in SELECT.
248+
//
249+
// Deprecated: Use OrderByAsc or OrderByDesc instead for better support of multiple ORDER BY columns with different directions.
250+
// OrderBy combined with Asc/Desc only supports a single direction for all columns.
248251
func (sb *SelectBuilder) OrderBy(col ...string) *SelectBuilder {
249252
sb.orderByCols = append(sb.orderByCols, col...)
250253
sb.marker = selectMarkerAfterOrderBy
251254
return sb
252255
}
253256

257+
// OrderByAsc sets a column of ORDER BY in SELECT with ASC order.
258+
// It supports chaining multiple calls to add multiple ORDER BY columns with different directions.
259+
//
260+
// sb.OrderByAsc("name").OrderByDesc("id")
261+
// // Generates: ORDER BY name ASC, id DESC
262+
func (sb *SelectBuilder) OrderByAsc(col string) *SelectBuilder {
263+
sb.orderByCols = append(sb.orderByCols, col+" ASC")
264+
sb.marker = selectMarkerAfterOrderBy
265+
return sb
266+
}
267+
268+
// OrderByDesc sets a column of ORDER BY in SELECT with DESC order.
269+
// It supports chaining multiple calls to add multiple ORDER BY columns with different directions.
270+
//
271+
// sb.OrderByDesc("id").OrderByAsc("name")
272+
// // Generates: ORDER BY id DESC, name ASC
273+
func (sb *SelectBuilder) OrderByDesc(col string) *SelectBuilder {
274+
sb.orderByCols = append(sb.orderByCols, col+" DESC")
275+
sb.marker = selectMarkerAfterOrderBy
276+
return sb
277+
}
278+
254279
// Asc sets order of ORDER BY to ASC.
280+
//
281+
// Deprecated: Use OrderByAsc instead. Asc only supports a single direction for all ORDER BY columns.
255282
func (sb *SelectBuilder) Asc() *SelectBuilder {
256283
sb.order = "ASC"
257284
sb.marker = selectMarkerAfterOrderBy
258285
return sb
259286
}
260287

261288
// Desc sets order of ORDER BY to DESC.
289+
//
290+
// Deprecated: Use OrderByDesc instead. Desc only supports a single direction for all ORDER BY columns.
262291
func (sb *SelectBuilder) Desc() *SelectBuilder {
263292
sb.order = "DESC"
264293
sb.marker = selectMarkerAfterOrderBy

select_test.go

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func ExampleSelectBuilder() {
4444
"modified_at > created_at + "+sb.Var(86400), // It's allowed to write arbitrary SQL.
4545
)
4646
sb.GroupBy("status").Having(sb.NotIn("status", 4, 5))
47-
sb.OrderBy("modified_at").Asc()
47+
sb.OrderByAsc("modified_at")
4848
sb.Limit(10).Offset(5)
4949

5050
s, args := sb.Build()
@@ -73,7 +73,7 @@ func ExampleSelectBuilder_advancedUsage() {
7373
sb.In("status", Flatten([]int{1, 2, 3})...),
7474
sb.Between("created_at", start, end),
7575
)
76-
sb.OrderBy("modified_at").Desc()
76+
sb.OrderByDesc("modified_at")
7777

7878
innerSb.Select("*")
7979
innerSb.From("banned")
@@ -407,7 +407,7 @@ func ExampleSelectBuilder_LateralAs() {
407407
Where(
408408
"all_sales.salesperson_id = salesperson.id",
409409
).
410-
OrderBy("amount").Desc().Limit(1),
410+
OrderByDesc("amount").Limit(1),
411411
"max_sale",
412412
),
413413
)
@@ -449,3 +449,106 @@ func TestSelectBuilderClone(t *testing.T) {
449449
s2After := clone.String()
450450
a.NotEqual(s1After, s2After)
451451
}
452+
453+
func ExampleSelectBuilder_OrderByAsc() {
454+
sb := NewSelectBuilder()
455+
sb.Select("id", "name", "score")
456+
sb.From("users")
457+
sb.Where(sb.GreaterThan("score", 0))
458+
sb.OrderByAsc("name")
459+
460+
sql, args := sb.Build()
461+
fmt.Println(sql)
462+
fmt.Println(args)
463+
464+
// Output:
465+
// SELECT id, name, score FROM users WHERE score > ? ORDER BY name ASC
466+
// [0]
467+
}
468+
469+
func ExampleSelectBuilder_OrderByDesc() {
470+
sb := NewSelectBuilder()
471+
sb.Select("id", "name", "score")
472+
sb.From("users")
473+
sb.Where(sb.GreaterThan("score", 0))
474+
sb.OrderByDesc("score")
475+
476+
sql, args := sb.Build()
477+
fmt.Println(sql)
478+
fmt.Println(args)
479+
480+
// Output:
481+
// SELECT id, name, score FROM users WHERE score > ? ORDER BY score DESC
482+
// [0]
483+
}
484+
485+
func ExampleSelectBuilder_OrderByAsc_multiple() {
486+
sb := NewSelectBuilder()
487+
sb.Select("id", "name", "score")
488+
sb.From("users")
489+
sb.Where(sb.GreaterThan("score", 0))
490+
// Chain multiple OrderByAsc and OrderByDesc calls with different directions
491+
sb.OrderByDesc("score").OrderByAsc("name").OrderByDesc("id")
492+
493+
sql, args := sb.Build()
494+
fmt.Println(sql)
495+
fmt.Println(args)
496+
497+
// Output:
498+
// SELECT id, name, score FROM users WHERE score > ? ORDER BY score DESC, name ASC, id DESC
499+
// [0]
500+
}
501+
502+
func TestSelectBuilder_OrderByAscDesc(t *testing.T) {
503+
a := assert.New(t)
504+
505+
// Test OrderByAsc with single column
506+
sb := NewSelectBuilder()
507+
sb.Select("*").From("users").OrderByAsc("name")
508+
sql, _ := sb.Build()
509+
a.Equal("SELECT * FROM users ORDER BY name ASC", sql)
510+
511+
// Test OrderByDesc with single column
512+
sb = NewSelectBuilder()
513+
sb.Select("*").From("users").OrderByDesc("id")
514+
sql, _ = sb.Build()
515+
a.Equal("SELECT * FROM users ORDER BY id DESC", sql)
516+
517+
// Test chaining OrderByAsc and OrderByDesc
518+
sb = NewSelectBuilder()
519+
sb.Select("*").From("users")
520+
sb.OrderByDesc("score").OrderByAsc("name")
521+
sql, _ = sb.Build()
522+
a.Equal("SELECT * FROM users ORDER BY score DESC, name ASC", sql)
523+
524+
// Test multiple OrderByDesc calls
525+
sb = NewSelectBuilder()
526+
sb.Select("*").From("users")
527+
sb.OrderByDesc("score").OrderByDesc("id")
528+
sql, _ = sb.Build()
529+
a.Equal("SELECT * FROM users ORDER BY score DESC, id DESC", sql)
530+
531+
// Test multiple OrderByAsc calls
532+
sb = NewSelectBuilder()
533+
sb.Select("*").From("users")
534+
sb.OrderByAsc("name").OrderByAsc("email")
535+
sql, _ = sb.Build()
536+
a.Equal("SELECT * FROM users ORDER BY name ASC, email ASC", sql)
537+
538+
// Test mixed ordering with more complex scenario
539+
sb = NewSelectBuilder()
540+
sb.Select("id", "name", "score", "created_at").From("users")
541+
sb.Where(sb.GreaterThan("score", 0))
542+
sb.OrderByDesc("score").OrderByAsc("name").OrderByDesc("created_at")
543+
sql, args := sb.Build()
544+
a.Equal("SELECT id, name, score, created_at FROM users WHERE score > ? ORDER BY score DESC, name ASC, created_at DESC", sql)
545+
a.Equal([]interface{}{0}, args)
546+
547+
// Test that OrderByAsc/OrderByDesc work with table aliases
548+
sb = NewSelectBuilder()
549+
sb.Select("u.id", "u.name", "o.total").From("users u")
550+
sb.Join("orders o", "u.id = o.user_id")
551+
sb.OrderByDesc("o.total").OrderByAsc("u.name")
552+
sql, _ = sb.Build()
553+
a.Equal("SELECT u.id, u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id ORDER BY o.total DESC, u.name ASC", sql)
554+
}

union.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,20 +107,49 @@ func (ub *UnionBuilder) union(opt string, builders ...Builder) *UnionBuilder {
107107
}
108108

109109
// OrderBy sets columns of ORDER BY in SELECT.
110+
//
111+
// Deprecated: Use OrderByAsc or OrderByDesc instead for better support of multiple ORDER BY columns with different directions.
112+
// OrderBy combined with Asc/Desc only supports a single direction for all columns.
110113
func (ub *UnionBuilder) OrderBy(col ...string) *UnionBuilder {
111114
ub.orderByCols = col
112115
ub.marker = unionMarkerAfterOrderBy
113116
return ub
114117
}
115118

119+
// OrderByAsc sets a column of ORDER BY in SELECT with ASC order.
120+
// It supports chaining multiple calls to add multiple ORDER BY columns with different directions.
121+
//
122+
// ub.OrderByAsc("name").OrderByDesc("id")
123+
// // Generates: ORDER BY name ASC, id DESC
124+
func (ub *UnionBuilder) OrderByAsc(col string) *UnionBuilder {
125+
ub.orderByCols = append(ub.orderByCols, col+" ASC")
126+
ub.marker = unionMarkerAfterOrderBy
127+
return ub
128+
}
129+
130+
// OrderByDesc sets a column of ORDER BY in SELECT with DESC order.
131+
// It supports chaining multiple calls to add multiple ORDER BY columns with different directions.
132+
//
133+
// ub.OrderByDesc("id").OrderByAsc("name")
134+
// // Generates: ORDER BY id DESC, name ASC
135+
func (ub *UnionBuilder) OrderByDesc(col string) *UnionBuilder {
136+
ub.orderByCols = append(ub.orderByCols, col+" DESC")
137+
ub.marker = unionMarkerAfterOrderBy
138+
return ub
139+
}
140+
116141
// Asc sets order of ORDER BY to ASC.
142+
//
143+
// Deprecated: Use OrderByAsc instead. Asc only supports a single direction for all ORDER BY columns.
117144
func (ub *UnionBuilder) Asc() *UnionBuilder {
118145
ub.order = "ASC"
119146
ub.marker = unionMarkerAfterOrderBy
120147
return ub
121148
}
122149

123150
// Desc sets order of ORDER BY to DESC.
151+
//
152+
// Deprecated: Use OrderByDesc instead. Desc only supports a single direction for all ORDER BY columns.
124153
func (ub *UnionBuilder) Desc() *UnionBuilder {
125154
ub.order = "DESC"
126155
ub.marker = unionMarkerAfterOrderBy

union_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func ExampleUnion() {
2626
)
2727

2828
ub := Union(sb1, sb2)
29-
ub.OrderBy("created_at").Desc()
29+
ub.OrderByDesc("created_at")
3030

3131
sql, args := ub.Build()
3232
fmt.Println(sql)
@@ -46,7 +46,7 @@ func ExampleUnionAll() {
4646
)
4747

4848
ub := UnionAll(sb, Build("TABLE demo.user_profile"))
49-
ub.OrderBy("created_at").Asc()
49+
ub.OrderByAsc("created_at")
5050
ub.Limit(100).Offset(5)
5151

5252
sql, args := ub.Build()

0 commit comments

Comments
 (0)