add FilterNil/FilterEmpty functions for package garray; add FieldsStr/FieldsExStr for gdb.Model

This commit is contained in:
John
2020-02-22 14:26:36 +08:00
parent 13dba407a2
commit 745a913cfb
23 changed files with 332 additions and 4 deletions

View File

@ -1,6 +1,6 @@
# GoFrame
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf/g#pkg-subdirectories)
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf)
[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf)
[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master)

View File

@ -1,5 +1,5 @@
# GoFrame
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf/g#pkg-subdirectories)
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf)
[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf)
[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master)

View File

@ -10,6 +10,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/text/gstr"
"math"
"sort"
@ -735,3 +736,32 @@ func (a *Array) UnmarshalValue(value interface{}) error {
}
return nil
}
// FilterNil removes all nil value of the array.
func (a *Array) FilterNil() *Array {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if empty.IsNil(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
i++
}
}
return a
}
// FilterEmpty removes all empty value of the array.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (a *Array) FilterEmpty() *Array {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if empty.IsEmpty(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
i++
}
}
return a
}

View File

@ -716,3 +716,17 @@ func (a *IntArray) UnmarshalValue(value interface{}) error {
}
return nil
}
// FilterEmpty removes all zero value of the array.
func (a *IntArray) FilterEmpty() *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if a.array[i] == 0 {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
i++
}
}
return a
}

View File

@ -716,3 +716,17 @@ func (a *StrArray) UnmarshalValue(value interface{}) error {
}
return nil
}
// FilterEmpty removes all empty string value of the array.
func (a *StrArray) FilterEmpty() *StrArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if a.array[i] == "" {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
i++
}
}
return a
}

View File

@ -10,6 +10,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gutil"
"math"
@ -686,3 +687,46 @@ func (a *SortedArray) UnmarshalValue(value interface{}) (err error) {
}
return err
}
// FilterNil removes all nil value of the array.
func (a *SortedArray) FilterNil() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if empty.IsNil(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
for i := len(a.array) - 1; i >= 0; {
if empty.IsNil(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
return a
}
// FilterEmpty removes all empty value of the array.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (a *SortedArray) FilterEmpty() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if empty.IsEmpty(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
for i := len(a.array) - 1; i >= 0; {
if empty.IsEmpty(a.array[i]) {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
return a
}

View File

@ -656,3 +656,24 @@ func (a *SortedIntArray) UnmarshalValue(value interface{}) (err error) {
}
return err
}
// FilterEmpty removes all zero value of the array.
func (a *SortedIntArray) FilterEmpty() *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if a.array[i] == 0 {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
for i := len(a.array) - 1; i >= 0; {
if a.array[i] == 0 {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
return a
}

View File

@ -652,3 +652,24 @@ func (a *SortedStrArray) UnmarshalValue(value interface{}) (err error) {
}
return err
}
// FilterEmpty removes all empty string value of the array.
func (a *SortedStrArray) FilterEmpty() *SortedStrArray {
a.mu.Lock()
defer a.mu.Unlock()
for i := 0; i < len(a.array); {
if a.array[i] == "" {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
for i := len(a.array) - 1; i >= 0; {
if a.array[i] == "" {
a.array = append(a.array[:i], a.array[i+1:]...)
} else {
break
}
}
return a
}

View File

@ -540,3 +540,26 @@ func TestArray_UnmarshalValue(t *testing.T) {
gtest.Assert(t.Array.Slice(), g.Slice{1, 2, 3})
})
}
func TestArray_FilterNil(t *testing.T) {
gtest.Case(t, func() {
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
array := garray.NewArrayFromCopy(values)
gtest.Assert(array.FilterNil().Slice(), values)
})
gtest.Case(t, func() {
array := garray.NewArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil})
gtest.Assert(array.FilterNil(), g.Slice{1, 2, 3, 4})
})
}
func TestArray_FilterEmpty(t *testing.T) {
gtest.Case(t, func() {
array := garray.NewArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}})
gtest.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
gtest.Case(t, func() {
array := garray.NewArrayFrom(g.Slice{1, 2, 3, 4})
gtest.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
}

View File

@ -575,3 +575,14 @@ func TestIntArray_UnmarshalValue(t *testing.T) {
gtest.Assert(t.Array.Slice(), g.Slice{1, 2, 3})
})
}
func TestIntArray_FilterEmpty(t *testing.T) {
gtest.Case(t, func() {
array := garray.NewIntArrayFrom(g.SliceInt{0, 1, 2, 3, 4, 0})
gtest.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
})
gtest.Case(t, func() {
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3, 4})
gtest.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
})
}

View File

