mirror of
https://gitee.com/milvus-io/milvus.git
synced 2024-11-29 18:38:44 +08:00
enhance: [GoSDK] support embedded struct in row data (#36443)
Related to milvus-io/milvus-sdk-go#818 This PR make Row-based insert data parsing embedded struct as flatten fields instead. Signed-off-by: Congqi Xia <congqi.xia@zilliz.com>
This commit is contained in:
parent
df7ae08851
commit
b3f2d3db6f
@ -276,60 +276,91 @@ type fieldCandi struct {
|
||||
}
|
||||
|
||||
func reflectValueCandi(v reflect.Value) (map[string]fieldCandi, error) {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
// unref **/***/... struct{}
|
||||
for v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
result := make(map[string]fieldCandi)
|
||||
switch v.Kind() {
|
||||
case reflect.Map: // map[string]any
|
||||
iter := v.MapRange()
|
||||
for iter.Next() {
|
||||
key := iter.Key().String()
|
||||
result[key] = fieldCandi{
|
||||
name: key,
|
||||
v: iter.Value(),
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
return getMapReflectCandidates(v), nil
|
||||
case reflect.Struct:
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
ft := v.Type().Field(i)
|
||||
name := ft.Name
|
||||
tag, ok := ft.Tag.Lookup(MilvusTag)
|
||||
|
||||
settings := make(map[string]string)
|
||||
if ok {
|
||||
if tag == MilvusSkipTagValue {
|
||||
continue
|
||||
}
|
||||
settings = ParseTagSetting(tag, MilvusTagSep)
|
||||
fn, has := settings[MilvusTagName]
|
||||
if has {
|
||||
// overwrite column to tag name
|
||||
name = fn
|
||||
}
|
||||
}
|
||||
_, ok = result[name]
|
||||
// duplicated
|
||||
if ok {
|
||||
return nil, fmt.Errorf("column has duplicated name: %s when parsing field: %s", name, ft.Name)
|
||||
}
|
||||
|
||||
v := v.Field(i)
|
||||
if v.Kind() == reflect.Array {
|
||||
v = v.Slice(0, v.Len())
|
||||
}
|
||||
|
||||
result[name] = fieldCandi{
|
||||
name: name,
|
||||
v: v,
|
||||
options: settings,
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return getStructReflectCandidates(v)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupport row type: %s", v.Kind().String())
|
||||
}
|
||||
}
|
||||
|
||||
// getMapReflectCandidates converts input map into fieldCandidate struct.
|
||||
// if value is struct/map etc, it will be treated as json data type directly(if schema say so).
|
||||
func getMapReflectCandidates(v reflect.Value) map[string]fieldCandi {
|
||||
result := make(map[string]fieldCandi)
|
||||
iter := v.MapRange()
|
||||
for iter.Next() {
|
||||
key := iter.Key().String()
|
||||
result[key] = fieldCandi{
|
||||
name: key,
|
||||
v: iter.Value(),
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// getStructReflectCandidates parses struct fields into fieldCandidates.
|
||||
// embedded struct will be flatten as field as well.
|
||||
func getStructReflectCandidates(v reflect.Value) (map[string]fieldCandi, error) {
|
||||
result := make(map[string]fieldCandi)
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
ft := v.Type().Field(i)
|
||||
name := ft.Name
|
||||
|
||||
// embedded struct, flatten all fields
|
||||
if ft.Anonymous && ft.Type.Kind() == reflect.Struct {
|
||||
embedCandidate, err := reflectValueCandi(v.Field(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for key, candi := range embedCandidate {
|
||||
// check duplicated field name in different structs
|
||||
_, ok := result[key]
|
||||
if ok {
|
||||
return nil, fmt.Errorf("column has duplicated name: %s when parsing field: %s", key, ft.Name)
|
||||
}
|
||||
result[key] = candi
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
tag, ok := ft.Tag.Lookup(MilvusTag)
|
||||
settings := make(map[string]string)
|
||||
if ok {
|
||||
if tag == MilvusSkipTagValue {
|
||||
continue
|
||||
}
|
||||
settings = ParseTagSetting(tag, MilvusTagSep)
|
||||
fn, has := settings[MilvusTagName]
|
||||
if has {
|
||||
// overwrite column to tag name
|
||||
name = fn
|
||||
}
|
||||
}
|
||||
_, ok = result[name]
|
||||
// duplicated
|
||||
if ok {
|
||||
return nil, fmt.Errorf("column has duplicated name: %s when parsing field: %s", name, ft.Name)
|
||||
}
|
||||
|
||||
v := v.Field(i)
|
||||
if v.Kind() == reflect.Array {
|
||||
v = v.Slice(0, v.Len())
|
||||
}
|
||||
|
||||
result[name] = fieldCandi{
|
||||
name: name,
|
||||
v: v,
|
||||
options: settings,
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package row
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@ -126,6 +127,10 @@ func (s *RowsSuite) TestDynamicSchema() {
|
||||
}
|
||||
|
||||
func (s *RowsSuite) TestReflectValueCandi() {
|
||||
type DynamicRows struct {
|
||||
Float float32 `json:"float" milvus:"name:float"`
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
tag string
|
||||
v reflect.Value
|
||||
@ -149,6 +154,65 @@ func (s *RowsSuite) TestReflectValueCandi() {
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
tag: "StructRow",
|
||||
v: reflect.ValueOf(struct {
|
||||
A string
|
||||
B int64
|
||||
}{A: "abc", B: 16}),
|
||||
expect: map[string]fieldCandi{
|
||||
"A": {
|
||||
name: "A",
|
||||
v: reflect.ValueOf("abc"),
|
||||
},
|
||||
"B": {
|
||||
name: "B",
|
||||
v: reflect.ValueOf(int64(16)),
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
tag: "StructRow_DuplicateName",
|
||||
v: reflect.ValueOf(struct {
|
||||
A string `milvus:"name:a"`
|
||||
B int64 `milvus:"name:a"`
|
||||
}{A: "abc", B: 16}),
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
tag: "StructRow_EmbedStruct",
|
||||
v: reflect.ValueOf(struct {
|
||||
A string `milvus:"name:a"`
|
||||
DynamicRows
|
||||
}{A: "emb", DynamicRows: DynamicRows{Float: 0.1}}),
|
||||
expect: map[string]fieldCandi{
|
||||
"a": {
|
||||
name: "a",
|
||||
v: reflect.ValueOf("emb"),
|
||||
},
|
||||
"float": {
|
||||
name: "float",
|
||||
v: reflect.ValueOf(float32(0.1)),
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
tag: "StructRow_EmbedDuplicateName",
|
||||
v: reflect.ValueOf(struct {
|
||||
Int64 int64 `json:"int64" milvus:"name:int64"`
|
||||
Float float32 `json:"float" milvus:"name:float"`
|
||||
FloatVec []float32 `json:"floatVec" milvus:"name:floatVec"`
|
||||
DynamicRows
|
||||
}{}),
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
tag: "Unsupported_primitive",
|
||||
v: reflect.ValueOf(int64(1)),
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@ -162,7 +226,7 @@ func (s *RowsSuite) TestReflectValueCandi() {
|
||||
s.Equal(len(c.expect), len(r))
|
||||
for k, v := range c.expect {
|
||||
rv, has := r[k]
|
||||
s.Require().True(has)
|
||||
s.Require().True(has, fmt.Sprintf("candidate with key(%s) must provided", k))
|
||||
s.Equal(v.name, rv.name)
|
||||
}
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user