mirror of https://github.com/go-gorm/gorm.git
Add method Group, Having
This commit is contained in:
parent
282b5d872c
commit
b41f2957fb
22
README.md
22
README.md
|
@ -12,8 +12,8 @@ Yet Another ORM library for Go, aims for developer friendly
|
||||||
* Transaction
|
* Transaction
|
||||||
* Logger Support
|
* Logger Support
|
||||||
* Bind struct with tag
|
* Bind struct with tag
|
||||||
* Iteration Support via sql.Rows
|
* Iteration Support via [Rows](#row--rows)
|
||||||
* sql.Scanner
|
* sql.Scanner support
|
||||||
* Every feature comes with tests
|
* Every feature comes with tests
|
||||||
* Convention Over Configuration
|
* Convention Over Configuration
|
||||||
* Developer Friendly
|
* Developer Friendly
|
||||||
|
@ -695,6 +695,20 @@ for rows.Next() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Group & Having
|
||||||
|
|
||||||
|
```go
|
||||||
|
rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
|
||||||
|
for rows.Next() {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
|
||||||
|
for rows.Next() {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Run Raw SQl
|
## Run Raw SQl
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
@ -762,9 +776,7 @@ db.Where("email = ?", "x@example.org").Attrs(User{RegisteredIp: "111.111.111.111
|
||||||
```
|
```
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
* Cache Stmt for performance
|
* Scopes, Valiations, Includes, Joins, UpdateColumn/Columns
|
||||||
* Join, Having, Group, Includes
|
|
||||||
* Scopes, Valiations
|
|
||||||
* AlertColumn, DropColumn, AddIndex, RemoveIndex
|
* AlertColumn, DropColumn, AddIndex, RemoveIndex
|
||||||
|
|
||||||
# Author
|
# Author
|
||||||
|
|
27
do.go
27
do.go
|
@ -342,11 +342,13 @@ func (s *Do) related(value interface{}, foreign_keys ...string) *Do {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Do) row() *sql.Row {
|
func (s *Do) row() *sql.Row {
|
||||||
|
defer s.trace(time.Now())
|
||||||
s.prepareQuerySql()
|
s.prepareQuerySql()
|
||||||
return s.db.db.QueryRow(s.sql, s.sqlVars...)
|
return s.db.db.QueryRow(s.sql, s.sqlVars...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Do) rows() (*sql.Rows, error) {
|
func (s *Do) rows() (*sql.Rows, error) {
|
||||||
|
defer s.trace(time.Now())
|
||||||
s.prepareQuerySql()
|
s.prepareQuerySql()
|
||||||
return s.db.db.Query(s.sql, s.sqlVars...)
|
return s.db.db.Query(s.sql, s.sqlVars...)
|
||||||
}
|
}
|
||||||
|
@ -409,15 +411,12 @@ func (s *Do) query() *Do {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Do) count(value interface{}) *Do {
|
func (s *Do) count(value interface{}) *Do {
|
||||||
defer s.trace(time.Now())
|
|
||||||
s.search = s.search.clone().selects("count(*)")
|
s.search = s.search.clone().selects("count(*)")
|
||||||
s.err(s.row().Scan(value))
|
s.err(s.row().Scan(value))
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Do) pluck(column string, value interface{}) *Do {
|
func (s *Do) pluck(column string, value interface{}) *Do {
|
||||||
defer s.trace(time.Now())
|
|
||||||
|
|
||||||
dest_out := reflect.Indirect(reflect.ValueOf(value))
|
dest_out := reflect.Indirect(reflect.ValueOf(value))
|
||||||
s.search = s.search.clone().selects(column)
|
s.search = s.search.clone().selects(column)
|
||||||
if dest_out.Kind() != reflect.Slice {
|
if dest_out.Kind() != reflect.Slice {
|
||||||
|
@ -634,8 +633,28 @@ func (s *Do) offsetSql() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Do) groupSql() string {
|
||||||
|
if len(s.search.groupStr) == 0 {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return " GROUP BY " + s.search.groupStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Do) havingSql() string {
|
||||||
|
if s.search.havingClause == nil {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return " HAVING " + s.buildWhereCondition(s.search.havingClause)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Do) joinsSql() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Do) combinedSql() string {
|
func (s *Do) combinedSql() string {
|
||||||
return s.whereSql() + s.orderSql() + s.limitSql() + s.offsetSql()
|
return s.whereSql() + s.orderSql() + s.limitSql() + s.offsetSql() + s.groupSql() + s.havingSql()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Do) createTable() *Do {
|
func (s *Do) createTable() *Do {
|
||||||
|
|
36
gorm_test.go
36
gorm_test.go
|
@ -1391,6 +1391,42 @@ func TestRows(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGroup(t *testing.T) {
|
||||||
|
rows, err := db.Select("name").Table("users").Group("name").Rows()
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
var name string
|
||||||
|
rows.Scan(&name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("Should not raise any error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHaving(t *testing.T) {
|
||||||
|
rows, err := db.Debug().Select("name, count(*) as total").Table("users").Group("name").Having("name IN (?)", []string{"2", "3"}).Rows()
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
var name string
|
||||||
|
var total int64
|
||||||
|
rows.Scan(&name, &total)
|
||||||
|
|
||||||
|
if name == "2" && total != 1 {
|
||||||
|
t.Errorf("Should have one user having name 2", total)
|
||||||
|
}
|
||||||
|
if name == "3" && total != 2 {
|
||||||
|
t.Errorf("Should have two users having name 3", total)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("Should not raise any error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkGorm(b *testing.B) {
|
func BenchmarkGorm(b *testing.B) {
|
||||||
b.N = 2000
|
b.N = 2000
|
||||||
for x := 0; x < b.N; x++ {
|
for x := 0; x < b.N; x++ {
|
||||||
|
|
16
main.go
16
main.go
|
@ -77,6 +77,22 @@ func (s *DB) Select(value interface{}) *DB {
|
||||||
return s.clone().search.selects(value).db
|
return s.clone().search.selects(value).db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DB) Group(query string) *DB {
|
||||||
|
return s.clone().search.group(query).db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DB) Having(query string, values ...interface{}) *DB {
|
||||||
|
return s.clone().search.having(query, values...).db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DB) Joins(query string) *DB {
|
||||||
|
return s.clone().search.joins(query).db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DB) Includes(value interface{}) *DB {
|
||||||
|
return s.clone().search.includes(value).db
|
||||||
|
}
|
||||||
|
|
||||||
func (s *DB) Unscoped() *DB {
|
func (s *DB) Unscoped() *DB {
|
||||||
return s.clone().search.unscoped().db
|
return s.clone().search.unscoped().db
|
||||||
}
|
}
|
||||||
|
|
25
search.go
25
search.go
|
@ -13,10 +13,13 @@ type search struct {
|
||||||
notClause []map[string]interface{}
|
notClause []map[string]interface{}
|
||||||
initAttrs []interface{}
|
initAttrs []interface{}
|
||||||
assignAttrs []interface{}
|
assignAttrs []interface{}
|
||||||
|
havingClause map[string]interface{}
|
||||||
orders []string
|
orders []string
|
||||||
|
joinsStr string
|
||||||
selectStr string
|
selectStr string
|
||||||
offsetStr string
|
offsetStr string
|
||||||
limitStr string
|
limitStr string
|
||||||
|
groupStr string
|
||||||
tableName string
|
tableName string
|
||||||
unscope bool
|
unscope bool
|
||||||
}
|
}
|
||||||
|
@ -28,11 +31,14 @@ func (s *search) clone() *search {
|
||||||
notClause: s.notClause,
|
notClause: s.notClause,
|
||||||
initAttrs: s.initAttrs,
|
initAttrs: s.initAttrs,
|
||||||
assignAttrs: s.assignAttrs,
|
assignAttrs: s.assignAttrs,
|
||||||
|
havingClause: s.havingClause,
|
||||||
orders: s.orders,
|
orders: s.orders,
|
||||||
selectStr: s.selectStr,
|
selectStr: s.selectStr,
|
||||||
offsetStr: s.offsetStr,
|
offsetStr: s.offsetStr,
|
||||||
limitStr: s.limitStr,
|
limitStr: s.limitStr,
|
||||||
unscope: s.unscope,
|
unscope: s.unscope,
|
||||||
|
groupStr: s.groupStr,
|
||||||
|
joinsStr: s.joinsStr,
|
||||||
tableName: s.tableName,
|
tableName: s.tableName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,6 +92,25 @@ func (s *search) offset(value interface{}) *search {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *search) group(query string) *search {
|
||||||
|
s.groupStr = s.getInterfaceAsSql(query)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *search) having(query string, values ...interface{}) *search {
|
||||||
|
s.havingClause = map[string]interface{}{"query": query, "args": values}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *search) includes(value interface{}) *search {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *search) joins(query string) *search {
|
||||||
|
s.joinsStr = query
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
func (s *search) unscoped() *search {
|
func (s *search) unscoped() *search {
|
||||||
s.unscope = true
|
s.unscope = true
|
||||||
return s
|
return s
|
||||||
|
|
Loading…
Reference in New Issue