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,13 +276,25 @@ type fieldCandi struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func reflectValueCandi(v reflect.Value) (map[string]fieldCandi, error) {
|
func reflectValueCandi(v reflect.Value) (map[string]fieldCandi, error) {
|
||||||
if v.Kind() == reflect.Ptr {
|
// unref **/***/... struct{}
|
||||||
|
for v.Kind() == reflect.Ptr {
|
||||||
v = v.Elem()
|
v = v.Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make(map[string]fieldCandi)
|
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
case reflect.Map: // map[string]any
|
case reflect.Map: // map[string]any
|
||||||
|
return getMapReflectCandidates(v), nil
|
||||||
|
case reflect.Struct:
|
||||||
|
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()
|
iter := v.MapRange()
|
||||||
for iter.Next() {
|
for iter.Next() {
|
||||||
key := iter.Key().String()
|
key := iter.Key().String()
|
||||||
@ -291,13 +303,35 @@ func reflectValueCandi(v reflect.Value) (map[string]fieldCandi, error) {
|
|||||||
v: iter.Value(),
|
v: iter.Value(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result, nil
|
return result
|
||||||
case reflect.Struct:
|
}
|
||||||
|
|
||||||
|
// 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++ {
|
for i := 0; i < v.NumField(); i++ {
|
||||||
ft := v.Type().Field(i)
|
ft := v.Type().Field(i)
|
||||||
name := ft.Name
|
name := ft.Name
|
||||||
tag, ok := ft.Tag.Lookup(MilvusTag)
|
|
||||||
|
|
||||||
|
// 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)
|
settings := make(map[string]string)
|
||||||
if ok {
|
if ok {
|
||||||
if tag == MilvusSkipTagValue {
|
if tag == MilvusSkipTagValue {
|
||||||
@ -329,7 +363,4 @@ func reflectValueCandi(v reflect.Value) (map[string]fieldCandi, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupport row type: %s", v.Kind().String())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package row
|
package row
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -126,6 +127,10 @@ func (s *RowsSuite) TestDynamicSchema() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *RowsSuite) TestReflectValueCandi() {
|
func (s *RowsSuite) TestReflectValueCandi() {
|
||||||
|
type DynamicRows struct {
|
||||||
|
Float float32 `json:"float" milvus:"name:float"`
|
||||||
|
}
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
tag string
|
tag string
|
||||||
v reflect.Value
|
v reflect.Value
|
||||||
@ -149,6 +154,65 @@ func (s *RowsSuite) TestReflectValueCandi() {
|
|||||||
},
|
},
|
||||||
expectErr: false,
|
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 {
|
for _, c := range cases {
|
||||||
@ -162,7 +226,7 @@ func (s *RowsSuite) TestReflectValueCandi() {
|
|||||||
s.Equal(len(c.expect), len(r))
|
s.Equal(len(c.expect), len(r))
|
||||||
for k, v := range c.expect {
|
for k, v := range c.expect {
|
||||||
rv, has := r[k]
|
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)
|
s.Equal(v.name, rv.name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user