Skip to content

Commit

Permalink
enhance: Support embedded struct field as Row input (#823)
Browse files Browse the repository at this point in the history
Related to #818

Signed-off-by: Congqi Xia <[email protected]>
  • Loading branch information
congqixia authored Sep 20, 2024
1 parent 110516d commit 99efce9
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 6 deletions.
20 changes: 18 additions & 2 deletions entity/rows.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const (
// MilvusTagSep struct tag const for attribute separator
MilvusTagSep = `;`

//MilvusTagName struct tag const for field name
// MilvusTagName struct tag const for field name
MilvusTagName = `NAME`

// VectorDimTag struct tag const for vector dimension
Expand Down Expand Up @@ -171,7 +171,7 @@ func ParseSchemaAny(r interface{}) (*Schema, error) {
switch elemType.Kind() {
case reflect.Uint8:
field.DataType = FieldTypeBinaryVector
//TODO maybe override by tag settings, when dim is not multiplier of 8
// TODO maybe override by tag settings, when dim is not multiplier of 8
field.TypeParams = map[string]string{
TypeParamDim: strconv.FormatInt(int64(arrayLen*8), 10),
}
Expand Down Expand Up @@ -526,6 +526,22 @@ func reflectValueCandi(v reflect.Value) (map[string]fieldCandi, error) {
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
ft := v.Type().Field(i)
// embedded struct
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 {
_, 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
}

name := ft.Name
tag, ok := ft.Tag.Lookup(MilvusTag)

Expand Down
44 changes: 40 additions & 4 deletions entity/rows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ type SliceBadDimStruct2 struct {
}

func TestParseSchema(t *testing.T) {

t.Run("invalid cases", func(t *testing.T) {
// anonymous struct with default collection name ("") will cause error
anonymusStruct := struct {
Expand Down Expand Up @@ -108,11 +107,9 @@ func TestParseSchema(t *testing.T) {
sch, err = ParseSchema(&SliceBadDimStruct2{})
assert.Nil(t, sch)
assert.NotNil(t, err)

})

t.Run("valid cases", func(t *testing.T) {

sch, err := ParseSchema(RowBase{})
assert.Nil(t, err)
assert.Equal(t, "RowBase", sch.CollectionName)
Expand Down Expand Up @@ -268,7 +265,6 @@ type RowsSuite struct {

func (s *RowsSuite) TestRowsToColumns() {
s.Run("valid_cases", func() {

columns, err := RowsToColumns([]Row{&ValidStruct{}})
s.Nil(err)
s.Equal(10, len(columns))
Expand Down Expand Up @@ -358,6 +354,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
Expand All @@ -381,6 +381,42 @@ 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_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,
},
}

for _, c := range cases {
Expand Down

0 comments on commit 99efce9

Please sign in to comment.