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
* Logger Support
* Bind struct with tag
* Iteration Support via sql.Rows
* sql.Scanner
* Iteration Support via [Rows](#row--rows)
* sql.Scanner support
* Every feature comes with tests
* Convention Over Configuration
* 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
```go
@ -762,9 +776,7 @@ db.Where("email = ?", "x@example.org").Attrs(User{RegisteredIp: "111.111.111.111
```
## TODO
* Cache Stmt for performance
* Join, Having, Group, Includes
* Scopes, Valiations
* Scopes, Valiations, Includes, Joins, UpdateColumn/Columns
* AlertColumn, DropColumn, AddIndex, RemoveIndex
# 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 {
defer s.trace(time.Now())
s.prepareQuerySql()
return s.db.db.QueryRow(s.sql, s.sqlVars...)
}
func (s *Do) rows() (*sql.Rows, error) {
defer s.trace(time.Now())
s.prepareQuerySql()
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 {
defer s.trace(time.Now())
s.search = s.search.clone().selects("count(*)")
s.err(s.row().Scan(value))
return s
}
func (s *Do) pluck(column string, value interface{}) *Do {
defer s.trace(time.Now())
dest_out := reflect.Indirect(reflect.ValueOf(value))
s.search = s.search.clone().selects(column)
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 {
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 {

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) {
b.N = 2000
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
}
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 {
return s.clone().search.unscoped().db
}

View File

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