Repository: milvus-io/milvus-sdk-go
Pull request #164 (2021-07-29 11:24:01)

Add SearchResult to Rows
Add SearchResult to Row util functions

Related issue: #161 

Signed-off-by: Congqi Xia <congqi.xia@zilliz.com>

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/164#" 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":[]} -->
sunby: /lgtm
/approve

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/164#" title="Author self-approved">congqixia</a>*, *<a href="https://github.com/milvus-io/milvus-sdk-go/pull/164#issuecomment-889037469" title="Approved">sunby</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,sunby]

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":[]} -->

Patch:
diff --git a/client/client_grpc_row.go b/client/client_grpc_row.go
new file mode 100644
index 0000000..a3a641a
--- /dev/null
+++ b/client/client_grpc_row.go
@@ -0,0 +1,219 @@
+package client
+
+import (
+	"errors"
+	"fmt"
+	"reflect"
+
+	"github.com/milvus-io/milvus-sdk-go/v2/entity"
+	"github.com/milvus-io/milvus-sdk-go/v2/internal/proto/schema"
+)
+
+type SearchResultByRows struct {
+	ResultCount int
+	Scores      []float32
+	Rows        []entity.Row
+	Err         error
+}
+
+func SearchResultToRows(sch *entity.Schema, results *schema.SearchResultData, t reflect.Type) {
+	offset := 0
+	sr := make([]SearchResultByRows, 0, results.GetNumQueries())
+	fieldDataList := results.GetFieldsData()
+	nameFieldData := make(map[string]*schema.FieldData)
+	for _, fieldData := range fieldDataList {
+		nameFieldData[fieldData.FieldName] = fieldData
+	}
+	ids := results.GetIds()
+	for i := 0; i < int(results.GetNumQueries()); i++ {
+		rc := int(results.GetTopks()[i]) // result entry count for current query
+		entry := SearchResultByRows{
+			ResultCount: rc,
+			Rows:        make([]entity.Row, 0, rc),
+			Scores:      results.GetScores()[offset:rc],
+		}
+		for j := 0; j < rc; j++ {
+			p := reflect.New(t)
+			v := p.Elem()
+
+			//extract primary field logic
+			for _, field := range sch.Fields {
+				f := v.FieldByName(field.Name)
+				if f.IsNil() {
+					//TODO check field in output list
+					continue
+				}
+				if field.PrimaryKey {
+					switch f.Kind() {
+					case reflect.Int64:
+						intIds := ids.GetIntId()
+						if intIds == nil {
+							entry.Err = fmt.Errorf("field %s is int64, but id column is not", field.Name)
+							break
+						}
+						f.SetInt(intIds.GetData()[offset+j])
+					case reflect.String:
+						strIds := ids.GetStrId()
+						if strIds == nil {
+							entry.Err = fmt.Errorf("field %s is string ,but id column is not", field.Name)
+							break
+						}
+						f.SetString(strIds.GetData()[offset+j])
+					default:
+						entry.Err = fmt.Errorf("field %s is not valid primary key", field.Name)
+						break
+					}
+					continue
+				}
+
+				//fieldDataList
+				fieldData, has := nameFieldData[field.Name]
+				if !has || fieldData == nil {
+					//TODO if check field in output list
+					continue
+				}
+
+				offset += rc
+				sr = append(sr, entry)
+			}
+
+		}
+	}
+}
+
+var (
+	ErrFieldTypeNotMatch = errors.New("field type not matched")
+)
+
+func SetFieldValue(field *entity.Field, f reflect.Value, fieldData *schema.FieldData, idx int) error {
+	scalars := fieldData.GetScalars()
+	vectors := fieldData.GetVectors()
+	switch field.DataType {
+	case entity.FieldTypeBool:
+		if f.Kind() != reflect.Bool {
+			return ErrFieldTypeNotMatch
+		}
+		if scalars == nil {
+			return ErrFieldTypeNotMatch
+		}
+		data := scalars.GetBoolData()
+		if data == nil {
+			return ErrFieldTypeNotMatch
+		}
+		f.SetBool(data.Data[idx])
+	case entity.FieldTypeInt8:
+		if f.Kind() != reflect.Int8 {
+			return ErrFieldTypeNotMatch
+		}
+		if scalars == nil {
+			return ErrFieldTypeNotMatch
+		}
+		data := scalars.GetIntData()
+		if data == nil {
+			return ErrFieldTypeNotMatch
+		}
+		f.SetInt(int64(data.Data[idx]))
+	case entity.FieldTypeInt16:
+		if f.Kind() != reflect.Int16 {
+			return ErrFieldTypeNotMatch
+		}
+		if scalars == nil {
+			return ErrFieldTypeNotMatch
+		}
+		data := scalars.GetIntData()
+		f.SetInt(int64(data.Data[idx]))
+	case entity.FieldTypeInt32:
+		if f.Kind() != reflect.Int32 {
+			return ErrFieldTypeNotMatch
+		}
+		if scalars == nil {
+			return ErrFieldTypeNotMatch
+		}
+		data := scalars.GetIntData()
+		f.SetInt(int64(data.Data[idx]))
+	case entity.FieldTypeInt64:
+		if f.Kind() != reflect.Int64 {
+			return ErrFieldTypeNotMatch
+		}
+		if scalars == nil {
+			return ErrFieldTypeNotMatch
+		}
+		data := scalars.GetIntData()
+		f.SetInt(int64(data.Data[idx]))
+
+	case entity.FieldTypeFloat:
+		if f.Kind() != reflect.Float32 {
+			return ErrFieldTypeNotMatch
+		}
+		if scalars == nil {
+			return ErrFieldTypeNotMatch
+		}
+		data := scalars.GetFloatData()
+		f.SetFloat(float64(data.Data[idx]))
+	case entity.FieldTypeDouble:
+		if f.Kind() != reflect.Float64 {
+			return ErrFieldTypeNotMatch
+		}
+		if scalars == nil {
+			return ErrFieldTypeNotMatch
+		}
+		data := scalars.GetDoubleData()
+		f.SetFloat(data.Data[idx])
+	case entity.FieldTypeString:
+		if f.Kind() != reflect.String {
+			return ErrFieldTypeNotMatch
+		}
+		if scalars == nil {
+			return ErrFieldTypeNotMatch
+		}
+		data := scalars.GetStringData()
+		f.SetString(data.Data[idx])
+
+	case entity.FieldTypeFloatVector:
+		if vectors == nil {
+			return ErrFieldTypeNotMatch
+		}
+		data := vectors.GetFloatVector()
+		if data == nil {
+
+			return ErrFieldTypeNotMatch
+		}
+		vector := data.Data[idx*int(vectors.Dim) : (idx+1)*int(vectors.Dim)]
+		switch f.Kind() {
+		case reflect.Slice:
+			f.Set(reflect.ValueOf(vector))
+		case reflect.Array:
+			arrType := reflect.ArrayOf(int(vectors.Dim), reflect.TypeOf(float32(0)))
+			arr := reflect.New(arrType).Elem()
+			for i := 0; i < int(vectors.Dim); i++ {
+				arr.Index(i).Set(reflect.ValueOf(vector[i]))
+			}
+			f.Set(arr)
+		default:
+			return ErrFieldTypeNotMatch
+		}
+	case entity.FieldTypeBinaryVector:
+		if vectors == nil {
+			return ErrFieldTypeNotMatch
+		}
+		data := vectors.GetBinaryVector()
+		if data == nil {
+			return ErrFieldTypeNotMatch
+		}
+		vector := data[idx*int(vectors.Dim/8) : (idx+1)*int(vectors.Dim/8)]
+		switch f.Kind() {
+		case reflect.Slice:
+			f.Set(reflect.ValueOf(vector))
+		case reflect.Array:
+			arrType := reflect.ArrayOf(int(vectors.Dim), reflect.TypeOf(byte(0)))
+			arr := reflect.New(arrType).Elem()
+			for i := 0; i < int(vectors.Dim/8); i++ {
+				arr.Index(i).Set(reflect.ValueOf(vector[i]))
+			}
+			f.Set(arr)
+		default:
+			return ErrFieldTypeNotMatch
+		}
+	}
+	return nil
+}


