Add method Group, Having

This commit is contained in:
Jinzhu 2013-11-17 13:22:09 +08:00
parent 282b5d872c
commit b41f2957fb
5 changed files with 140 additions and 32 deletions

View File

@ -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
View File

@ -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 {

View File

@ -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
View File

@ -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
} }

View File

@ -7,33 +7,39 @@ import (
) )
type search struct { type search struct {
db *DB db *DB
whereClause []map[string]interface{} whereClause []map[string]interface{}
orClause []map[string]interface{} orClause []map[string]interface{}
notClause []map[string]interface{} notClause []map[string]interface{}
initAttrs []interface{} initAttrs []interface{}
assignAttrs []interface{} assignAttrs []interface{}
orders []string havingClause map[string]interface{}
selectStr string orders []string
offsetStr string joinsStr string
limitStr string selectStr string
tableName string offsetStr string
unscope bool limitStr string
groupStr string
tableName string
unscope bool
} }
func (s *search) clone() *search { func (s *search) clone() *search {
return &search{ return &search{
whereClause: s.whereClause, whereClause: s.whereClause,
orClause: s.orClause, orClause: s.orClause,
notClause: s.notClause, notClause: s.notClause,
initAttrs: s.initAttrs, initAttrs: s.initAttrs,
assignAttrs: s.assignAttrs, assignAttrs: s.assignAttrs,
orders: s.orders, havingClause: s.havingClause,
selectStr: s.selectStr, orders: s.orders,
offsetStr: s.offsetStr, selectStr: s.selectStr,
limitStr: s.limitStr, offsetStr: s.offsetStr,
unscope: s.unscope, limitStr: s.limitStr,
tableName: s.tableName, unscope: s.unscope,
groupStr: s.groupStr,
joinsStr: s.joinsStr,
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