diff --git a/.example/frame/mvc/app/model/article/article_model.go b/.example/frame/mvc/app/model/article/article_model.go index 4b9234220..021f5a3ae 100644 --- a/.example/frame/mvc/app/model/article/article_model.go +++ b/.example/frame/mvc/app/model/article/article_model.go @@ -21,65 +21,53 @@ var ( Table = "gf_article" // Model is the model object of gf_article. Model = &arModel{g.DB("default").Table(Table).Safe()} - // Primary is the primary key name of table gf_article. - Primary = gdb.GetPrimaryKey(new(Entity)) ) -// FindOne retrieves and returns a single Entity by a primary key or where conditions by -// Model.Where. The optional parameter is like follows: -// 123, []int{1, 2, 3}, "john", []string{"john", "smith"} -// g.Map{"id": g.Slice{1,2,3}}, g.Map{"id": 1, "name": "john"}, etc. -// -// Note that it differs with Mode.One is that any single where condition parameter will -// be treated as the value of the primary key in FindOne, but a string condition in -// Model.One. That is, if primary key is "id" and given parameter as "123", the -// FindOne function treats it as "id=123", but Model.One treats it as "123", just a string. +// FindOne is a convenience method for Model.FindOne. +// See Model.FindOne. func FindOne(where ...interface{}) (*Entity, error) { - return Model.One(gdb.GetPrimaryKeyCondition(Primary, where...)...) + return Model.FindOne(where...) } -// FindAll retrieves and returns multiple Entity by a primary key or where conditions by -// Model.Where. Also see FindOne. +// FindAll is a convenience method for Model.FindAll. +// See Model.FindAll. func FindAll(where ...interface{}) ([]*Entity, error) { - return Model.All(gdb.GetPrimaryKeyCondition(Primary, where...)...) + return Model.FindAll(where...) } -// FindValue retrieves and returns single field value by a primary key or where conditions by -// Model.Where. Also see FindOne. +// FindValue is a convenience method for Model.FindValue. +// See Model.FindValue. func FindValue(fieldsAndWhere ...interface{}) (gdb.Value, error) { - if len(fieldsAndWhere) == 2 { - return Model.Value(append(fieldsAndWhere[0:1], gdb.GetPrimaryKeyCondition(Primary, fieldsAndWhere[1:]...)...)...) - } - return Model.Value(fieldsAndWhere...) + return Model.FindValue(fieldsAndWhere...) } -// FindCount retrieves and returns the record number by a primary key or where conditions by -// Model.Where. Also see FindOne. +// FindCount is a convenience method for Model.FindCount. +// See Model.FindCount. func FindCount(where ...interface{}) (int, error) { - return Model.Count(gdb.GetPrimaryKeyCondition(Primary, where...)...) + return Model.FindCount(where...) } -// Insert is alias of Model.Insert. +// Insert is a convenience method for Model.Insert. func Insert(data ...interface{}) (result sql.Result, err error) { return Model.Insert(data...) } -// Replace is alias of Model.Replace. +// Replace is a convenience method for Model.Replace. func Replace(data ...interface{}) (result sql.Result, err error) { return Model.Replace(data...) } -// Save is alias of Model.Save. +// Save is a convenience method for Model.Save. func Save(data ...interface{}) (result sql.Result, err error) { return Model.Save(data...) } -// Update is alias of Model.Update. +// Update is a convenience method for Model.Update. func Update(dataAndWhere ...interface{}) (result sql.Result, err error) { return Model.Update(dataAndWhere...) } -// Delete is alias of Model.Delete. +// Delete is a convenience method for Model.Delete. func Delete(where ...interface{}) (result sql.Result, err error) { return Model.Delete(where...) } @@ -166,14 +154,14 @@ func (m *arModel) Or(where interface{}, args ...interface{}) *arModel { return &arModel{m.M.Or(where, args...)} } -// GroupBy sets the "GROUP BY" statement for the model. -func (m *arModel) GroupBy(groupBy string) *arModel { - return &arModel{m.M.GroupBy(groupBy)} +// Group sets the "GROUP BY" statement for the model. +func (m *arModel) Group(groupBy string) *arModel { + return &arModel{m.M.Group(groupBy)} } -// OrderBy sets the "ORDER BY" statement for the model. -func (m *arModel) OrderBy(orderBy string) *arModel { - return &arModel{m.M.OrderBy(orderBy)} +// Order sets the "ORDER BY" statement for the model. +func (m *arModel) Order(orderBy string) *arModel { + return &arModel{m.M.Order(orderBy)} } // Limit sets the "LIMIT" statement for the model. @@ -190,11 +178,11 @@ func (m *arModel) Offset(offset int) *arModel { return &arModel{m.M.Offset(offset)} } -// ForPage sets the paging number for the model. +// Page sets the paging number for the model. // The parameter is started from 1 for paging. // Note that, it differs that the Limit function start from 0 for "LIMIT" statement. -func (m *arModel) ForPage(page, limit int) *arModel { - return &arModel{m.M.ForPage(page, limit)} +func (m *arModel) Page(page, limit int) *arModel { + return &arModel{m.M.Page(page, limit)} } // Batch sets the batch operation number for the model. @@ -321,6 +309,46 @@ func (m *arModel) Value(fieldsAndWhere ...interface{}) (gdb.Value, error) { return m.M.Value(fieldsAndWhere...) } +// FindOne retrieves and returns a single Record by Model.WherePri and Model.One. +// Also see Model.WherePri and Model.One. +func (m *arModel) FindOne(where ...interface{}) (*Entity, error) { + one, err := m.M.FindOne(where...) + if err != nil { + return nil, err + } + var entity *Entity + if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entity, nil +} + +// FindAll retrieves and returns Result by by Model.WherePri and Model.All. +// Also see Model.WherePri and Model.All. +func (m *arModel) FindAll(where ...interface{}) ([]*Entity, error) { + all, err := m.M.FindAll(where...) + if err != nil { + return nil, err + } + var entities []*Entity + if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entities, nil +} + +// FindValue retrieves and returns single field value by Model.WherePri and Model.Value. +// Also see Model.WherePri and Model.Value. +func (m *arModel) FindValue(fieldsAndWhere ...interface{}) (gdb.Value, error) { + return m.M.FindValue(fieldsAndWhere...) +} + +// FindCount retrieves and returns the record number by Model.WherePri and Model.Count. +// Also see Model.WherePri and Model.Count. +func (m *arModel) FindCount(where ...interface{}) (int, error) { + return m.M.FindCount(where...) +} + // Chunk iterates the table with given size and callback function. func (m *arModel) Chunk(limit int, callback func(entities []*Entity, err error) bool) { m.M.Chunk(limit, func(result gdb.Result, err error) bool { diff --git a/.example/other/test.go b/.example/other/test.go index 2d29742f5..08c55a582 100644 --- a/.example/other/test.go +++ b/.example/other/test.go @@ -1,48 +1,13 @@ package main import ( - "container/list" "fmt" - "github.com/gogf/gf/container/garray" - "github.com/gogf/gf/container/glist" + "github.com/gogf/gf/text/gstr" ) func main() { - // concurrent-safe list. - l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true) - // iterate reading from head. - l.RLockFunc(func(list *list.List) { - length := list.Len() - if length > 0 { - for i, e := 0, list.Front(); i < length; i, e = i+1, e.Next() { - fmt.Print(e.Value) - } - } - }) - fmt.Println() - // iterate reading from tail. - l.RLockFunc(func(list *list.List) { - length := list.Len() - if length > 0 { - for i, e := 0, list.Back(); i < length; i, e = i+1, e.Prev() { - fmt.Print(e.Value) - } - } - }) - - fmt.Println() - - // iterate writing from head. - l.LockFunc(func(list *list.List) { - length := list.Len() - if length > 0 { - for i, e := 0, list.Front(); i < length; i, e = i+1, e.Next() { - if e.Value == 6 { - e.Value = "M" - break - } - } - } - }) - fmt.Println(l) + a := "aaaaa_post" + b := "aaaaa_" + c := gstr.TrimLeftStr(a, b) + fmt.Println(c) } diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 1f7734993..f07f1b106 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -222,7 +222,7 @@ func New(name ...string) (db DB, err error) { return nil, err } } else { - return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", group)) + return nil, errors.New(fmt.Sprintf(`database configuration node "%s" is not found`, group)) } } diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 89ed4eed2..9a0028d91 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -156,7 +156,6 @@ func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondi } } return where - } // 获得orm标签与属性的映射关系 diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index b36d59042..d2b7b58eb 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -10,14 +10,13 @@ import ( "database/sql" "errors" "fmt" + "github.com/gogf/gf/container/gmap" "reflect" "time" "github.com/gogf/gf/container/gset" "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/container/gmap" - "github.com/gogf/gf/util/gconv" ) @@ -289,6 +288,18 @@ func (m *Model) Where(where interface{}, args ...interface{}) *Model { return model } +// WherePri does the same logic as Model.Where except that if the parameter +// is a single condition like int/string/float/slice, it treats the condition as the primary +// key value. That is, if primary key is "id" and given parameter as "123", the +// WherePri function treats it as "id=123", but Model.Where treats it as string "123". +func (m *Model) WherePri(where interface{}, args ...interface{}) *Model { + if len(args) > 0 { + return m.Where(where, args...) + } + newWhere := GetPrimaryKeyCondition(m.getPrimaryKey(), where) + return m.Where(newWhere[0], newWhere[1:]...) +} + // And adds "AND" condition to the where statement. func (m *Model) And(where interface{}, args ...interface{}) *Model { model := m.getModel() @@ -317,20 +328,34 @@ func (m *Model) Or(where interface{}, args ...interface{}) *Model { return model } -// GroupBy sets the "GROUP BY" statement for the model. -func (m *Model) GroupBy(groupBy string) *Model { +// Group sets the "GROUP BY" statement for the model. +func (m *Model) Group(groupBy string) *Model { model := m.getModel() model.groupBy = m.db.quoteString(groupBy) return model } -// OrderBy sets the "ORDER BY" statement for the model. -func (m *Model) OrderBy(orderBy string) *Model { +// GroupBy is alias of Model.Group. +// See Model.Group. +// Deprecated. +func (m *Model) GroupBy(groupBy string) *Model { + return m.Group(groupBy) +} + +// Order sets the "ORDER BY" statement for the model. +func (m *Model) Order(orderBy string) *Model { model := m.getModel() model.orderBy = m.db.quoteString(orderBy) return model } +// OrderBy is alias of Model.Order. +// See Model.Order. +// Deprecated. +func (m *Model) OrderBy(orderBy string) *Model { + return m.Order(orderBy) +} + // Limit sets the "LIMIT" statement for the model. // The parameter can be either one or two number, if passed two number is passed, // it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]" @@ -355,16 +380,23 @@ func (m *Model) Offset(offset int) *Model { return model } -// ForPage sets the paging number for the model. +// Page sets the paging number for the model. // The parameter is started from 1 for paging. // Note that, it differs that the Limit function start from 0 for "LIMIT" statement. -func (m *Model) ForPage(page, limit int) *Model { +func (m *Model) Page(page, limit int) *Model { model := m.getModel() model.start = (page - 1) * limit model.limit = limit return model } +// ForPage is alias of Model.Page. +// See Model.Page. +// Deprecated. +func (m *Model) ForPage(page, limit int) *Model { + return m.Page(page, limit) +} + // Batch sets the batch operation number for the model. func (m *Model) Batch(batch int) *Model { model := m.getModel() @@ -446,50 +478,6 @@ func (m *Model) Data(data ...interface{}) *Model { return model } -// filterDataForInsertOrUpdate does filter feature with data for inserting/updating operations. -// Note that, it does not filter list item, which is also type of map, for "omit empty" feature. -func (m *Model) filterDataForInsertOrUpdate(data interface{}) interface{} { - if list, ok := m.data.(List); ok { - for k, item := range list { - list[k] = m.doFilterDataMapForInsertOrUpdate(item, false) - } - return list - } else if item, ok := m.data.(Map); ok { - return m.doFilterDataMapForInsertOrUpdate(item, true) - } - return data -} - -// doFilterDataMapForInsertOrUpdate does the filter features for map. -// Note that, it does not filter list item, which is also type of map, for "omit empty" feature. -func (m *Model) doFilterDataMapForInsertOrUpdate(data Map, allowOmitEmpty bool) Map { - if m.filter { - data = m.db.filterFields(m.tables, data) - } - // Remove key-value pairs of which the value is empty. - if allowOmitEmpty && m.option&OPTION_OMITEMPTY > 0 { - m := gmap.NewStrAnyMapFrom(data) - m.FilterEmpty() - data = m.Map() - } - - if len(m.fields) > 0 && m.fields != "*" { - // Keep specified fields. - set := gset.NewStrSetFrom(gstr.SplitAndTrim(m.fields, ",")) - for k := range data { - if !set.Contains(k) { - delete(data, k) - } - } - } else if len(m.fieldsEx) > 0 { - // Filter specified fields. - for _, v := range gstr.SplitAndTrim(m.fieldsEx, ",") { - delete(data, v) - } - } - return data -} - // Insert does "INSERT INTO ..." statement for the model. // The optional parameter is the same as the parameter of Model.Data function, // see Model.Data. @@ -637,7 +625,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro if m.data == nil { return nil, errors.New("updating table with empty data") } - condition, conditionArgs := m.formatCondition() + condition, conditionArgs := m.formatCondition(false) return m.db.doUpdate( m.getLink(), m.tables, @@ -659,7 +647,7 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) { m.checkAndRemoveCache() } }() - condition, conditionArgs := m.formatCondition() + condition, conditionArgs := m.formatCondition(false) return m.db.doDelete(m.getLink(), m.tables, condition, conditionArgs...) } @@ -680,7 +668,7 @@ func (m *Model) All(where ...interface{}) (Result, error) { if len(where) > 0 { return m.Where(where[0], where[1:]...).All() } - condition, conditionArgs := m.formatCondition() + condition, conditionArgs := m.formatCondition(false) return m.getAll(fmt.Sprintf("SELECT %s FROM %s%s", m.fields, m.tables, condition), conditionArgs...) } @@ -690,12 +678,16 @@ func (m *Model) All(where ...interface{}) (Result, error) { // The optional parameter is the same as the parameter of Model.Where function, // see Model.Where. func (m *Model) One(where ...interface{}) (Record, error) { - list, err := m.All(where...) + if len(where) > 0 { + return m.Where(where[0], where[1:]...).One() + } + condition, conditionArgs := m.formatCondition(true) + all, err := m.getAll(fmt.Sprintf("SELECT %s FROM %s%s", m.fields, m.tables, condition), conditionArgs...) if err != nil { return nil, err } - if len(list) > 0 { - return list[0], nil + if len(all) > 0 { + return all[0], nil } return nil, nil } @@ -833,7 +825,7 @@ func (m *Model) Count(where ...interface{}) (int, error) { } else { m.fields = fmt.Sprintf(`COUNT(%s)`, m.fields) } - condition, conditionArgs := m.formatCondition() + condition, conditionArgs := m.formatCondition(false) s := fmt.Sprintf("SELECT %s FROM %s %s", m.fields, m.tables, condition) if len(m.groupBy) > 0 { s = fmt.Sprintf("SELECT COUNT(1) FROM (%s) count_alias", s) @@ -850,6 +842,116 @@ func (m *Model) Count(where ...interface{}) (int, error) { return 0, nil } +// FindOne retrieves and returns a single Record by Model.WherePri and Model.One. +// Also see Model.WherePri and Model.One. +func (m *Model) FindOne(where ...interface{}) (Record, error) { + if len(where) > 0 { + return m.WherePri(where[0], where[1:]...).One() + } + return m.One() +} + +// FindAll retrieves and returns Result by by Model.WherePri and Model.All. +// Also see Model.WherePri and Model.All. +func (m *Model) FindAll(where ...interface{}) (Result, error) { + if len(where) > 0 { + return m.WherePri(where[0], where[1:]...).All() + } + return m.All() +} + +// FindValue retrieves and returns single field value by Model.WherePri and Model.Value. +// Also see Model.WherePri and Model.Value. +func (m *Model) FindValue(fieldsAndWhere ...interface{}) (Value, error) { + if len(fieldsAndWhere) >= 2 { + return m.WherePri(fieldsAndWhere[1], fieldsAndWhere[2:]...).Fields(gconv.String(fieldsAndWhere[0])).Value() + } + if len(fieldsAndWhere) == 1 { + return m.Fields(gconv.String(fieldsAndWhere[0])).Value() + } + return m.Value() +} + +// FindCount retrieves and returns the record number by Model.WherePri and Model.Count. +// Also see Model.WherePri and Model.Count. +func (m *Model) FindCount(where ...interface{}) (int, error) { + if len(where) > 0 { + return m.WherePri(where[0], where[1:]...).Count() + } + return m.Count() +} + +// Chunk iterates the table with given size and callback function. +func (m *Model) Chunk(limit int, callback func(result Result, err error) bool) { + page := m.start + if page == 0 { + page = 1 + } + model := m + for { + model = model.Page(page, limit) + data, err := model.All() + if err != nil { + callback(nil, err) + break + } + if len(data) == 0 { + break + } + if callback(data, err) == false { + break + } + if len(data) < limit { + break + } + page++ + } +} + +// filterDataForInsertOrUpdate does filter feature with data for inserting/updating operations. +// Note that, it does not filter list item, which is also type of map, for "omit empty" feature. +func (m *Model) filterDataForInsertOrUpdate(data interface{}) interface{} { + if list, ok := m.data.(List); ok { + for k, item := range list { + list[k] = m.doFilterDataMapForInsertOrUpdate(item, false) + } + return list + } else if item, ok := m.data.(Map); ok { + return m.doFilterDataMapForInsertOrUpdate(item, true) + } + return data +} + +// doFilterDataMapForInsertOrUpdate does the filter features for map. +// Note that, it does not filter list item, which is also type of map, for "omit empty" feature. +func (m *Model) doFilterDataMapForInsertOrUpdate(data Map, allowOmitEmpty bool) Map { + if m.filter { + data = m.db.filterFields(m.tables, data) + } + // Remove key-value pairs of which the value is empty. + if allowOmitEmpty && m.option&OPTION_OMITEMPTY > 0 { + m := gmap.NewStrAnyMapFrom(data) + m.FilterEmpty() + data = m.Map() + } + + if len(m.fields) > 0 && m.fields != "*" { + // Keep specified fields. + set := gset.NewStrSetFrom(gstr.SplitAndTrim(m.fields, ",")) + for k := range data { + if !set.Contains(k) { + delete(data, k) + } + } + } else if len(m.fieldsEx) > 0 { + // Filter specified fields. + for _, v := range gstr.SplitAndTrim(m.fieldsEx, ",") { + delete(data, v) + } + } + return data +} + // getLink returns the underlying database link object with configured attribute. func (m *Model) getLink() dbLink { if m.tx != nil { @@ -891,6 +993,23 @@ func (m *Model) getAll(query string, args ...interface{}) (result Result, err er return result, err } +// getPrimaryKey retrieves and returns the primary key name of the model table. +// It parses m.tables to retrieve the primary table name, supporting m.tables like: +// "user", "user u", "user as u, user_detail as ud". +func (m *Model) getPrimaryKey() string { + table := gstr.SplitAndTrim(m.tables, " ")[0] + tableFields, err := m.db.TableFields(table) + if err != nil { + return "" + } + for name, field := range tableFields { + if gstr.ContainsI(field.Key, "pri") { + return name + } + } + return "" +} + // checkAndRemoveCache checks and remove the cache if necessary. func (m *Model) checkAndRemoveCache() { if m.cacheEnabled && m.cacheDuration < 0 && len(m.cacheName) > 0 { @@ -900,7 +1019,9 @@ func (m *Model) checkAndRemoveCache() { // formatCondition formats where arguments of the model and returns a new condition sql and its arguments. // Note that this function does not change any attribute value of the . -func (m *Model) formatCondition() (condition string, conditionArgs []interface{}) { +// +// The parameter specifies whether limits querying only one record if m.limit is not set. +func (m *Model) formatCondition(limit bool) (condition string, conditionArgs []interface{}) { var where string if len(m.whereHolder) > 0 { for _, v := range m.whereHolder { @@ -955,36 +1076,11 @@ func (m *Model) formatCondition() (condition string, conditionArgs []interface{} } else { condition += fmt.Sprintf(" LIMIT %d", m.limit) } + } else if limit { + condition += " LIMIT 1" } if m.offset >= 0 { condition += fmt.Sprintf(" OFFSET %d", m.offset) } return } - -// Chunk iterates the table with given size and callback function. -func (m *Model) Chunk(limit int, callback func(result Result, err error) bool) { - page := m.start - if page == 0 { - page = 1 - } - model := m - for { - model = model.ForPage(page, limit) - data, err := model.All() - if err != nil { - callback(nil, err) - break - } - if len(data) == 0 { - break - } - if callback(data, err) == false { - break - } - if len(data) < limit { - break - } - page++ - } -} diff --git a/database/gdb/gdb_unit_z_mysql_model_test.go b/database/gdb/gdb_unit_z_mysql_model_test.go index bc13f9767..ac495d584 100644 --- a/database/gdb/gdb_unit_z_mysql_model_test.go +++ b/database/gdb/gdb_unit_z_mysql_model_test.go @@ -200,7 +200,7 @@ func Test_Model_Update(t *testing.T) { defer dropTable(table) // UPDATE...LIMIT gtest.Case(t, func() { - result, err := db.Table(table).Data("nickname", "T100").OrderBy("id desc").Limit(2).Update() + result, err := db.Table(table).Data("nickname", "T100").Order("id desc").Limit(2).Update() gtest.Assert(err, nil) n, _ := result.RowsAffected() gtest.Assert(n, 2) @@ -246,10 +246,10 @@ func Test_Model_Clone(t *testing.T) { count, err := md.Count() gtest.Assert(err, nil) - record, err := md.OrderBy("id DESC").One() + record, err := md.Order("id DESC").One() gtest.Assert(err, nil) - result, err := md.OrderBy("id ASC").All() + result, err := md.Order("id ASC").All() gtest.Assert(err, nil) gtest.Assert(count, 2) @@ -327,7 +327,7 @@ func Test_Model_Safe(t *testing.T) { gtest.Assert(err, nil) gtest.Assert(count, 2) - all, err := md2.OrderBy("id asc").All() + all, err := md2.Order("id asc").All() gtest.Assert(err, nil) gtest.Assert(len(all), 2) gtest.Assert(all[0]["id"].Int(), 1) @@ -342,7 +342,7 @@ func Test_Model_Safe(t *testing.T) { gtest.Assert(err, nil) gtest.Assert(count, 3) - all, err = md3.OrderBy("id asc").All() + all, err = md3.Order("id asc").All() gtest.Assert(err, nil) gtest.Assert(len(all), 3) gtest.Assert(all[0]["id"].Int(), 4) @@ -371,6 +371,44 @@ func Test_Model_All(t *testing.T) { }) } +func Test_Model_FindAll(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.Case(t, func() { + result, err := db.Table(table).FindAll(5) + gtest.Assert(err, nil) + gtest.Assert(len(result), 1) + gtest.Assert(result[0]["id"].Int(), 5) + }) + + gtest.Case(t, func() { + result, err := db.Table(table).Order("id asc").FindAll("id", 8) + gtest.Assert(err, nil) + gtest.Assert(len(result), 1) + gtest.Assert(result[0]["id"].Int(), 8) + }) + + gtest.Case(t, func() { + result, err := db.Table(table).Order("id asc").FindAll(g.Slice{3, 9}) + gtest.Assert(err, nil) + gtest.Assert(len(result), 2) + gtest.Assert(result[0]["id"].Int(), 3) + gtest.Assert(result[1]["id"].Int(), 9) + }) + + gtest.Case(t, func() { + result, err := db.Table(table).FindAll() + gtest.Assert(err, nil) + gtest.Assert(len(result), INIT_DATA_SIZE) + }) + gtest.Case(t, func() { + result, err := db.Table(table).Where("id<0").FindAll() + gtest.Assert(result, nil) + gtest.Assert(err, nil) + }) +} + func Test_Model_One(t *testing.T) { table := createInitTable() defer dropTable(table) @@ -387,9 +425,45 @@ func Test_Model_One(t *testing.T) { }) } +func Test_Model_FindOne(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.Case(t, func() { + record, err := db.Table(table).FindOne(1) + gtest.Assert(err, nil) + gtest.Assert(record["nickname"].String(), "name_1") + }) + + gtest.Case(t, func() { + record, err := db.Table(table).FindOne(3) + gtest.Assert(err, nil) + gtest.Assert(record["nickname"].String(), "name_3") + }) + + gtest.Case(t, func() { + record, err := db.Table(table).Where("id", 1).FindOne() + gtest.Assert(err, nil) + gtest.Assert(record["nickname"].String(), "name_1") + }) + + gtest.Case(t, func() { + record, err := db.Table(table).FindOne("id", 9) + gtest.Assert(err, nil) + gtest.Assert(record["nickname"].String(), "name_9") + }) + + gtest.Case(t, func() { + record, err := db.Table(table).Where("id", 0).FindOne() + gtest.Assert(err, nil) + gtest.Assert(record, nil) + }) +} + func Test_Model_Value(t *testing.T) { table := createInitTable() defer dropTable(table) + gtest.Case(t, func() { value, err := db.Table(table).Fields("nickname").Where("id", 1).Value() gtest.Assert(err, nil) @@ -403,6 +477,35 @@ func Test_Model_Value(t *testing.T) { }) } +func Test_Model_FindValue(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.Case(t, func() { + value, err := db.Table(table).FindValue("nickname", 1) + gtest.Assert(err, nil) + gtest.Assert(value.String(), "name_1") + }) + + gtest.Case(t, func() { + value, err := db.Table(table).Order("id desc").FindValue("nickname") + gtest.Assert(err, nil) + gtest.Assert(value.String(), "name_10") + }) + + gtest.Case(t, func() { + value, err := db.Table(table).Fields("nickname").Where("id", 1).FindValue() + gtest.Assert(err, nil) + gtest.Assert(value.String(), "name_1") + }) + + gtest.Case(t, func() { + value, err := db.Table(table).Fields("nickname").Where("id", 0).FindValue() + gtest.Assert(err, nil) + gtest.Assert(value, nil) + }) +} + func Test_Model_Count(t *testing.T) { table := createInitTable() defer dropTable(table) @@ -413,6 +516,26 @@ func Test_Model_Count(t *testing.T) { }) } +func Test_Model_FindCount(t *testing.T) { + table := createInitTable() + defer dropTable(table) + gtest.Case(t, func() { + count, err := db.Table(table).FindCount(g.Slice{1, 3}) + gtest.Assert(err, nil) + gtest.Assert(count, 2) + }) + gtest.Case(t, func() { + count, err := db.Table(table).FindCount(g.Slice{1, 300000}) + gtest.Assert(err, nil) + gtest.Assert(count, 1) + }) + gtest.Case(t, func() { + count, err := db.Table(table).FindCount() + gtest.Assert(err, nil) + gtest.Assert(count, INIT_DATA_SIZE) + }) +} + func Test_Model_Select(t *testing.T) { table := createInitTable() defer dropTable(table) @@ -514,7 +637,7 @@ func Test_Model_Structs(t *testing.T) { CreateTime gtime.Time } var users []User - err := db.Table(table).OrderBy("id asc").Structs(&users) + err := db.Table(table).Order("id asc").Structs(&users) if err != nil { gtest.Error(err) } @@ -537,7 +660,7 @@ func Test_Model_Structs(t *testing.T) { CreateTime *gtime.Time } var users []*User - err := db.Table(table).OrderBy("id asc").Structs(&users) + err := db.Table(table).Order("id asc").Structs(&users) if err != nil { gtest.Error(err) } @@ -560,7 +683,7 @@ func Test_Model_Structs(t *testing.T) { CreateTime *gtime.Time } var users []*User - err := db.Table(table).OrderBy("id asc").Scan(&users) + err := db.Table(table).Order("id asc").Scan(&users) if err != nil { gtest.Error(err) } @@ -629,7 +752,7 @@ func Test_Model_Scan(t *testing.T) { CreateTime gtime.Time } var users []User - err := db.Table(table).OrderBy("id asc").Scan(&users) + err := db.Table(table).Order("id asc").Scan(&users) gtest.Assert(err, nil) gtest.Assert(len(users), INIT_DATA_SIZE) gtest.Assert(users[0].Id, 1) @@ -649,7 +772,7 @@ func Test_Model_Scan(t *testing.T) { CreateTime *gtime.Time } var users []*User - err := db.Table(table).OrderBy("id asc").Scan(&users) + err := db.Table(table).Order("id asc").Scan(&users) gtest.Assert(err, nil) gtest.Assert(len(users), INIT_DATA_SIZE) gtest.Assert(users[0].Id, 1) @@ -683,7 +806,7 @@ func Test_Model_OrderBy(t *testing.T) { defer dropTable(table) gtest.Case(t, func() { - result, err := db.Table(table).OrderBy("id DESC").Select() + result, err := db.Table(table).Order("id DESC").Select() gtest.Assert(err, nil) gtest.Assert(len(result), INIT_DATA_SIZE) gtest.Assert(result[0]["nickname"].String(), fmt.Sprintf("name_%d", INIT_DATA_SIZE)) @@ -724,7 +847,7 @@ func Test_Model_Where(t *testing.T) { gtest.Case(t, func() { result, err := db.Table(table).Where(g.Map{ "passport like": "user_1%", - }).OrderBy("id asc").All() + }).Order("id asc").All() gtest.Assert(err, nil) gtest.Assert(len(result), 2) gtest.Assert(result[0].GMap().Get("id"), 1) @@ -870,7 +993,7 @@ func Test_Model_Where(t *testing.T) { "create_time > 0": nil, "id": g.Slice{1, 2, 3}, } - result, err := db.Table(table).Where(conditions).OrderBy("id asc").All() + result, err := db.Table(table).Where(conditions).Order("id asc").All() gtest.Assert(err, nil) gtest.Assert(len(result), 3) gtest.Assert(result[0]["id"].Int(), 1) @@ -885,7 +1008,7 @@ func Test_Model_Where(t *testing.T) { "create_time > ?": 0, "id in(?)": g.Slice{1, 2, 3}, } - result, err := db.Table(table).Where(conditions).OrderBy("id asc").All() + result, err := db.Table(table).Where(conditions).Order("id asc").All() gtest.Assert(err, nil) gtest.Assert(len(result), 3) gtest.Assert(result[0]["id"].Int(), 1) @@ -906,7 +1029,7 @@ func Test_Model_Where(t *testing.T) { }) // slice single gtest.Case(t, func() { - result, err := db.Table(table).Where("id IN(?)", g.Slice{1, 3}).OrderBy("id ASC").All() + result, err := db.Table(table).Where("id IN(?)", g.Slice{1, 3}).Order("id ASC").All() gtest.Assert(err, nil) gtest.Assert(len(result), 2) gtest.Assert(result[0]["id"].Int(), 1) @@ -914,7 +1037,7 @@ func Test_Model_Where(t *testing.T) { }) // slice + string gtest.Case(t, func() { - result, err := db.Table(table).Where("nickname=? AND id IN(?)", "name_3", g.Slice{1, 3}).OrderBy("id ASC").All() + result, err := db.Table(table).Where("nickname=? AND id IN(?)", "name_3", g.Slice{1, 3}).Order("id ASC").All() gtest.Assert(err, nil) gtest.Assert(len(result), 1) gtest.Assert(result[0]["id"].Int(), 3) @@ -924,7 +1047,7 @@ func Test_Model_Where(t *testing.T) { result, err := db.Table(table).Where(g.Map{ "id": g.Slice{1, 3}, "nickname": "name_3", - }).OrderBy("id ASC").All() + }).Order("id ASC").All() gtest.Assert(err, nil) gtest.Assert(len(result), 1) gtest.Assert(result[0]["id"].Int(), 3) @@ -938,7 +1061,265 @@ func Test_Model_Where(t *testing.T) { result, err := db.Table(table).Where(User{ Ids: []int{1, 3}, Nickname: "name_3", - }).OrderBy("id ASC").All() + }).Order("id ASC").All() + gtest.Assert(err, nil) + gtest.Assert(len(result), 1) + gtest.Assert(result[0]["id"].Int(), 3) + }) +} + +func Test_Model_WherePri(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + // primary key + gtest.Case(t, func() { + one, err := db.Table(table).WherePri(3).One() + gtest.Assert(err, nil) + gtest.AssertNE(one, nil) + gtest.Assert(one["id"].Int(), 3) + }) + gtest.Case(t, func() { + all, err := db.Table(table).WherePri(g.Slice{3, 9}).Order("id asc").All() + gtest.Assert(err, nil) + gtest.Assert(len(all), 2) + gtest.Assert(all[0]["id"].Int(), 3) + gtest.Assert(all[1]["id"].Int(), 9) + }) + + // string + gtest.Case(t, func() { + result, err := db.Table(table).WherePri("id=? and nickname=?", 3, "name_3").One() + gtest.Assert(err, nil) + gtest.AssertGT(len(result), 0) + gtest.Assert(result["id"].Int(), 3) + }) + // slice parameter + gtest.Case(t, func() { + result, err := db.Table(table).WherePri("id=? and nickname=?", g.Slice{3, "name_3"}).One() + gtest.Assert(err, nil) + gtest.AssertGT(len(result), 0) + gtest.Assert(result["id"].Int(), 3) + }) + // map like + gtest.Case(t, func() { + result, err := db.Table(table).WherePri(g.Map{ + "passport like": "user_1%", + }).Order("id asc").All() + gtest.Assert(err, nil) + gtest.Assert(len(result), 2) + gtest.Assert(result[0].GMap().Get("id"), 1) + gtest.Assert(result[1].GMap().Get("id"), 10) + }) + // map + slice parameter + gtest.Case(t, func() { + result, err := db.Table(table).WherePri(g.Map{ + "id": g.Slice{1, 2, 3}, + "passport": g.Slice{"user_2", "user_3"}, + }).And("id=? and nickname=?", g.Slice{3, "name_3"}).One() + gtest.Assert(err, nil) + gtest.AssertGT(len(result), 0) + gtest.Assert(result["id"].Int(), 3) + }) + gtest.Case(t, func() { + result, err := db.Table(table).WherePri(g.Map{ + "id": g.Slice{1, 2, 3}, + "passport": g.Slice{"user_2", "user_3"}, + }).Or("nickname=?", g.Slice{"name_4"}).And("id", 3).One() + gtest.Assert(err, nil) + gtest.AssertGT(len(result), 0) + gtest.Assert(result["id"].Int(), 3) + }) + gtest.Case(t, func() { + result, err := db.Table(table).WherePri("id=3", g.Slice{}).One() + gtest.Assert(err, nil) + gtest.AssertGT(len(result), 0) + gtest.Assert(result["id"].Int(), 3) + }) + gtest.Case(t, func() { + result, err := db.Table(table).WherePri("id=?", g.Slice{3}).One() + gtest.Assert(err, nil) + gtest.AssertGT(len(result), 0) + gtest.Assert(result["id"].Int(), 3) + }) + gtest.Case(t, func() { + result, err := db.Table(table).WherePri("id", 3).One() + gtest.Assert(err, nil) + gtest.AssertGT(len(result), 0) + gtest.Assert(result["id"].Int(), 3) + }) + gtest.Case(t, func() { + result, err := db.Table(table).WherePri("id", 3).WherePri("nickname", "name_3").One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 3) + }) + gtest.Case(t, func() { + result, err := db.Table(table).WherePri("id", 3).And("nickname", "name_3").One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 3) + }) + gtest.Case(t, func() { + result, err := db.Table(table).WherePri("id", 30).Or("nickname", "name_3").One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 3) + }) + gtest.Case(t, func() { + result, err := db.Table(table).WherePri("id", 30).Or("nickname", "name_3").And("id>?", 1).One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 3) + }) + gtest.Case(t, func() { + result, err := db.Table(table).WherePri("id", 30).Or("nickname", "name_3").And("id>", 1).One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 3) + }) + // slice + gtest.Case(t, func() { + result, err := db.Table(table).WherePri("id=? AND nickname=?", g.Slice{3, "name_3"}...).One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 3) + }) + gtest.Case(t, func() { + result, err := db.Table(table).WherePri("id=? AND nickname=?", g.Slice{3, "name_3"}).One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 3) + }) + gtest.Case(t, func() { + result, err := db.Table(table).WherePri("passport like ? and nickname like ?", g.Slice{"user_3", "name_3"}).One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 3) + }) + // map + gtest.Case(t, func() { + result, err := db.Table(table).WherePri(g.Map{"id": 3, "nickname": "name_3"}).One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 3) + }) + // map key operator + gtest.Case(t, func() { + result, err := db.Table(table).WherePri(g.Map{"id>": 1, "id<": 3}).One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 2) + }) + + // gmap.Map + gtest.Case(t, func() { + result, err := db.Table(table).WherePri(gmap.NewFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 3) + }) + // gmap.Map key operator + gtest.Case(t, func() { + result, err := db.Table(table).WherePri(gmap.NewFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 2) + }) + + // list map + gtest.Case(t, func() { + result, err := db.Table(table).WherePri(gmap.NewListMapFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 3) + }) + // list map key operator + gtest.Case(t, func() { + result, err := db.Table(table).WherePri(gmap.NewListMapFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 2) + }) + + // tree map + gtest.Case(t, func() { + result, err := db.Table(table).WherePri(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 3) + }) + // tree map key operator + gtest.Case(t, func() { + result, err := db.Table(table).WherePri(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id>": 1, "id<": 3})).One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 2) + }) + + // complicated where 1 + gtest.Case(t, func() { + //db.SetDebug(true) + conditions := g.Map{ + "nickname like ?": "%name%", + "id between ? and ?": g.Slice{1, 3}, + "id > 0": nil, + "create_time > 0": nil, + "id": g.Slice{1, 2, 3}, + } + result, err := db.Table(table).WherePri(conditions).Order("id asc").All() + gtest.Assert(err, nil) + gtest.Assert(len(result), 3) + gtest.Assert(result[0]["id"].Int(), 1) + }) + // complicated where 2 + gtest.Case(t, func() { + //db.SetDebug(true) + conditions := g.Map{ + "nickname like ?": "%name%", + "id between ? and ?": g.Slice{1, 3}, + "id >= ?": 1, + "create_time > ?": 0, + "id in(?)": g.Slice{1, 2, 3}, + } + result, err := db.Table(table).WherePri(conditions).Order("id asc").All() + gtest.Assert(err, nil) + gtest.Assert(len(result), 3) + gtest.Assert(result[0]["id"].Int(), 1) + }) + // struct + gtest.Case(t, func() { + type User struct { + Id int `json:"id"` + Nickname string `gconv:"nickname"` + } + result, err := db.Table(table).WherePri(User{3, "name_3"}).One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 3) + + result, err = db.Table(table).WherePri(&User{3, "name_3"}).One() + gtest.Assert(err, nil) + gtest.Assert(result["id"].Int(), 3) + }) + // slice single + gtest.Case(t, func() { + result, err := db.Table(table).WherePri("id IN(?)", g.Slice{1, 3}).Order("id ASC").All() + gtest.Assert(err, nil) + gtest.Assert(len(result), 2) + gtest.Assert(result[0]["id"].Int(), 1) + gtest.Assert(result[1]["id"].Int(), 3) + }) + // slice + string + gtest.Case(t, func() { + result, err := db.Table(table).WherePri("nickname=? AND id IN(?)", "name_3", g.Slice{1, 3}).Order("id ASC").All() + gtest.Assert(err, nil) + gtest.Assert(len(result), 1) + gtest.Assert(result[0]["id"].Int(), 3) + }) + // slice + map + gtest.Case(t, func() { + result, err := db.Table(table).WherePri(g.Map{ + "id": g.Slice{1, 3}, + "nickname": "name_3", + }).Order("id ASC").All() + gtest.Assert(err, nil) + gtest.Assert(len(result), 1) + gtest.Assert(result[0]["id"].Int(), 3) + }) + // slice + struct + gtest.Case(t, func() { + type User struct { + Ids []int `json:"id"` + Nickname string `gconv:"nickname"` + } + result, err := db.Table(table).WherePri(User{ + Ids: []int{1, 3}, + Nickname: "name_3", + }).Order("id ASC").All() gtest.Assert(err, nil) gtest.Assert(len(result), 1) gtest.Assert(result[0]["id"].Int(), 3) @@ -969,7 +1350,7 @@ func Test_Model_Offset(t *testing.T) { table := createInitTable() defer dropTable(table) - result, err := db.Table(table).Limit(2).Offset(5).OrderBy("id").Select() + result, err := db.Table(table).Limit(2).Offset(5).Order("id").Select() gtest.Assert(err, nil) gtest.Assert(len(result), 2) gtest.Assert(result[0]["id"], 6) @@ -980,7 +1361,7 @@ func Test_Model_ForPage(t *testing.T) { table := createInitTable() defer dropTable(table) - result, err := db.Table(table).ForPage(3, 3).OrderBy("id").Select() + result, err := db.Table(table).ForPage(3, 3).Order("id").Select() gtest.Assert(err, nil) gtest.Assert(len(result), 3) gtest.Assert(result[0]["id"], 7) @@ -1153,7 +1534,7 @@ func Test_Model_Option_List(t *testing.T) { gtest.Assert(err, nil) n, _ := r.RowsAffected() gtest.Assert(n, 2) - list, err := db.Table(table).OrderBy("id asc").All() + list, err := db.Table(table).Order("id asc").All() gtest.Assert(err, nil) gtest.Assert(len(list), 2) gtest.Assert(list[0]["id"].String(), "1") @@ -1187,7 +1568,7 @@ func Test_Model_Option_List(t *testing.T) { gtest.Assert(err, nil) n, _ := r.RowsAffected() gtest.Assert(n, 2) - list, err := db.Table(table).OrderBy("id asc").All() + list, err := db.Table(table).Order("id asc").All() g.Dump(list) gtest.Assert(err, nil) gtest.Assert(len(list), 2) @@ -1232,7 +1613,7 @@ func Test_Model_FieldsEx(t *testing.T) { defer dropTable(table) // Select. gtest.Case(t, func() { - r, err := db.Table(table).FieldsEx("create_time, id").Where("id in (?)", g.Slice{1, 2}).OrderBy("id asc").All() + r, err := db.Table(table).FieldsEx("create_time, id").Where("id in (?)", g.Slice{1, 2}).Order("id asc").All() gtest.Assert(err, nil) gtest.Assert(len(r), 2) gtest.Assert(len(r[0]), 3) @@ -1268,7 +1649,7 @@ func Test_Model_Prefix(t *testing.T) { defer dropTable(PREFIX1 + table) // Select. gtest.Case(t, func() { - r, err := db.Table(table).Where("id in (?)", g.Slice{1, 2}).OrderBy("id asc").All() + r, err := db.Table(table).Where("id in (?)", g.Slice{1, 2}).Order("id asc").All() gtest.Assert(err, nil) gtest.Assert(len(r), 2) gtest.Assert(r[0]["id"], "1") @@ -1276,7 +1657,7 @@ func Test_Model_Prefix(t *testing.T) { }) // Select with alias. gtest.Case(t, func() { - r, err := db.Table(table+" as u").Where("u.id in (?)", g.Slice{1, 2}).OrderBy("u.id asc").All() + r, err := db.Table(table+" as u").Where("u.id in (?)", g.Slice{1, 2}).Order("u.id asc").All() gtest.Assert(err, nil) gtest.Assert(len(r), 2) gtest.Assert(r[0]["id"], "1") @@ -1284,7 +1665,7 @@ func Test_Model_Prefix(t *testing.T) { }) // Select with alias and join statement. gtest.Case(t, func() { - r, err := db.Table(table+" as u1").LeftJoin(table+" as u2", "u2.id=u1.id").Where("u1.id in (?)", g.Slice{1, 2}).OrderBy("u1.id asc").All() + r, err := db.Table(table+" as u1").LeftJoin(table+" as u2", "u2.id=u1.id").Where("u1.id in (?)", g.Slice{1, 2}).Order("u1.id asc").All() gtest.Assert(err, nil) gtest.Assert(len(r), 2) gtest.Assert(r[0]["id"], "1") diff --git a/frame/gins/gins.go b/frame/gins/gins.go index 2eba3fdd3..a2fc22365 100644 --- a/frame/gins/gins.go +++ b/frame/gins/gins.go @@ -5,6 +5,9 @@ // You can obtain one at https://github.com/gogf/gf. // Package gins provides instances and core components management. +// +// Note that it should not used glog.Panic* functions for panics if you do not want +// to log all the panics. package gins import ( @@ -103,7 +106,7 @@ func View(name ...string) *gview.View { } if m != nil { if err := view.SetConfigWithMap(m); err != nil { - glog.Panic(err) + panic(err) } } } @@ -150,7 +153,7 @@ func Log(name ...string) *glog.Logger { } if m != nil { if err := logger.SetConfigWithMap(m); err != nil { - glog.Panic(err) + panic(err) } } } @@ -172,15 +175,15 @@ func Database(name ...string) gdb.DB { if gdb.GetConfig(group) != nil { db, err := gdb.Instance(group) if err != nil { - glog.Panic(err) + panic(err) } return db } m := config.GetMap("database") if m == nil { - glog.Panic(`database init failed: "database" node not found, is config file or configuration missing?`) + panic(`database init failed: "database" node not found, is config file or configuration missing?`) } - // Parse as map-slice. + // Parse as map-slice and adds it to gdb's global configurations. for group, groupConfig := range m { cg := gdb.ConfigGroup{} switch value := groupConfig.(type) { @@ -199,14 +202,15 @@ func Database(name ...string) gdb.DB { gdb.SetConfigGroup(group, cg) } } - // Parse as a single node configuration. + // Parse as a single node configuration, + // which is the default group configuration. if node := parseDBConfigNode(m); node != nil { cg := gdb.ConfigGroup{} if node.LinkInfo != "" || node.Host != "" { cg = append(cg, *node) } if len(cg) > 0 { - gdb.SetConfigGroup(group, cg) + gdb.SetConfigGroup(gdb.DEFAULT_GROUP_NAME, cg) } } addConfigMonitor(instanceKey, config) @@ -219,12 +223,12 @@ func Database(name ...string) gdb.DB { } if m != nil { if err := db.GetLogger().SetConfigWithMap(m); err != nil { - glog.Panic(err) + panic(err) } } return db } else { - glog.Panic(err) + panic(err) } return nil }) @@ -242,7 +246,7 @@ func parseDBConfigNode(value interface{}) *gdb.ConfigNode { node := &gdb.ConfigNode{} err := gconv.Struct(nodeMap, node) if err != nil { - glog.Panic(err) + panic(err) } if _, v := gutil.MapPossibleItemByKey(nodeMap, "link"); v != nil { node.LinkInfo = gconv.String(v) @@ -282,15 +286,15 @@ func Redis(name ...string) *gredis.Redis { if v, ok := m[group]; ok { redisConfig, err := gredis.ConfigFromStr(gconv.String(v)) if err != nil { - glog.Panic(err) + panic(err) } addConfigMonitor(instanceKey, config) return gredis.New(redisConfig) } else { - glog.Panicf(`configuration for redis not found for group "%s"`, group) + panic(fmt.Sprintf(`configuration for redis not found for group "%s"`, group)) } } else { - glog.Panicf(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.FilePath()) + panic(fmt.Sprintf(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.FilePath())) } return nil }) diff --git a/net/ghttp/ghttp.go b/net/ghttp/ghttp.go index f99b14c73..7babc0ebe 100644 --- a/net/ghttp/ghttp.go +++ b/net/ghttp/ghttp.go @@ -8,5 +8,7 @@ package ghttp var ( + // paramTagPriority is the priority tag array for request parameter + // to struct field mapping. paramTagPriority = []string{"param", "params", "p"} ) diff --git a/net/ghttp/ghttp_client_api.go b/net/ghttp/ghttp_client_api.go index 5f1cdc062..dc6460732 100644 --- a/net/ghttp/ghttp_client_api.go +++ b/net/ghttp/ghttp_client_api.go @@ -6,122 +6,182 @@ package ghttp +// Get is a convenience method for sending GET request. +// NOTE that remembers CLOSING the response object when it'll never be used. func Get(url string) (*ClientResponse, error) { return DoRequest("GET", url) } +// Put is a convenience method for sending PUT request. +// NOTE that remembers CLOSING the response object when it'll never be used. func Put(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("PUT", url, data...) } +// Post is a convenience method for sending POST request. +// NOTE that remembers CLOSING the response object when it'll never be used. func Post(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("POST", url, data...) } +// Delete is a convenience method for sending DELETE request. +// NOTE that remembers CLOSING the response object when it'll never be used. func Delete(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("DELETE", url, data...) } +// Head is a convenience method for sending HEAD request. +// NOTE that remembers CLOSING the response object when it'll never be used. func Head(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("HEAD", url, data...) } +// Patch is a convenience method for sending PATCH request. +// NOTE that remembers CLOSING the response object when it'll never be used. func Patch(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("PATCH", url, data...) } +// Connect is a convenience method for sending CONNECT request. +// NOTE that remembers CLOSING the response object when it'll never be used. func Connect(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("CONNECT", url, data...) } +// Options is a convenience method for sending OPTIONS request. +// NOTE that remembers CLOSING the response object when it'll never be used. func Options(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("OPTIONS", url, data...) } +// Trace is a convenience method for sending TRACE request. +// NOTE that remembers CLOSING the response object when it'll never be used. func Trace(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("TRACE", url, data...) } +// DoRequest is a convenience method for sending custom http method request. +// NOTE that remembers CLOSING the response object when it'll never be used. func DoRequest(method, url string, data ...interface{}) (*ClientResponse, error) { return NewClient().DoRequest(method, url, data...) } +// GetContent is a convenience method for sending GET request, which retrieves and returns +// the result content and automatically closes response object. func GetContent(url string, data ...interface{}) string { return RequestContent("GET", url, data...) } +// PutContent is a convenience method for sending PUT request, which retrieves and returns +// the result content and automatically closes response object. func PutContent(url string, data ...interface{}) string { return RequestContent("PUT", url, data...) } +// PostContent is a convenience method for sending POST request, which retrieves and returns +// the result content and automatically closes response object. func PostContent(url string, data ...interface{}) string { return RequestContent("POST", url, data...) } +// DeleteContent is a convenience method for sending DELETE request, which retrieves and returns +// the result content and automatically closes response object. func DeleteContent(url string, data ...interface{}) string { return RequestContent("DELETE", url, data...) } +// HeadContent is a convenience method for sending HEAD request, which retrieves and returns +// the result content and automatically closes response object. func HeadContent(url string, data ...interface{}) string { return RequestContent("HEAD", url, data...) } +// PatchContent is a convenience method for sending PATCH request, which retrieves and returns +// the result content and automatically closes response object. func PatchContent(url string, data ...interface{}) string { return RequestContent("PATCH", url, data...) } +// ConnectContent is a convenience method for sending CONNECT request, which retrieves and returns +// the result content and automatically closes response object. func ConnectContent(url string, data ...interface{}) string { return RequestContent("CONNECT", url, data...) } +// OptionsContent is a convenience method for sending OPTIONS request, which retrieves and returns +// the result content and automatically closes response object. func OptionsContent(url string, data ...interface{}) string { return RequestContent("OPTIONS", url, data...) } +// TraceContent is a convenience method for sending TRACE request, which retrieves and returns +// the result content and automatically closes response object. func TraceContent(url string, data ...interface{}) string { return RequestContent("TRACE", url, data...) } +// RequestContent is a convenience method for sending custom http method request, which +// retrieves and returns the result content and automatically closes response object. func RequestContent(method string, url string, data ...interface{}) string { return NewClient().RequestContent(method, url, data...) } +// GetBytes is a convenience method for sending GET request, which retrieves and returns +// the result content as bytes and automatically closes response object. func GetBytes(url string, data ...interface{}) []byte { return RequestBytes("GET", url, data...) } +// PutBytes is a convenience method for sending PUT request, which retrieves and returns +// the result content as bytes and automatically closes response object. func PutBytes(url string, data ...interface{}) []byte { return RequestBytes("PUT", url, data...) } +// PostBytes is a convenience method for sending POST request, which retrieves and returns +// the result content as bytes and automatically closes response object. func PostBytes(url string, data ...interface{}) []byte { return RequestBytes("POST", url, data...) } +// DeleteBytes is a convenience method for sending DELETE request, which retrieves and returns +// the result content as bytes and automatically closes response object. func DeleteBytes(url string, data ...interface{}) []byte { return RequestBytes("DELETE", url, data...) } +// HeadBytes is a convenience method for sending HEAD request, which retrieves and returns +// the result content as bytes and automatically closes response object. func HeadBytes(url string, data ...interface{}) []byte { return RequestBytes("HEAD", url, data...) } +// PatchBytes is a convenience method for sending PATCH request, which retrieves and returns +// the result content as bytes and automatically closes response object. func PatchBytes(url string, data ...interface{}) []byte { return RequestBytes("PATCH", url, data...) } +// ConnectBytes is a convenience method for sending CONNECT request, which retrieves and returns +// the result content as bytes and automatically closes response object. func ConnectBytes(url string, data ...interface{}) []byte { return RequestBytes("CONNECT", url, data...) } +// OptionsBytes is a convenience method for sending OPTIONS request, which retrieves and returns +// the result content as bytes and automatically closes response object. func OptionsBytes(url string, data ...interface{}) []byte { return RequestBytes("OPTIONS", url, data...) } +// TraceBytes is a convenience method for sending TRACE request, which retrieves and returns +// the result content as bytes and automatically closes response object. func TraceBytes(url string, data ...interface{}) []byte { return RequestBytes("TRACE", url, data...) } +// RequestBytes is a convenience method for sending custom http method request, which +// retrieves and returns the result content as bytes and automatically closes response object. func RequestBytes(method string, url string, data ...interface{}) []byte { return NewClient().RequestBytes(method, url, data...) } diff --git a/net/ghttp/ghttp_client_bytes.go b/net/ghttp/ghttp_client_bytes.go index 7fac5255b..cde71c21f 100644 --- a/net/ghttp/ghttp_client_bytes.go +++ b/net/ghttp/ghttp_client_bytes.go @@ -6,44 +6,53 @@ package ghttp +// GetBytes sends a GET request, retrieves and returns the result content as bytes. func (c *Client) GetBytes(url string, data ...interface{}) []byte { return c.RequestBytes("GET", url, data...) } +// PutBytes sends a PUT request, retrieves and returns the result content as bytes. func (c *Client) PutBytes(url string, data ...interface{}) []byte { return c.RequestBytes("PUT", url, data...) } +// PostBytes sends a POST request, retrieves and returns the result content as bytes. func (c *Client) PostBytes(url string, data ...interface{}) []byte { return c.RequestBytes("POST", url, data...) } +// DeleteBytes sends a DELETE request, retrieves and returns the result content as bytes. func (c *Client) DeleteBytes(url string, data ...interface{}) []byte { return c.RequestBytes("DELETE", url, data...) } +// HeadBytes sends a HEAD request, retrieves and returns the result content as bytes. func (c *Client) HeadBytes(url string, data ...interface{}) []byte { return c.RequestBytes("HEAD", url, data...) } +// PatchBytes sends a PATCH request, retrieves and returns the result content as bytes. func (c *Client) PatchBytes(url string, data ...interface{}) []byte { return c.RequestBytes("PATCH", url, data...) } +// ConnectBytes sends a CONNECT request, retrieves and returns the result content as bytes. func (c *Client) ConnectBytes(url string, data ...interface{}) []byte { return c.RequestBytes("CONNECT", url, data...) } +// OptionsBytes sends a OPTIONS request, retrieves and returns the result content as bytes. func (c *Client) OptionsBytes(url string, data ...interface{}) []byte { return c.RequestBytes("OPTIONS", url, data...) } +// TraceBytes sends a TRACE request, retrieves and returns the result content as bytes. func (c *Client) TraceBytes(url string, data ...interface{}) []byte { return c.RequestBytes("TRACE", url, data...) } -// RequestBytes sends request using given HTTP method and data and returns the binary as bytes. -// It reads and closes the response object internally automatically. +// RequestBytes sends request using given HTTP method and data, retrieves returns the result +// as bytes. It reads and closes the response object internally automatically. func (c *Client) RequestBytes(method string, url string, data ...interface{}) []byte { response, err := c.DoRequest(method, url, data...) if err != nil { diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index ed9ef4d65..3939d60e3 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -128,8 +128,15 @@ func (r *Request) GetMapStrStr(def ...map[string]interface{}) map[string]string return r.GetRequestMapStrStr(def...) } +// GetStruct is alias of GetRequestToStruct. +// See GetRequestToStruct. +func (r *Request) GetStruct(pointer interface{}, mapping ...map[string]string) error { + return r.GetRequestToStruct(pointer, mapping...) +} + // GetToStruct is alias of GetRequestToStruct. // See GetRequestToStruct. +// Deprecated. func (r *Request) GetToStruct(pointer interface{}, mapping ...map[string]string) error { return r.GetRequestToStruct(pointer, mapping...) } diff --git a/net/ghttp/ghttp_request_param_form.go b/net/ghttp/ghttp_request_param_form.go index 3c450a3af..5ceeac585 100644 --- a/net/ghttp/ghttp_request_param_form.go +++ b/net/ghttp/ghttp_request_param_form.go @@ -154,10 +154,10 @@ func (r *Request) GetFormMapStrVar(kvMap ...map[string]interface{}) map[string]* return nil } -// GetFormToStruct retrieves all form parameters passed from client and converts them to +// GetFormStruct retrieves all form parameters passed from client and converts them to // given struct object. Note that the parameter is a pointer to the struct object. // The optional parameter is used to specify the key to attribute mapping. -func (r *Request) GetFormToStruct(pointer interface{}, mapping ...map[string]string) error { +func (r *Request) GetFormStruct(pointer interface{}, mapping ...map[string]string) error { r.ParseForm() tagMap := structs.TagMapName(pointer, paramTagPriority, true) if len(mapping) > 0 { @@ -167,3 +167,9 @@ func (r *Request) GetFormToStruct(pointer interface{}, mapping ...map[string]str } return gconv.StructDeep(r.formMap, pointer, tagMap) } + +// GetFormToStruct is alias of GetFormStruct. See GetFormStruct. +// Deprecated. +func (r *Request) GetFormToStruct(pointer interface{}, mapping ...map[string]string) error { + return r.GetFormStruct(pointer, mapping...) +} diff --git a/net/ghttp/ghttp_request_param_post.go b/net/ghttp/ghttp_request_param_post.go index d1bd1f764..e03a27f89 100644 --- a/net/ghttp/ghttp_request_param_post.go +++ b/net/ghttp/ghttp_request_param_post.go @@ -174,11 +174,11 @@ func (r *Request) GetPostMapStrVar(kvMap ...map[string]interface{}) map[string]* return nil } -// GetPostToStruct retrieves all parameters in the form and body passed from client +// GetPostStruct retrieves all parameters in the form and body passed from client // and converts them to given struct object. Note that the parameter is a pointer // to the struct object. The optional parameter is used to specify the key to // attribute mapping. -func (r *Request) GetPostToStruct(pointer interface{}, mapping ...map[string]string) error { +func (r *Request) GetPostStruct(pointer interface{}, mapping ...map[string]string) error { tagMap := structs.TagMapName(pointer, paramTagPriority, true) if len(mapping) > 0 { for k, v := range mapping[0] { @@ -187,3 +187,9 @@ func (r *Request) GetPostToStruct(pointer interface{}, mapping ...map[string]str } return gconv.StructDeep(r.GetPostMap(), pointer, tagMap) } + +// GetPostToStruct is alias of GetQueryStruct. See GetPostStruct. +// Deprecated. +func (r *Request) GetPostToStruct(pointer interface{}, mapping ...map[string]string) error { + return r.GetPostStruct(pointer, mapping...) +} diff --git a/net/ghttp/ghttp_request_param_query.go b/net/ghttp/ghttp_request_param_query.go index 747975fce..ba644a3e0 100644 --- a/net/ghttp/ghttp_request_param_query.go +++ b/net/ghttp/ghttp_request_param_query.go @@ -188,11 +188,11 @@ func (r *Request) GetQueryMapStrVar(kvMap ...map[string]interface{}) map[string] return nil } -// GetQueryToStruct retrieves all parameters passed from client using HTTP GET method +// GetQueryStruct retrieves all parameters passed from client using HTTP GET method // and converts them to given struct object. Note that the parameter is a pointer // to the struct object. The optional parameter is used to specify the key to // attribute mapping. -func (r *Request) GetQueryToStruct(pointer interface{}, mapping ...map[string]string) error { +func (r *Request) GetQueryStruct(pointer interface{}, mapping ...map[string]string) error { r.ParseQuery() tagMap := structs.TagMapName(pointer, paramTagPriority, true) if len(mapping) > 0 { @@ -202,3 +202,9 @@ func (r *Request) GetQueryToStruct(pointer interface{}, mapping ...map[string]st } return gconv.StructDeep(r.GetQueryMap(), pointer, tagMap) } + +// GetQueryToStruct is alias of GetQueryStruct. See GetQueryStruct. +// Deprecated. +func (r *Request) GetQueryToStruct(pointer interface{}, mapping ...map[string]string) error { + return r.GetQueryStruct(pointer, mapping...) +} diff --git a/net/ghttp/ghttp_request_param_request.go b/net/ghttp/ghttp_request_param_request.go index da528675c..5647dcfb6 100644 --- a/net/ghttp/ghttp_request_param_request.go +++ b/net/ghttp/ghttp_request_param_request.go @@ -263,10 +263,10 @@ func (r *Request) GetRequestMapStrVar(kvMap ...map[string]interface{}) map[strin return nil } -// GetRequestToStruct retrieves all parameters passed from client no matter what HTTP method the client is using, +// GetRequestStruct retrieves all parameters passed from client no matter what HTTP method the client is using, // and converts them to given struct object. Note that the parameter is a pointer to the struct object. // The optional parameter is used to specify the key to attribute mapping. -func (r *Request) GetRequestToStruct(pointer interface{}, mapping ...map[string]string) error { +func (r *Request) GetRequestStruct(pointer interface{}, mapping ...map[string]string) error { tagMap := structs.TagMapName(pointer, paramTagPriority, true) if len(mapping) > 0 { for k, v := range mapping[0] { @@ -275,3 +275,9 @@ func (r *Request) GetRequestToStruct(pointer interface{}, mapping ...map[string] } return gconv.StructDeep(r.GetRequestMap(), pointer, tagMap) } + +// GetRequestToStruct is alias of GetRequestStruct. See GetRequestStruct. +// Deprecated. +func (r *Request) GetRequestToStruct(pointer interface{}, mapping ...map[string]string) error { + return r.GetRequestStruct(pointer, mapping...) +} diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 38c1eba87..ed0655eb4 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -335,31 +335,20 @@ func (s *Server) dumpRouterMap() { if s.config.DumpRouterMap && len(s.routesMap) > 0 { buffer := bytes.NewBuffer(nil) table := tablewriter.NewWriter(buffer) - table.SetHeader([]string{"SERVER", "ADDRESS", "DOMAIN", "METHOD", "P", "ROUTE", "HANDLER", "MIDDLEWARE"}) + table.SetHeader([]string{"SERVER", "DOMAIN", "ADDRESS", "METHOD", "ROUTE", "HANDLER", "MIDDLEWARE"}) table.SetRowLine(true) table.SetBorder(false) table.SetCenterSeparator("|") - table.SetColumnAlignment([]int{ - tablewriter.ALIGN_CENTER, - tablewriter.ALIGN_CENTER, - tablewriter.ALIGN_CENTER, - tablewriter.ALIGN_CENTER, - tablewriter.ALIGN_CENTER, - tablewriter.ALIGN_LEFT, - tablewriter.ALIGN_LEFT, - tablewriter.ALIGN_LEFT, - }) for _, item := range s.GetRouterArray() { - data := make([]string, 8) + data := make([]string, 7) data[0] = item.Server - data[1] = item.Address - data[2] = item.Domain + data[1] = item.Domain + data[2] = item.Address data[3] = item.Method - data[4] = gconv.String(len(strings.Split(item.Route, "/")) - 1 + item.Priority) - data[5] = item.Route - data[6] = item.handler.itemName - data[7] = item.Middleware + data[4] = item.Route + data[5] = item.handler.itemName + data[6] = item.Middleware table.Append(data) } table.Render() diff --git a/os/gproc/gproc_comm_receive.go b/os/gproc/gproc_comm_receive.go index 9ebe6e703..eb4c8b13d 100644 --- a/os/gproc/gproc_comm_receive.go +++ b/os/gproc/gproc_comm_receive.go @@ -63,7 +63,7 @@ func receiveTcpListening() { } // Save the port to the pid file. if err := gfile.PutContents(getCommFilePath(Pid()), gconv.String(i)); err != nil { - glog.Panic(err) + panic(err) } break } diff --git a/os/gsession/gsession_storage_file.go b/os/gsession/gsession_storage_file.go index 8611f947c..a474c1c32 100644 --- a/os/gsession/gsession_storage_file.go +++ b/os/gsession/gsession_storage_file.go @@ -8,6 +8,7 @@ package gsession import ( "encoding/json" + "fmt" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/internal/intlog" "os" @@ -22,8 +23,6 @@ import ( "github.com/gogf/gf/os/gtime" - "github.com/gogf/gf/os/glog" - "github.com/gogf/gf/os/gfile" ) @@ -39,7 +38,7 @@ var ( DefaultStorageFilePath = gfile.Join(gfile.TempDir(), "gsessions") DefaultStorageFileCryptoKey = []byte("Session storage file crypto key!") DefaultStorageFileCryptoEnabled = false - DefaultStorageFileLoopInterval = time.Minute + DefaultStorageFileLoopInterval = 10 * time.Second ) func init() { @@ -55,15 +54,15 @@ func NewStorageFile(path ...string) *StorageFile { if len(path) > 0 && path[0] != "" { storagePath, _ = gfile.Search(path[0]) if storagePath == "" { - glog.Panicf("'%s' does not exist", path[0]) + panic(fmt.Sprintf(fmt.Sprintf("'%s' does not exist", path[0]))) } if !gfile.IsWritable(storagePath) { - glog.Panicf("'%s' is not writable", path[0]) + panic(fmt.Sprintf("'%s' is not writable", path[0])) } } if storagePath != "" { if err := gfile.Mkdir(storagePath); err != nil { - glog.Panicf("mkdir '%s' failed: %v", path[0], err) + panic(fmt.Sprintf("mkdir '%s' failed: %v", path[0], err)) } } s := &StorageFile{ diff --git a/text/gstr/gstr_trim.go b/text/gstr/gstr_trim.go index 3f63dd748..155eb1e38 100644 --- a/text/gstr/gstr_trim.go +++ b/text/gstr/gstr_trim.go @@ -53,8 +53,9 @@ func TrimLeft(str string, characterMask ...string) string { // TrimLeftStr strips all of the given string from the beginning of a string. // Note that it does not strips the whitespaces of its beginning. func TrimLeftStr(str string, cut string) string { - for str[0:len(cut)] == cut { - str = str[len(cut):] + var lenCut = len(cut) + for len(str) >= lenCut && str[0:lenCut] == cut { + str = str[lenCut:] } return str } @@ -71,14 +72,12 @@ func TrimRight(str string, characterMask ...string) string { // TrimRightStr strips all of the given string from the end of a string. // Note that it does not strips the whitespaces of its end. func TrimRightStr(str string, cut string) string { - var length int - for { - length = len(str) - if str[length-len(cut):length] == cut { - str = str[:length-len(cut)] - } else { - break - } + var lenStr = len(str) + var lenCut = len(cut) + for lenStr >= lenCut && str[lenStr-lenCut:lenStr] == cut { + lenStr = lenStr - lenCut + str = str[:lenStr] + } return str } diff --git a/text/gstr/gstr_z_unit_trim_test.go b/text/gstr/gstr_z_unit_trim_test.go index cca73e236..a0f18bcff 100644 --- a/text/gstr/gstr_z_unit_trim_test.go +++ b/text/gstr/gstr_z_unit_trim_test.go @@ -41,9 +41,11 @@ func Test_TrimRight(t *testing.T) { func Test_TrimRightStr(t *testing.T) { gtest.Case(t, func() { gtest.Assert(gstr.TrimRightStr("gogo我爱gogo", "go"), "gogo我爱") + gtest.Assert(gstr.TrimRightStr("gogo我爱gogo", "go我爱gogo"), "go") }) gtest.Case(t, func() { gtest.Assert(gstr.TrimRightStr("我爱中国人", "人"), "我爱中国") + gtest.Assert(gstr.TrimRightStr("我爱中国人", "爱中国人"), "我") }) } @@ -57,8 +59,10 @@ func Test_TrimLeft(t *testing.T) { func Test_TrimLeftStr(t *testing.T) { gtest.Case(t, func() { gtest.Assert(gstr.TrimLeftStr("gogo我爱gogo", "go"), "我爱gogo") + gtest.Assert(gstr.TrimLeftStr("gogo我爱gogo", "gogo我爱go"), "go") }) gtest.Case(t, func() { gtest.Assert(gstr.TrimLeftStr("我爱中国人", "我爱"), "中国人") + gtest.Assert(gstr.TrimLeftStr("我爱中国人", "我爱中国"), "人") }) }