@ -578,3 +578,14 @@ func TestStrArray_UnmarshalValue(t *testing.T) {
gtest.Assert(t.Array.Slice(), g.SliceStr{"1", "2", "3"})
})
}
func TestStrArray_FilterEmpty(t *testing.T) {
gtest.Case(t, func() {
array := garray.NewStrArrayFrom(g.SliceStr{"", "1", "2", "0"})
gtest.Assert(array.FilterEmpty(), g.SliceStr{"1", "2", "0"})
})
gtest.Case(t, func() {
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2"})
gtest.Assert(array.FilterEmpty(), g.SliceStr{"1", "2"})
})
}

View File

@ -678,3 +678,26 @@ func TestSortedArray_UnmarshalValue(t *testing.T) {
gtest.Assert(t.Array.Slice(), g.Slice{1, 2, 3})
})
}
func TestSortedArray_FilterNil(t *testing.T) {
gtest.Case(t, func() {
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
array := garray.NewSortedArrayFromCopy(values, gutil.ComparatorInt)
gtest.Assert(array.FilterNil().Slice(), g.Slice{0, "", g.Slice{}, 1, 2, 3, 4})
})
gtest.Case(t, func() {
array := garray.NewSortedArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil}, gutil.ComparatorInt)
gtest.Assert(array.FilterNil(), g.Slice{1, 2, 3, 4})
})
}
func TestSortedArray_FilterEmpty(t *testing.T) {
gtest.Case(t, func() {
array := garray.NewSortedArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}, gutil.ComparatorInt)
gtest.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
gtest.Case(t, func() {
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3, 4}, gutil.ComparatorInt)
gtest.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
}

View File

@ -559,3 +559,14 @@ func TestSortedIntArray_UnmarshalValue(t *testing.T) {
gtest.Assert(t.Array.Slice(), g.Slice{1, 2, 3})
})
}
func TestSortedIntArray_FilterEmpty(t *testing.T) {
gtest.Case(t, func() {
array := garray.NewSortedIntArrayFrom(g.SliceInt{0, 1, 2, 3, 4, 0})
gtest.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
})
gtest.Case(t, func() {
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2, 3, 4})
gtest.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
})
}

View File

@ -568,3 +568,14 @@ func TestSortedStrArray_UnmarshalValue(t *testing.T) {
gtest.Assert(t.Array.Slice(), g.SliceStr{"1", "2", "3"})
})
}
func TestSortedStrArray_FilterEmpty(t *testing.T) {
gtest.Case(t, func() {
array := garray.NewSortedStrArrayFrom(g.SliceStr{"", "1", "2", "0"})
gtest.Assert(array.FilterEmpty(), g.SliceStr{"0", "1", "2"})
})
gtest.Case(t, func() {
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2"})
gtest.Assert(array.FilterEmpty(), g.SliceStr{"1", "2"})
})
}

View File

@ -35,7 +35,7 @@ type apiIterator interface {
Iterator(f func(key, value interface{}) bool)
}
// apiInterfacesis the type assert api for Interfaces.
// apiInterfaces is the type assert api for Interfaces.
type apiInterfaces interface {
Interfaces() []interface{}
}

View File

