diff --git a/.gitignore b/.gitignore index 22486d5f..314fdca9 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ # Folders _obj _test +vendor # Architecture specific extensions/prefixes *.[568vq] diff --git a/engine.go b/engine.go index 846889c3..2e1ce1f4 100644 --- a/engine.go +++ b/engine.go @@ -1417,6 +1417,16 @@ func (engine *Engine) Find(beans interface{}, condiBeans ...interface{}) error { return session.Find(beans, condiBeans...) } +// Pluck retrieve a list of column values. +// colName is the column name where values from +// slicePtr should be a slice which will filled by the list values +// beans is optional which represents the table, should be a *Struct +func (engine *Engine) Pluck(colName string, slicePtr interface{}, beans ...interface{}) error { + session := engine.NewSession() + defer session.Close() + return session.Pluck(colName, slicePtr, beans...) +} + // FindAndCount find the results and also return the counts func (engine *Engine) FindAndCount(rowsSlicePtr interface{}, condiBean ...interface{}) (int64, error) { session := engine.NewSession() diff --git a/go.mod b/go.mod index 4510e93c..732fabb4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,25 @@ -module "github.com/go-xorm/xorm" +module github.com/go-xorm/xorm require ( - "github.com/go-xorm/builder" v0.0.0-20180322150003-a9b7ffcca3f0 - "github.com/go-xorm/core" v0.0.0-20180322150003-0177c08cee88 + cloud.google.com/go v0.25.0 + github.com/cockroachdb/apd v1.0.0 + github.com/denisenkom/go-mssqldb v0.0.0-20180707235734-242fa5aa1b45 + github.com/go-sql-driver/mysql v1.4.0 + github.com/go-xorm/builder v0.0.0-20180322150003-a9b7ffcca3f0 + github.com/go-xorm/core v0.0.0-20180322150003-0177c08cee88 + github.com/google/go-cmp v0.2.0 + github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 + github.com/jackc/pgx v0.0.0-20180724145307-93ee40e691de + github.com/kr/pretty v0.1.0 + github.com/lib/pq v0.0.0-20180523175426-90697d60dd84 + github.com/mattn/go-sqlite3 v1.9.0 + github.com/pkg/errors v0.8.0 + github.com/satori/go.uuid v1.2.0 + github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 + github.com/stretchr/testify v1.2.2 + github.com/ziutek/mymysql v1.5.4 + golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb + google.golang.org/appengine v1.1.0 + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 + gopkg.in/stretchr/testify.v1 v1.2.2 ) diff --git a/interface.go b/interface.go index 0bc12ba0..3a634022 100644 --- a/interface.go +++ b/interface.go @@ -64,6 +64,7 @@ type Interface interface { Update(bean interface{}, condiBeans ...interface{}) (int64, error) UseBool(...string) *Session Where(interface{}, ...interface{}) *Session + Pluck(colName string, slicePtr interface{}, bean ...interface{}) error } // EngineInterface defines the interface which Engine, EngineGroup will implementate. diff --git a/session_pluck.go b/session_pluck.go new file mode 100644 index 00000000..663673a6 --- /dev/null +++ b/session_pluck.go @@ -0,0 +1,61 @@ +package xorm + +import ( + "errors" + "fmt" + "reflect" + "strings" +) + +// Pluck retrieve a list of column values. +// colName is the column name where values from +// slicePtr should be a slice which will filled by the list values +// beans is optional which represents the table, should be a *Struct +func (session *Session) Pluck(colName string, slicePtr interface{}, beans ...interface{}) error { + if session.isAutoClose { + defer session.Close() + } + return session.pluck(colName, slicePtr, beans...) +} + +func (session *Session) pluck(colName string, slicePtr interface{}, beans ...interface{}) error { + sliceValue := reflect.Indirect(reflect.ValueOf(slicePtr)) + if sliceValue.Kind() != reflect.Slice { + return errors.New("needs a pointer to a slice") + } + sliceElemType := sliceValue.Type().Elem() + rows := []map[string]interface{}{} + query := session + if session.statement.selectStr == "" { + query.Select(colName) + } + if len(beans) != 0 { + // find table + bean := beans[0] + beanValue := reflect.ValueOf(bean) + if beanValue.Kind() != reflect.Ptr || beanValue.Elem().Kind() != reflect.Struct { + return errors.New("needs a pointer to a struct") + } + session.statement.setRefValue(beanValue) + tbName := session.statement.TableName() + query.Table(tbName) + } + if err := query.Find(&rows); err != nil { + return err + } + + colSegs := strings.Split(colName, ".") + col := colSegs[len(colSegs)-1] // last one + col = strings.Trim(col, "`") // unwrap column name + nSlice := reflect.New(sliceValue.Type()).Elem() + rowsLen := len(rows) + for i := 0; i < rowsLen; i++ { + if v, ok := rows[i][col]; !ok { + return fmt.Errorf("cannot find column: %s", col) + } else { + nSlice = reflect.Append(nSlice, reflect.ValueOf(v).Convert(sliceElemType)) + } + } + sliceValue.Set(nSlice) + return nil +} diff --git a/session_pluck_test.go b/session_pluck_test.go new file mode 100644 index 00000000..b5c4408c --- /dev/null +++ b/session_pluck_test.go @@ -0,0 +1,103 @@ +package xorm + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestPluck(t *testing.T) { + var err error + assert.NoError(t, prepareEngine()) + assertSync(t, new(Userinfo)) + _, err = testEngine.Insert([]Userinfo{ + { + Username: "Lenny", + }, + { + Username: "Jimmy", + }, + { + Username: "John Wick", + }, + }) + assert.NoError(t, err) + var ids []int64 + err = testEngine.Table("userinfo").Alias("u").Pluck("u.id", &ids) + assert.NoError(t, err) + t.Logf("%+v", ids) + + err = testEngine.Table("userinfo").Alias("u").Pluck("`u`.`id`", &ids) + assert.NoError(t, err) + t.Logf("%+v", ids) + + var names []string + err = testEngine.Table("userinfo").Pluck("username", &names) + assert.NoError(t, err) + t.Logf("%+v", names) +} + +func TestPluck2(t *testing.T) { + var err error + assert.NoError(t, prepareEngine()) + assertSync(t, new(Userinfo)) + _, err = testEngine.Insert([]Userinfo{ + { + Username: "Lenny", + }, + { + Username: "Jimmy", + }, + { + Username: "John Wick", + }, + }) + assert.NoError(t, err) + var names []string + err = testEngine.Pluck("username", &names, new(Userinfo)) + assert.NoError(t, err) + t.Logf("%+v", names) +} + +func TestPluck3(t *testing.T) { + var err error + assert.NoError(t, prepareEngine()) + assertSync(t, new(Userinfo)) + _, err = testEngine.Insert([]Userinfo{ + { + Username: "Lenny", + }, + { + Username: "Jimmy", + }, + { + Username: "John Wick", + }, + }) + assert.NoError(t, err) + var names []string + err = testEngine.Table("userinfo").Pluck("username", &names, new(Team)) + assert.NoError(t, err) + t.Logf("%+v", names) +} + +func TestPluck4(t *testing.T) { + var err error + assert.NoError(t, prepareEngine()) + assertSync(t, new(Userinfo)) + _, err = testEngine.Insert([]Userinfo{ + { + Username: "Lenny", + }, + { + Username: "Jimmy", + }, + { + Username: "John Wick", + }, + }) + assert.NoError(t, err) + var names []string + err = testEngine.Table("userinfo").Select("id, username as name").Pluck("name", &names, new(Team)) + assert.NoError(t, err) + t.Logf("%+v", names) +}