From 6192d325010e0dc1d2da4666fd28d7efdf844bc3 Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 29 Oct 2021 15:12:31 +0800 Subject: [PATCH] add LeftJoinOnField/InnerJoinOnField/InnerJoinOnField/FieldsPrefix/FieldsExPrefix for package gdb --- database/gdb/gdb_func.go | 35 ++++++-- database/gdb/gdb_model_condition.go | 10 +++ database/gdb/gdb_model_fields.go | 26 ++++++ database/gdb/gdb_model_join.go | 85 ++++++++++++++---- database/gdb/gdb_schema.go | 10 +-- ...est.go => gdb_z_mysql_model_basic_test.go} | 16 ++-- ...st.go => gdb_z_mysql_model_filter_test.go} | 0 database/gdb/gdb_z_mysql_model_join_test.go | 86 +++++++++++++++++++ ...st.go => gdb_z_mysql_model_struct_test.go} | 0 ....go => gdb_z_mysql_model_subquery_test.go} | 0 text/gstr/gstr.go | 17 ---- text/gstr/gstr_array.go | 31 +++++++ text/gstr/gstr_z_unit_array_test.go | 44 ++++++++++ text/gstr/gstr_z_unit_basic_test.go | 20 ----- 14 files changed, 304 insertions(+), 76 deletions(-) rename database/gdb/{gdb_z_mysql_model_test.go => gdb_z_mysql_model_basic_test.go} (99%) rename database/gdb/{gdb_z_mysql_filter_test.go => gdb_z_mysql_model_filter_test.go} (100%) create mode 100644 database/gdb/gdb_z_mysql_model_join_test.go rename database/gdb/{gdb_z_mysql_struct_test.go => gdb_z_mysql_model_struct_test.go} (100%) rename database/gdb/{gdb_z_mysql_subquery_test.go => gdb_z_mysql_model_subquery_test.go} (100%) create mode 100644 text/gstr/gstr_array.go create mode 100644 text/gstr/gstr_z_unit_array_test.go diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 16e8f480d..056fb3409 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -408,6 +408,7 @@ type formatWhereInput struct { OmitEmpty bool Schema string Table string + Prefix string // Field prefix, eg: "user.", "order.". } // formatWhere formats where statement and its arguments for `Where` and `Having` statements. @@ -436,6 +437,7 @@ func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interfa Args: newArgs, Key: key, Value: value, + Prefix: in.Prefix, }) } @@ -462,6 +464,7 @@ func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interfa Key: ketStr, Value: value, OmitEmpty: in.OmitEmpty, + Prefix: in.Prefix, }) return true }) @@ -494,6 +497,7 @@ func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interfa Key: foundKey, Value: foundValue, OmitEmpty: in.OmitEmpty, + Prefix: in.Prefix, }) } } @@ -501,18 +505,31 @@ func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interfa default: // Usually a string. var ( - i = 0 whereStr = gconv.String(in.Where) ) + // Is `whereStr` a field name which composed as a key-value condition? // Eg: - // Where("id", []int{}).All() -> SELECT xxx FROM xxx WHERE 0=1 - // Where("name", "").All() -> SELECT xxx FROM xxx WHERE `name`='' - // OmitEmpty().Where("id", []int{}).All() -> SELECT xxx FROM xxx - // OmitEmpty().("name", "").All() -> SELECT xxx FROM xxx - if in.OmitEmpty && len(in.Args) == 1 && gstr.Count(whereStr, "?") == 0 && utils.IsEmpty(in.Args[0]) { + // Where("id", 1) + // Where("id", g.Slice{1,2,3}) + if gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, whereStr) && len(in.Args) == 1 { + newArgs = formatWhereKeyValue(formatWhereKeyValueInput{ + Db: db, + Buffer: buffer, + Args: newArgs, + Key: whereStr, + Value: in.Args[0], + OmitEmpty: in.OmitEmpty, + Prefix: in.Prefix, + }) in.Args = in.Args[:0] break } + // Regular string and parameter place holder handling. + // Eg: + // Where("id in(?) and name=?", g.Slice{1,2,3}, "john") + var ( + i = 0 + ) for { if i >= len(in.Args) { break @@ -612,6 +629,7 @@ type formatWhereKeyValueInput struct { Key string Value interface{} OmitEmpty bool + Prefix string // Field prefix, eg: "user.", "order.". } // formatWhereKeyValue handles each key-value pair of the parameter map. @@ -628,6 +646,9 @@ func formatWhereKeyValue(in formatWhereKeyValueInput) (newArgs []interface{}) { if in.OmitEmpty && holderCount == 0 && gutil.IsEmpty(in.Value) { return in.Args } + if in.Prefix != "" && !gstr.Contains(quotedKey, ".") { + quotedKey = in.Prefix + "." + quotedKey + } if in.Buffer.Len() > 0 { in.Buffer.WriteString(" AND ") } @@ -664,7 +685,7 @@ func formatWhereKeyValue(in formatWhereKeyValueInput) (newArgs []interface{}) { in.Buffer.WriteString(quotedKey) } } else { - // It also supports "LIKE" statement, which we considers it an operator. + // It also supports "LIKE" statement, which we consider it an operator. quotedKey = gstr.Trim(quotedKey) if gstr.Pos(quotedKey, "?") == -1 { like := " LIKE" diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index 43b898cb8..65efb008f 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -334,6 +334,12 @@ func (m *Model) Page(page, limit int) *Model { // // The parameter `limit1` specifies whether limits querying only one record if m.limit is not set. func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) { + var ( + prefix = "" + ) + if gstr.Contains(m.tables, " JOIN ") { + prefix = m.db.GetCore().QuoteWord(m.tablesInit) + } if len(m.whereHolder) > 0 { for _, v := range m.whereHolder { switch v.Operator { @@ -346,6 +352,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh OmitEmpty: m.option&optionOmitEmptyWhere > 0, Schema: m.schema, Table: m.tables, + Prefix: prefix, }) if len(newWhere) > 0 { conditionWhere = newWhere @@ -363,6 +370,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh OmitEmpty: m.option&optionOmitEmptyWhere > 0, Schema: m.schema, Table: m.tables, + Prefix: prefix, }) if len(newWhere) > 0 { if len(conditionWhere) == 0 { @@ -383,6 +391,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh OmitEmpty: m.option&optionOmitEmptyWhere > 0, Schema: m.schema, Table: m.tables, + Prefix: prefix, }) if len(newWhere) > 0 { if len(conditionWhere) == 0 { @@ -430,6 +439,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh OmitEmpty: m.option&optionOmitEmptyWhere > 0, Schema: m.schema, Table: m.tables, + Prefix: prefix, }) if len(havingStr) > 0 { conditionExtra += " HAVING " + havingStr diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index 19e8a0f2e..e2791a394 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -17,6 +17,12 @@ import ( // Fields appends `fieldNamesOrMapStruct` to the operation fields of the model, multiple fields joined using char ','. // The parameter `fieldNamesOrMapStruct` can be type of string/map/*map/struct/*struct. +// +// Eg: +// Fields("id", "name", "age") +// Fields([]string{"id", "name", "age"}) +// Fields(map[string]interface{}{"id":1, "name":"john", "age":18}) +// Fields(User{ Id: 1, Name: "john", Age: 18}) func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model { length := len(fieldNamesOrMapStruct) if length == 0 { @@ -52,10 +58,21 @@ func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model { return m } +// FieldsPrefix performs as function Fields but add extra prefix for each field. +func (m *Model) FieldsPrefix(prefix string, fieldNamesOrMapStruct ...interface{}) *Model { + model := m.Fields(fieldNamesOrMapStruct...) + array := gstr.SplitAndTrim(model.fields, ",") + gstr.PrefixArray(array, prefix+".") + model.fields = gstr.Join(array, ",") + return model +} + // FieldsEx appends `fieldNamesOrMapStruct` to the excluded operation fields of the model, // multiple fields joined using char ','. // Note that this function supports only single table operations. // The parameter `fieldNamesOrMapStruct` can be type of string/map/*map/struct/*struct. +// +// Also see Fields. func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model { length := len(fieldNamesOrMapStruct) if length == 0 { @@ -80,6 +97,15 @@ func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model { return m } +// FieldsExPrefix performs as function FieldsEx but add extra prefix for each field. +func (m *Model) FieldsExPrefix(prefix string, fieldNamesOrMapStruct ...interface{}) *Model { + model := m.FieldsEx(fieldNamesOrMapStruct...) + array := gstr.SplitAndTrim(model.fieldsEx, ",") + gstr.PrefixArray(array, prefix+".") + model.fieldsEx = gstr.Join(array, ",") + return model +} + // FieldCount formats and appends commonly used field `COUNT(column)` to the select fields of model. func (m *Model) FieldCount(column string, as ...string) *Model { asStr := "" diff --git a/database/gdb/gdb_model_join.go b/database/gdb/gdb_model_join.go index 4f4ae9709..2b3410e78 100644 --- a/database/gdb/gdb_model_join.go +++ b/database/gdb/gdb_model_join.go @@ -25,40 +25,93 @@ func isSubQuery(s string) bool { // LeftJoin does "LEFT JOIN ... ON ..." statement on the model. // The parameter `table` can be joined table and its joined condition, -// and also with its alias name, like: -// Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid") -// Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid") -// Table("user", "u").LeftJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid") +// and also with its alias name. +// +// Eg: +// Model("user").LeftJoin("user_detail", "user_detail.uid=user.uid") +// Model("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid") +// Model("user", "u").LeftJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid") func (m *Model) LeftJoin(table ...string) *Model { return m.doJoin("LEFT", table...) } // RightJoin does "RIGHT JOIN ... ON ..." statement on the model. // The parameter `table` can be joined table and its joined condition, -// and also with its alias name, like: -// Table("user").RightJoin("user_detail", "user_detail.uid=user.uid") -// Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid") -// Table("user", "u").RightJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid") +// and also with its alias name. +// +// Eg: +// Model("user").RightJoin("user_detail", "user_detail.uid=user.uid") +// Model("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid") +// Model("user", "u").RightJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid") func (m *Model) RightJoin(table ...string) *Model { return m.doJoin("RIGHT", table...) } // InnerJoin does "INNER JOIN ... ON ..." statement on the model. // The parameter `table` can be joined table and its joined condition, -// and also with its alias name, like: -// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid") -// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid") -// Table("user", "u").InnerJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid") +// and also with its alias name。 +// +// Eg: +// Model("user").InnerJoin("user_detail", "user_detail.uid=user.uid") +// Model("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid") +// Model("user", "u").InnerJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid") func (m *Model) InnerJoin(table ...string) *Model { return m.doJoin("INNER", table...) } +// LeftJoinOnField performs as LeftJoin, but it joins both tables with the same field name. +// +// Eg: +// Model("order").LeftJoinOnField("user", "user_id") +// Model("order").LeftJoinOnField("product", "product_id") +func (m *Model) LeftJoinOnField(table, field string) *Model { + return m.doJoin("LEFT", table, fmt.Sprintf( + `%s.%s=%s.%s`, + m.tables, + m.db.GetCore().QuoteWord(field), + m.db.GetCore().QuoteWord(table), + m.db.GetCore().QuoteWord(field), + )) +} + +// RightJoinOnField performs as RightJoin, but it joins both tables with the same field name. +// +// Eg: +// Model("order").InnerJoinOnField("user", "user_id") +// Model("order").InnerJoinOnField("product", "product_id") +func (m *Model) RightJoinOnField(table, field string) *Model { + return m.doJoin("RIGHT", table, fmt.Sprintf( + `%s.%s=%s.%s`, + m.tables, + m.db.GetCore().QuoteWord(field), + m.db.GetCore().QuoteWord(table), + m.db.GetCore().QuoteWord(field), + )) +} + +// InnerJoinOnField performs as InnerJoin, but it joins both tables with the same field name. +// +// Eg: +// Model("order").InnerJoinOnField("user", "user_id") +// Model("order").InnerJoinOnField("product", "product_id") +func (m *Model) InnerJoinOnField(table, field string) *Model { + return m.doJoin("INNER", table, fmt.Sprintf( + `%s.%s=%s.%s`, + m.tables, + m.db.GetCore().QuoteWord(field), + m.db.GetCore().QuoteWord(table), + m.db.GetCore().QuoteWord(field), + )) +} + // doJoin does "LEFT/RIGHT/INNER JOIN ... ON ..." statement on the model. // The parameter `table` can be joined table and its joined condition, -// and also with its alias name, like: -// Model("user").InnerJoin("user_detail", "user_detail.uid=user.uid") -// Model("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid") -// Model("user", "u").InnerJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid") +// and also with its alias name. +// +// Eg: +// Model("user").InnerJoin("user_detail", "user_detail.uid=user.uid") +// Model("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid") +// Model("user", "u").InnerJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid") // Related issues: // https://github.com/gogf/gf/issues/1024 func (m *Model) doJoin(operator string, table ...string) *Model { diff --git a/database/gdb/gdb_schema.go b/database/gdb/gdb_schema.go index 7794eada4..d1c3242d3 100644 --- a/database/gdb/gdb_schema.go +++ b/database/gdb/gdb_schema.go @@ -30,10 +30,10 @@ func (tx *TX) Schema(schema string) *Schema { } } -// Table creates and returns a new ORM model. +// Model creates and returns a new ORM model. // The parameter `tables` can be more than one table names, like : // "user", "user u", "user, user_detail", "user u, user_detail ud" -func (s *Schema) Table(table string) *Model { +func (s *Schema) Model(table string) *Model { var m *Model if s.tx != nil { m = s.tx.Model(table) @@ -51,9 +51,3 @@ func (s *Schema) Table(table string) *Model { m.schema = s.schema return m } - -// Model is alias of Core.Table. -// See Core.Table. -func (s *Schema) Model(table string) *Model { - return s.Table(table) -} diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_basic_test.go similarity index 99% rename from database/gdb/gdb_z_mysql_model_test.go rename to database/gdb/gdb_z_mysql_model_basic_test.go index 2cb30d64d..c1e7496ef 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_basic_test.go @@ -2308,31 +2308,31 @@ func Test_Model_Schema2(t *testing.T) { }() // Schema. gtest.C(t, func(t *gtest.T) { - v, err := db.Schema(TestSchema1).Table(table).Value("nickname", "id=2") + v, err := db.Schema(TestSchema1).Model(table).Value("nickname", "id=2") t.AssertNil(err) t.Assert(v.String(), "name_2") - r, err := db.Schema(TestSchema1).Table(table).Update(g.Map{"nickname": "name_200"}, "id=2") + r, err := db.Schema(TestSchema1).Model(table).Update(g.Map{"nickname": "name_200"}, "id=2") t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - v, err = db.Schema(TestSchema1).Table(table).Value("nickname", "id=2") + v, err = db.Schema(TestSchema1).Model(table).Value("nickname", "id=2") t.AssertNil(err) t.Assert(v.String(), "name_200") - v, err = db.Schema(TestSchema2).Table(table).Value("nickname", "id=2") + v, err = db.Schema(TestSchema2).Model(table).Value("nickname", "id=2") t.AssertNil(err) t.Assert(v.String(), "name_2") - v, err = db.Schema(TestSchema1).Table(table).Value("nickname", "id=2") + v, err = db.Schema(TestSchema1).Model(table).Value("nickname", "id=2") t.AssertNil(err) t.Assert(v.String(), "name_200") }) // Schema. gtest.C(t, func(t *gtest.T) { i := 1000 - _, err := db.Schema(TestSchema1).Table(table).Insert(g.Map{ + _, err := db.Schema(TestSchema1).Model(table).Insert(g.Map{ "id": i, "passport": fmt.Sprintf(`user_%d`, i), "password": fmt.Sprintf(`pass_%d`, i), @@ -2342,11 +2342,11 @@ func Test_Model_Schema2(t *testing.T) { }) t.AssertNil(err) - v, err := db.Schema(TestSchema1).Table(table).Value("nickname", "id=?", i) + v, err := db.Schema(TestSchema1).Model(table).Value("nickname", "id=?", i) t.AssertNil(err) t.Assert(v.String(), "name_1000") - v, err = db.Schema(TestSchema2).Table(table).Value("nickname", "id=?", i) + v, err = db.Schema(TestSchema2).Model(table).Value("nickname", "id=?", i) t.AssertNil(err) t.Assert(v.String(), "") }) diff --git a/database/gdb/gdb_z_mysql_filter_test.go b/database/gdb/gdb_z_mysql_model_filter_test.go similarity index 100% rename from database/gdb/gdb_z_mysql_filter_test.go rename to database/gdb/gdb_z_mysql_model_filter_test.go diff --git a/database/gdb/gdb_z_mysql_model_join_test.go b/database/gdb/gdb_z_mysql_model_join_test.go new file mode 100644 index 000000000..4942ac8e7 --- /dev/null +++ b/database/gdb/gdb_z_mysql_model_join_test.go @@ -0,0 +1,86 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb_test + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/test/gtest" + "testing" +) + +func Test_Model_LeftJoinOnField(t *testing.T) { + var ( + table1 = gtime.TimestampNanoStr() + "_table1" + table2 = gtime.TimestampNanoStr() + "_table2" + ) + createInitTable(table1) + defer dropTable(table1) + createInitTable(table2) + defer dropTable(table2) + + db.SetDebug(true) + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table1). + FieldsPrefix(table1, "*"). + LeftJoinOnField(table2, "id"). + Where("id", g.Slice{1, 2}). + Order("id asc").All() + t.AssertNil(err) + t.Assert(len(r), 2) + t.Assert(r[0]["id"], "1") + t.Assert(r[1]["id"], "2") + }) +} + +func Test_Model_RightJoinOnField(t *testing.T) { + var ( + table1 = gtime.TimestampNanoStr() + "_table1" + table2 = gtime.TimestampNanoStr() + "_table2" + ) + createInitTable(table1) + defer dropTable(table1) + createInitTable(table2) + defer dropTable(table2) + + db.SetDebug(true) + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table1). + FieldsPrefix(table1, "*"). + RightJoinOnField(table2, "id"). + Where("id", g.Slice{1, 2}). + Order("id asc").All() + t.AssertNil(err) + t.Assert(len(r), 2) + t.Assert(r[0]["id"], "1") + t.Assert(r[1]["id"], "2") + }) +} + +func Test_Model_InnerJoinOnField(t *testing.T) { + var ( + table1 = gtime.TimestampNanoStr() + "_table1" + table2 = gtime.TimestampNanoStr() + "_table2" + ) + createInitTable(table1) + defer dropTable(table1) + createInitTable(table2) + defer dropTable(table2) + + db.SetDebug(true) + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table1). + FieldsPrefix(table1, "*"). + InnerJoinOnField(table2, "id"). + Where("id", g.Slice{1, 2}). + Order("id asc").All() + t.AssertNil(err) + t.Assert(len(r), 2) + t.Assert(r[0]["id"], "1") + t.Assert(r[1]["id"], "2") + }) +} diff --git a/database/gdb/gdb_z_mysql_struct_test.go b/database/gdb/gdb_z_mysql_model_struct_test.go similarity index 100% rename from database/gdb/gdb_z_mysql_struct_test.go rename to database/gdb/gdb_z_mysql_model_struct_test.go diff --git a/database/gdb/gdb_z_mysql_subquery_test.go b/database/gdb/gdb_z_mysql_model_subquery_test.go similarity index 100% rename from database/gdb/gdb_z_mysql_subquery_test.go rename to database/gdb/gdb_z_mysql_model_subquery_test.go diff --git a/text/gstr/gstr.go b/text/gstr/gstr.go index 908780664..7a08c0cf4 100644 --- a/text/gstr/gstr.go +++ b/text/gstr/gstr.go @@ -489,20 +489,3 @@ func QuoteMeta(str string, chars ...string) string { } return buf.String() } - -// SearchArray searches string `s` in string slice `a` case-sensitively, -// returns its index in `a`. -// If `s` is not found in `a`, it returns -1. -func SearchArray(a []string, s string) int { - for i, v := range a { - if s == v { - return i - } - } - return NotFoundIndex -} - -// InArray checks whether string `s` in slice `a`. -func InArray(a []string, s string) bool { - return SearchArray(a, s) != NotFoundIndex -} diff --git a/text/gstr/gstr_array.go b/text/gstr/gstr_array.go new file mode 100644 index 000000000..1e4670237 --- /dev/null +++ b/text/gstr/gstr_array.go @@ -0,0 +1,31 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gstr + +// SearchArray searches string `s` in string slice `a` case-sensitively, +// returns its index in `a`. +// If `s` is not found in `a`, it returns -1. +func SearchArray(a []string, s string) int { + for i, v := range a { + if s == v { + return i + } + } + return NotFoundIndex +} + +// InArray checks whether string `s` in slice `a`. +func InArray(a []string, s string) bool { + return SearchArray(a, s) != NotFoundIndex +} + +// PrefixArray adds `prefix` string for each item of `array`. +func PrefixArray(array []string, prefix string) { + for k, v := range array { + array[k] = prefix + v + } +} diff --git a/text/gstr/gstr_z_unit_array_test.go b/text/gstr/gstr_z_unit_array_test.go new file mode 100644 index 000000000..754feed4b --- /dev/null +++ b/text/gstr/gstr_z_unit_array_test.go @@ -0,0 +1,44 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// go test *.go -bench=".*" + +package gstr_test + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/test/gtest" + "github.com/gogf/gf/v2/text/gstr" + "testing" +) + +func Test_SearchArray(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + a := g.SliceStr{"a", "b", "c"} + t.AssertEQ(gstr.SearchArray(a, "a"), 0) + t.AssertEQ(gstr.SearchArray(a, "b"), 1) + t.AssertEQ(gstr.SearchArray(a, "c"), 2) + t.AssertEQ(gstr.SearchArray(a, "d"), -1) + }) +} + +func Test_InArray(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + a := g.SliceStr{"a", "b", "c"} + t.AssertEQ(gstr.InArray(a, "a"), true) + t.AssertEQ(gstr.InArray(a, "b"), true) + t.AssertEQ(gstr.InArray(a, "c"), true) + t.AssertEQ(gstr.InArray(a, "d"), false) + }) +} + +func Test_PrefixArray(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + a := g.SliceStr{"a", "b", "c"} + gstr.PrefixArray(a, "1-") + t.AssertEQ(a, g.SliceStr{"1-a", "1-b", "1-c"}) + }) +} diff --git a/text/gstr/gstr_z_unit_basic_test.go b/text/gstr/gstr_z_unit_basic_test.go index c64d0c5c8..19ffeedc9 100644 --- a/text/gstr/gstr_z_unit_basic_test.go +++ b/text/gstr/gstr_z_unit_basic_test.go @@ -466,26 +466,6 @@ func Test_ContainsAny(t *testing.T) { }) } -func Test_SearchArray(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - a := g.SliceStr{"a", "b", "c"} - t.AssertEQ(gstr.SearchArray(a, "a"), 0) - t.AssertEQ(gstr.SearchArray(a, "b"), 1) - t.AssertEQ(gstr.SearchArray(a, "c"), 2) - t.AssertEQ(gstr.SearchArray(a, "d"), -1) - }) -} - -func Test_InArray(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - a := g.SliceStr{"a", "b", "c"} - t.AssertEQ(gstr.InArray(a, "a"), true) - t.AssertEQ(gstr.InArray(a, "b"), true) - t.AssertEQ(gstr.InArray(a, "c"), true) - t.AssertEQ(gstr.InArray(a, "d"), false) - }) -} - func Test_SubStrFrom(t *testing.T) { gtest.C(t, func(t *gtest.T) { t.Assert(gstr.SubStrFrom("我爱GoFrameGood", `G`), "GoFrameGood")