Repository: milvus-io/milvus-sdk-go
Pull request #823 (2024-09-20 04:36:04)

enhance: Support embedded struct field as Row input
Related to #818

sre-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/milvus-io/milvus-sdk-go/pull/823#" title="Author self-approved">congqixia</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=milvus-io%2Fmilvus-sdk-go).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/milvus-io/milvus-sdk-go/blob/master/OWNERS)~~ [congqixia]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
ThreadDao: /lgtm

Patch:
diff --git a/entity/rows.go b/entity/rows.go
index dc30af7..7577523 100644
--- a/entity/rows.go
+++ b/entity/rows.go
@@ -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
@@ -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),
 				}
@@ -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)
 


Test patch:
diff --git a/entity/rows_test.go b/entity/rows_test.go
index 19d182d..81c633d 100644
--- a/entity/rows_test.go
+++ b/entity/rows_test.go
@@ -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 {
@@ -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)
@@ -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))
@@ -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
@@ -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 {