Test patch:
diff --git a/client/client_grpc_row_test.go b/client/client_grpc_row_test.go
new file mode 100644
index 0000000..9f8f4c8
--- /dev/null
+++ b/client/client_grpc_row_test.go
@@ -0,0 +1,91 @@
+package client
+
+import (
+	"reflect"
+	"testing"
+
+	"github.com/milvus-io/milvus-sdk-go/v2/entity"
+	"github.com/milvus-io/milvus-sdk-go/v2/internal/proto/schema"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestSetFieldValue(t *testing.T) {
+	type TestStruct struct {
+		Int8  int8
+		Int16 int16
+		Int32 int32
+		Int64 int64
+		Arr   [8]float32
+	}
+
+	var err error
+	item := &TestStruct{}
+	i8 := reflect.ValueOf(item).Elem().FieldByName("Int8")
+	err = SetFieldValue(&entity.Field{
+		DataType: entity.FieldTypeInt8,
+	}, i8, &schema.FieldData{
+		Field: &schema.FieldData_Scalars{
+			Scalars: &schema.ScalarField{
+				Data: &schema.ScalarField_IntData{
+					IntData: &schema.IntArray{
+						Data: []int32{10},
+					},
+				},
+			},
+		},
+	}, 0)
+	assert.Nil(t, err)
+	assert.EqualValues(t, 10, item.Int8)
+
+	i16 := reflect.ValueOf(item).Elem().FieldByName("Int16")
+	err = SetFieldValue(&entity.Field{
+		DataType: entity.FieldTypeInt16,
+	}, i16, &schema.FieldData{
+		Field: &schema.FieldData_Scalars{
+			Scalars: &schema.ScalarField{
+				Data: &schema.ScalarField_IntData{
+					IntData: &schema.IntArray{
+						Data: []int32{10},
+					},
+				},
+			},
+		},
+	}, 0)
+	assert.Nil(t, err)
+	assert.EqualValues(t, 10, item.Int16)
+
+	i32 := reflect.ValueOf(item).Elem().FieldByName("Int32")
+	err = SetFieldValue(&entity.Field{
+		DataType: entity.FieldTypeInt32,
+	}, i32, &schema.FieldData{
+		Field: &schema.FieldData_Scalars{
+			Scalars: &schema.ScalarField{
+				Data: &schema.ScalarField_IntData{
+					IntData: &schema.IntArray{
+						Data: []int32{10},
+					},
+				},
+			},
+		},
+	}, 0)
+	assert.Nil(t, err)
+	assert.EqualValues(t, 10, item.Int32)
+
+	arr := reflect.ValueOf(item).Elem().FieldByName("Arr")
+	err = SetFieldValue(&entity.Field{
+		DataType: entity.FieldTypeFloatVector,
+	}, arr, &schema.FieldData{
+		Field: &schema.FieldData_Vectors{
+			Vectors: &schema.VectorField{
+				Dim: 8,
+				Data: &schema.VectorField_FloatVector{
+					FloatVector: &schema.FloatArray{
+						Data: []float32{0, 1, 2, 3, 4, 5, 6, 7},
+					},
+				},
+			},
+		},
+	}, 0)
+	assert.Nil(t, err)
+	assert.EqualValues(t, [8]float32{0, 1, 2, 3, 4, 5, 6, 7}, item.Arr)
+}