@ -10,6 +10,7 @@ import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/container/gmap"
"reflect"
"time"
@ -243,6 +244,9 @@ func (m *Model) Fields(fields string) *Model {
// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
func (m *Model) FieldsEx(fields string) *Model {
if gstr.Contains(m.tables, " ") {
panic("function FieldsEx supports only single table operations")
}
model := m.getModel()
model.fieldsEx = fields
fieldsExSet := gset.NewStrSetFrom(gstr.SplitAndTrim(fields, ","))
@ -261,6 +265,47 @@ func (m *Model) FieldsEx(fields string) *Model {
return model
}
// FieldsStr retrieves and returns all fields from the table, joined with char ','.
// The optional parameter <prefix> specifies the prefix for each field, eg: FieldsStr("u.").
func (m *Model) FieldsStr(prefix ...string) string {
prefixStr := ""
if len(prefix) > 0 {
prefixStr = prefix[0]
}
if m, err := m.db.TableFields(m.tables); err == nil {
fieldsArray := garray.NewStrArraySize(len(m), len(m))
for _, field := range m {
fieldsArray.Set(field.Index, prefixStr+field.Name)
}
return fieldsArray.Join(",")
}
return ""
}
// FieldsExStr retrieves and returns fields which are not in parameter <fields> from the table,
// joined with char ','.
// The parameter <fields> specifies the fields that are excluded.
// The optional parameter <prefix> specifies the prefix for each field, eg: FieldsExStr("id", "u.").
func (m *Model) FieldsExStr(fields string, prefix ...string) string {
prefixStr := ""
if len(prefix) > 0 {
prefixStr = prefix[0]
}
if m, err := m.db.TableFields(m.tables); err == nil {
fieldsArray := garray.NewStrArraySize(len(m), len(m))
fieldsExSet := gset.NewStrSetFrom(gstr.SplitAndTrim(fields, ","))
for _, field := range m {
if fieldsExSet.Contains(field.Name) {
continue
}
fieldsArray.Set(field.Index, prefixStr+field.Name)
}
fieldsArray.FilterEmpty()
return fieldsArray.Join(",")
}
return ""
}
// Option adds extra operation option for the model.
func (m *Model) Option(option int) *Model {
model := m.getModel()
@ -283,6 +328,9 @@ func (m *Model) OmitEmpty() *Model {
// Filter marks filtering the fields which does not exist in the fields of the operated table.
func (m *Model) Filter() *Model {
if gstr.Contains(m.tables, " ") {
panic("function Filter supports only single table operations")
}
model := m.getModel()
model.filter = true
return model

View File

@ -14,6 +14,7 @@ package gdb
import (
"database/sql"
"fmt"
"github.com/gogf/gf/text/gstr"
"strconv"
"strings"
@ -154,6 +155,10 @@ func (db *dbMssql) Tables(schema ...string) (tables []string, err error) {
}
func (db *dbMssql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table = gstr.Trim(table)
if gstr.Contains(table, " ") {
panic("function TableFields supports only single table operations")
}
checkSchema := db.schema.Val()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]

View File

@ -15,6 +15,7 @@ import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/text/gstr"
"reflect"
"strconv"
"strings"
@ -118,6 +119,10 @@ func (db *dbOracle) Tables(schema ...string) (tables []string, err error) {
}
func (db *dbOracle) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table = gstr.Trim(table)
if gstr.Contains(table, " ") {
panic("function TableFields supports only single table operations")
}
checkSchema := db.schema.Val()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]

View File

@ -14,6 +14,7 @@ package gdb
import (
"database/sql"
"fmt"
"github.com/gogf/gf/text/gstr"
"strings"
"github.com/gogf/gf/text/gregex"
@ -60,6 +61,10 @@ func (db *dbPgsql) Tables(schema ...string) (tables []string, err error) {
}
func (db *dbPgsql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table = gstr.Trim(table)
if gstr.Contains(table, " ") {
panic("function TableFields supports only single table operations")
}
table, _ = gregex.ReplaceString("\"", "", table)
checkSchema := db.schema.Val()
if len(schema) > 0 && schema[0] != "" {

View File

@ -12,6 +12,7 @@ package gdb
import (
"database/sql"
"github.com/gogf/gf/text/gstr"
)
type dbSqlite struct {
@ -43,6 +44,10 @@ func (db *dbSqlite) Tables(schema ...string) (tables []string, err error) {
// TODO
func (db *dbSqlite) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table = gstr.Trim(table)
if gstr.Contains(table, " ") {
panic("function TableFields supports only single table operations")
}
return
}

View File

@ -141,11 +141,16 @@ func (bs *dbBase) Tables(schema ...string) (tables []string, err error) {
}
// TableFields retrieves and returns the fields of given table.
//
// Note that it returns a map containing the field name and its corresponding fields.
// As a map is unsorted, the TableField struct has a "Index" field marks its sequence in the fields.
//
// It's using cache feature to enhance the performance, which is never expired util the process restarts.
func (bs *dbBase) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table = gstr.Trim(table)
if gstr.Contains(table, " ") {
panic("function TableFields supports only single table operations")
}
checkSchema := bs.schema.Val()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]

View File

@ -1698,6 +1698,26 @@ func Test_Model_FieldsEx(t *testing.T) {
})
}
func Test_Model_FieldsStr(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.Case(t, func() {
gtest.Assert(db.Table(table).FieldsStr(), "id,passport,password,nickname,create_time")
gtest.Assert(db.Table(table).FieldsStr("a."), "a.id,a.passport,a.password,a.nickname,a.create_time")
})
}
func Test_Model_FieldsExStr(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.Case(t, func() {
gtest.Assert(db.Table(table).FieldsExStr("create_time,nickname"), "id,passport,password")
gtest.Assert(db.Table(table).FieldsExStr("create_time,nickname", "a."), "a.id,a.passport,a.password")
})
}
func Test_Model_Prefix(t *testing.T) {
db := dbPrefix
table := fmt.Sprintf(`%s_%d`, TABLE, gtime.TimestampNano())

View File

@ -16,7 +16,7 @@ import (
"github.com/gogf/gf/internal/utilstr"
)
// apiMapStrAny is the interface support for package gmap.
// apiMapStrAny is the interface support for converting struct parameter to map.
type apiMapStrAny interface {
MapStrAny() map[string]interface{}
}
@ -138,6 +138,7 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]
m[String(k.Interface())] = rv.MapIndex(k).Interface()
}
case reflect.Struct:
// Map converting interface check.
if v, ok := value.(apiMapStrAny); ok {
return v.MapStrAny()
}