Skip to content

Commit

Permalink
Add additional methods of specifying the 'select' portion of a query.
Browse files Browse the repository at this point in the history
This commit adds more ways of specifying selects:

-) You can now pass in a []string.  This is mostly for convenience,
since you may want to dynamically create a list of fields to be
selected.

-) You can now use variables.  This is important because a select
could take user input.  For example, finding a MAX between a record
and a given number could be easily done using select, and then
you don't have to process anything in backend logic.  This is also
necessary to use postgres text search capabilities (which actaully
play nicely with the rest of gorm).

-) You can now chain select calls.  This could be useful in
conjunction with gorm's scopes functionality.
  • Loading branch information
jnfeinstein committed Nov 17, 2014
1 parent 62b447b commit 429a100
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 10 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,12 @@ db.Where(User{Name: "jinzhu"}).Assign(User{Age: 30}).FirstOrCreate(&user)
```go
db.Select("name, age").Find(&users)
//// SELECT name, age FROM users;

db.Select([]string{"name", "age"}).Find(&users)
//// SELECT name, age FROM users;

db.Table("users").Select("COALESCE(age,?)", 42).Rows()
//// SELECT COALESCE(age,'42') FROM users;
```

## Order
Expand Down
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ func (s *DB) Order(value string, reorder ...bool) *DB {
return s.clone().search.order(value, reorder...).db
}

func (s *DB) Select(value interface{}) *DB {
return s.clone().search.selects(value).db
func (s *DB) Select(query interface{}, args ...interface{}) *DB {
return s.clone().search.selects(query, args...).db
}

func (s *DB) Group(query string) *DB {
Expand Down
23 changes: 23 additions & 0 deletions query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/jinzhu/now"

"math/rand"
"testing"
"time"
)
Expand Down Expand Up @@ -537,3 +538,25 @@ func TestSelectWithEscapedFieldName(t *testing.T) {
t.Errorf("Expected 3 name, but got: %d", len(names))
}
}

func TestSelectWithVariables(t *testing.T) {
DB.Save(&User{Name: "jinzhu"})

randomNum := rand.Intn(1000000000)
rows, _ := DB.Table("users").Select("? as fake", randomNum).Where("fake = ?", randomNum).Rows()

if !rows.Next() {
t.Errorf("Should have returned at least one row")
}
}

func TestSelectWithArrayInput(t *testing.T) {
DB.Save(&User{Name: "jinzhu", Age: 42})

var user User
DB.Select([]string{"name", "age"}).Where("age = 42 AND name = 'jinzhu'").First(&user)

if user.Name != "jinzhu" || user.Age != 42 {
t.Errorf("Should have selected both age and name")
}
}
41 changes: 38 additions & 3 deletions scope_private.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,34 @@ func (scope *Scope) buildNotCondition(clause map[string]interface{}) (str string
return
}

func (scope *Scope) buildSelectQuery(clause map[string]interface{}) (str string) {
switch value := clause["query"].(type) {
case string:
str = value
case []string:
str = strings.Join(value, ", ")
}

args := clause["args"].([]interface{})
for _, arg := range args {
switch reflect.TypeOf(arg).Kind() {
case reflect.Slice:
values := reflect.ValueOf(arg)
var tempMarks []string
for i := 0; i < values.Len(); i++ {
tempMarks = append(tempMarks, scope.AddToVars(values.Index(i).Interface()))
}
str = strings.Replace(str, "?", strings.Join(tempMarks, ","), 1)
default:
if valuer, ok := interface{}(arg).(driver.Valuer); ok {
arg, _ = valuer.Value()
}
str = strings.Replace(str, "?", scope.AddToVars(arg), 1)
}
}
return
}

func (scope *Scope) where(where ...interface{}) {
if len(where) > 0 {
scope.Search = scope.Search.clone().where(where[0], where[1:]...)
Expand Down Expand Up @@ -180,11 +208,18 @@ func (scope *Scope) whereSql() (sql string) {
}

func (s *Scope) selectSql() string {
if len(s.Search.Select) == 0 {
if len(s.Search.Selects) == 0 {
return "*"
} else {
return s.Search.Select
}

var selectQueries []string

for _, clause := range s.Search.Selects {
selectQueries = append(selectQueries, s.buildSelectQuery(clause))
}

return strings.Join(selectQueries, ", ")

}

func (s *Scope) orderSql() string {
Expand Down
8 changes: 4 additions & 4 deletions search.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type search struct {
HavingCondition map[string]interface{}
Orders []string
Joins string
Select string
Selects []map[string]interface{}
Offset string
Limit string
Group string
Expand All @@ -30,7 +30,7 @@ func (s *search) clone() *search {
AssignAttrs: s.AssignAttrs,
HavingCondition: s.HavingCondition,
Orders: s.Orders,
Select: s.Select,
Selects: s.Selects,
Offset: s.Offset,
Limit: s.Limit,
Unscope: s.Unscope,
Expand Down Expand Up @@ -75,8 +75,8 @@ func (s *search) order(value string, reorder ...bool) *search {
return s
}

func (s *search) selects(value interface{}) *search {
s.Select = s.getInterfaceAsSql(value)
func (s *search) selects(query interface{}, args ...interface{}) *search {
s.Selects = append(s.Selects, map[string]interface{}{"query": query, "args": args})
return s
}

Expand Down
2 changes: 1 addition & 1 deletion search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestCloneSearch(t *testing.T) {
t.Errorf("InitAttrs should be copied")
}

if reflect.DeepEqual(s.Select, s1.Select) {
if reflect.DeepEqual(s.Selects, s1.Selects) {
t.Errorf("selectStr should be copied")
}
}

0 comments on commit 429a100

Please sign in to comment.