Repository: milvus-io/milvus-sdk-go
Pull request #732 (2024-04-18 08:42:52)

fix: Return empty resultset for empty nq and related testcases

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

This pull-request has been approved by: *<a href="https://github.com/milvus-io/milvus-sdk-go/pull/732#" title="Author self-approved">ThreadDao</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **yah01** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @yah01` in a comment when ready.

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).

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

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

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":["yah01"]} -->
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/milvus-io/milvus-sdk-go/pull/732?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=milvus-io) Report
Attention: Patch coverage is `67.81609%` with `28 lines` in your changes are missing coverage. Please review.
> Project coverage is 71.98%. Comparing base [(`1c53c8f`)](https://app.codecov.io/gh/milvus-io/milvus-sdk-go/commit/1c53c8fcdac675fd754df00c8f89fe5d74671933?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=milvus-io) to head [(`5b00816`)](https://app.codecov.io/gh/milvus-io/milvus-sdk-go/pull/732?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=milvus-io).

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/milvus-io/milvus-sdk-go/pull/732/graphs/tree.svg?width=650&height=150&src=pr&token=WJK43PV390&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=milvus-io)](https://app.codecov.io/gh/milvus-io/milvus-sdk-go/pull/732?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=milvus-io)

```diff
@@            Coverage Diff             @@
##           master     #732      +/-   ##
==========================================
- Coverage   72.01%   71.98%   -0.03%     
==========================================
  Files          51       51              
  Lines        4742     4755      +13     
==========================================
+ Hits         3415     3423       +8     
- Misses       1206     1209       +3     
- Partials      121      123       +2     
```


| [Files](https://app.codecov.io/gh/milvus-io/milvus-sdk-go/pull/732?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=milvus-io) | Coverage Δ | |
|---|---|---|
| [client/insert.go](https://app.codecov.io/gh/milvus-io/milvus-sdk-go/pull/732?src=pr&el=tree&filepath=client%2Finsert.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=milvus-io#diff-Y2xpZW50L2luc2VydC5nbw==) | `78.47% <100.00%> (ø)` | |
| [client/row.go](https://app.codecov.io/gh/milvus-io/milvus-sdk-go/pull/732?src=pr&el=tree&filepath=client%2Frow.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=milvus-io#diff-Y2xpZW50L3Jvdy5nbw==) | `86.42% <100.00%> (ø)` | |
| [entity/schema.go](https://app.codecov.io/gh/milvus-io/milvus-sdk-go/pull/732?src=pr&el=tree&filepath=entity%2Fschema.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=milvus-io#diff-ZW50aXR5L3NjaGVtYS5nbw==) | `92.03% <100.00%> (+0.24%)` | :arrow_up: |
| [client/data.go](https://app.codecov.io/gh/milvus-io/milvus-sdk-go/pull/732?src=pr&el=tree&filepath=client%2Fdata.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=milvus-io#diff-Y2xpZW50L2RhdGEuZ28=) | `64.33% <71.42%> (+0.07%)` | :arrow_up: |
| [entity/columns.go](https://app.codecov.io/gh/milvus-io/milvus-sdk-go/pull/732?src=pr&el=tree&filepath=entity%2Fcolumns.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=milvus-io#diff-ZW50aXR5L2NvbHVtbnMuZ28=) | `46.31% <61.19%> (-0.36%)` | :arrow_down: |

</details>
congqixia: /kind bug
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/732#pullrequestreview-2010386388" title="LGTM">congqixia</a>*, *<a href="https://github.com/milvus-io/milvus-sdk-go/pull/732#" title="Author self-approved">ThreadDao</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":[]} -->

Patch:
diff --git a/client/data.go b/client/data.go
index e6b526e..4cb19b2 100644
--- a/client/data.go
+++ b/client/data.go
@@ -143,25 +143,20 @@ func (c *GrpcClient) handleSearchResult(schema *entity.Schema, outputFields []st
 			Scores:      results.GetScores()[offset : offset+rc],
 		}
 
-		// parse result set if current nq is not empty
-		if rc > 0 {
-			entry.IDs, entry.Err = entity.IDColumns(results.GetIds(), offset, offset+rc)
+		entry.IDs, entry.Err = entity.IDColumns(schema, results.GetIds(), offset, offset+rc)
+		if entry.Err != nil {
+			continue
+		}
+		// parse group-by values
+		if gb != nil {
+			entry.GroupByValue, entry.Err = entity.FieldDataColumn(gb, offset, offset+rc)
 			if entry.Err != nil {
 				offset += rc
 				continue
 			}
-			// parse group-by values
-			if gb != nil {
-				entry.GroupByValue, entry.Err = entity.FieldDataColumn(gb, offset, offset+rc)
-				if entry.Err != nil {
-					offset += rc
-					continue
-				}
-			}
-			// entry.GroupByValue, entry.Err = c.parseSearchResult()
-			entry.Fields, entry.Err = c.parseSearchResult(schema, outputFields, fieldDataList, i, offset, offset+rc)
-			sr = append(sr, entry)
 		}
+		entry.Fields, entry.Err = c.parseSearchResult(schema, outputFields, fieldDataList, i, offset, offset+rc)
+		sr = append(sr, entry)
 
 		offset += rc
 	}
diff --git a/client/insert.go b/client/insert.go
index d233012..2f52f0e 100644
--- a/client/insert.go
+++ b/client/insert.go
@@ -77,7 +77,7 @@ func (c *GrpcClient) Insert(ctx context.Context, collName string, partitionName
 	}
 	MetaCache.setSessionTs(collName, resp.Timestamp)
 	// 3. parse id column
-	return entity.IDColumns(resp.GetIDs(), 0, -1)
+	return entity.IDColumns(coll.Schema, resp.GetIDs(), 0, -1)
 }
 
 func (c *GrpcClient) processInsertColumns(colSchema *entity.Schema, columns ...entity.Column) ([]*schemapb.FieldData, int, error) {
@@ -392,7 +392,7 @@ func (c *GrpcClient) Upsert(ctx context.Context, collName string, partitionName
 	}
 	MetaCache.setSessionTs(collName, resp.Timestamp)
 	// 3. parse id column
-	return entity.IDColumns(resp.GetIDs(), 0, -1)
+	return entity.IDColumns(coll.Schema, resp.GetIDs(), 0, -1)
 }
 
 // BulkInsert data files(json, numpy, etc.) on MinIO/S3 storage, read and parse them into sealed segments
diff --git a/client/row.go b/client/row.go
index 42001ea..64ac5c0 100644
--- a/client/row.go
+++ b/client/row.go
@@ -118,7 +118,7 @@ func (c *GrpcClient) InsertRows(ctx context.Context, collName string, partitionN
 	}
 	MetaCache.setSessionTs(collName, resp.Timestamp)
 	// 3. parse id column
-	return entity.IDColumns(resp.GetIDs(), 0, -1)
+	return entity.IDColumns(coll.Schema, resp.GetIDs(), 0, -1)
 }
 
 // SearchResultByRows search result for row-based Search
diff --git a/entity/columns.go b/entity/columns.go
index 27e6b93..8c06cee 100644
--- a/entity/columns.go
+++ b/entity/columns.go
@@ -16,7 +16,7 @@ import (
 	"fmt"
 	"math"
 
-	schema "github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
+	"github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
 
 	"github.com/cockroachdb/errors"
 )
@@ -28,7 +28,7 @@ type Column interface {
 	Name() string
 	Type() FieldType
 	Len() int
-	FieldData() *schema.FieldData
+	FieldData() *schemapb.FieldData
 	AppendValue(interface{}) error
 	Get(int) (interface{}, error)
 	GetAsInt64(int) (int64, error)
@@ -142,38 +142,51 @@ func (bv BinaryVector) FieldType() FieldType {
 
 var errFieldDataTypeNotMatch = errors.New("FieldData type not matched")
 
-// IDColumns converts schema.IDs to corresponding column
+// IDColumns converts schemapb.IDs to corresponding column
 // currently Int64 / string may be in IDs
-func IDColumns(idField *schema.IDs, begin, end int) (Column, error) {
+func IDColumns(schema *Schema, idField *schemapb.IDs, begin, end int) (Column, error) {
 	var idColumn Column
-	if idField == nil {
-		return nil, errors.New("nil Ids from response")
+
+	pkField := schema.PKField()
+	if pkField == nil {
+		return nil, errors.New("PK Field not found")
 	}
-	switch field := idField.GetIdField().(type) {
-	case *schema.IDs_IntId:
+	switch pkField.DataType {
+	case FieldTypeInt64:
+		data := idField.GetIntId().GetData()
+		if data == nil {
+			return NewColumnInt64(pkField.Name, nil), nil
+		}
 		if end >= 0 {
-			idColumn = NewColumnInt64("", field.IntId.GetData()[begin:end])
+			idColumn = NewColumnInt64(pkField.Name, data[begin:end])
 		} else {
-			idColumn = NewColumnInt64("", field.IntId.GetData()[begin:])
+			idColumn = NewColumnInt64(pkField.Name, data[begin:])
+		}
+	case FieldTypeVarChar, FieldTypeString:
+		data := idField.GetStrId().GetData()
+		if data == nil {
+			return NewColumnVarChar(pkField.Name, nil), nil
 		}
-	case *schema.IDs_StrId:
 		if end >= 0 {
-			idColumn = NewColumnVarChar("", field.StrId.GetData()[begin:end])
+			idColumn = NewColumnVarChar(pkField.Name, data[begin:end])
 		} else {
-			idColumn = NewColumnVarChar("", field.StrId.GetData()[begin:])
+			idColumn = NewColumnVarChar(pkField.Name, data[begin:])
 		}
 	default:
-		return nil, fmt.Errorf("unsupported id type %v", field)
+		return nil, fmt.Errorf("unsupported id type %v", pkField.DataType)
+	}
+	if idField == nil {
+		return nil, errors.New("nil Ids from response")
 	}
 	return idColumn, nil
 }
 
-// FieldDataColumn converts schema.FieldData to Column, used int search result conversion logic
+// FieldDataColumn converts schemapb.FieldData to Column, used int search result conversion logic
 // begin, end specifies the start and end positions
-func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
+func FieldDataColumn(fd *schemapb.FieldData, begin, end int) (Column, error) {
 	switch fd.GetType() {
-	case schema.DataType_Bool:
-		data, ok := fd.GetScalars().GetData().(*schema.ScalarField_BoolData)
+	case schemapb.DataType_Bool:
+		data, ok := fd.GetScalars().GetData().(*schemapb.ScalarField_BoolData)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
 		}
@@ -182,7 +195,7 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 		}
 		return NewColumnBool(fd.GetFieldName(), data.BoolData.GetData()[begin:end]), nil
 
-	case schema.DataType_Int8:
+	case schemapb.DataType_Int8:
 		data, ok := getIntData(fd)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
@@ -198,7 +211,7 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 
 		return NewColumnInt8(fd.GetFieldName(), values[begin:end]), nil
 
-	case schema.DataType_Int16:
+	case schemapb.DataType_Int16:
 		data, ok := getIntData(fd)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
@@ -213,7 +226,7 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 
 		return NewColumnInt16(fd.GetFieldName(), values[begin:end]), nil
 
-	case schema.DataType_Int32:
+	case schemapb.DataType_Int32:
 		data, ok := getIntData(fd)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
@@ -223,8 +236,8 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 		}
 		return NewColumnInt32(fd.GetFieldName(), data.IntData.GetData()[begin:end]), nil
 
-	case schema.DataType_Int64:
-		data, ok := fd.GetScalars().GetData().(*schema.ScalarField_LongData)
+	case schemapb.DataType_Int64:
+		data, ok := fd.GetScalars().GetData().(*schemapb.ScalarField_LongData)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
 		}
@@ -233,8 +246,8 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 		}
 		return NewColumnInt64(fd.GetFieldName(), data.LongData.GetData()[begin:end]), nil
 
-	case schema.DataType_Float:
-		data, ok := fd.GetScalars().GetData().(*schema.ScalarField_FloatData)
+	case schemapb.DataType_Float:
+		data, ok := fd.GetScalars().GetData().(*schemapb.ScalarField_FloatData)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
 		}
@@ -243,8 +256,8 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 		}
 		return NewColumnFloat(fd.GetFieldName(), data.FloatData.GetData()[begin:end]), nil
 
-	case schema.DataType_Double:
-		data, ok := fd.GetScalars().GetData().(*schema.ScalarField_DoubleData)
+	case schemapb.DataType_Double:
+		data, ok := fd.GetScalars().GetData().(*schemapb.ScalarField_DoubleData)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
 		}
@@ -253,8 +266,8 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 		}
 		return NewColumnDouble(fd.GetFieldName(), data.DoubleData.GetData()[begin:end]), nil
 
-	case schema.DataType_String:
-		data, ok := fd.GetScalars().GetData().(*schema.ScalarField_StringData)
+	case schemapb.DataType_String:
+		data, ok := fd.GetScalars().GetData().(*schemapb.ScalarField_StringData)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
 		}
@@ -263,8 +276,8 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 		}
 		return NewColumnString(fd.GetFieldName(), data.StringData.GetData()[begin:end]), nil
 
-	case schema.DataType_VarChar:
-		data, ok := fd.GetScalars().GetData().(*schema.ScalarField_StringData)
+	case schemapb.DataType_VarChar:
+		data, ok := fd.GetScalars().GetData().(*schemapb.ScalarField_StringData)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
 		}
@@ -273,12 +286,12 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 		}
 		return NewColumnVarChar(fd.GetFieldName(), data.StringData.GetData()[begin:end]), nil
 
-	case schema.DataType_Array:
+	case schemapb.DataType_Array:
 		data := fd.GetScalars().GetArrayData()
 		if data == nil {
 			return nil, errFieldDataTypeNotMatch
 		}
-		var arrayData []*schema.ScalarField
+		var arrayData []*schemapb.ScalarField
 		if end < 0 {
 			arrayData = data.GetData()[begin:]
 		} else {
@@ -287,8 +300,8 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 
 		return parseArrayData(fd.GetFieldName(), data.GetElementType(), arrayData)
 
-	case schema.DataType_JSON:
-		data, ok := fd.GetScalars().GetData().(*schema.ScalarField_JsonData)
+	case schemapb.DataType_JSON:
+		data, ok := fd.GetScalars().GetData().(*schemapb.ScalarField_JsonData)
 		isDynamic := fd.GetIsDynamic()
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
@@ -298,9 +311,9 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 		}
 		return NewColumnJSONBytes(fd.GetFieldName(), data.JsonData.GetData()[begin:end]).WithIsDynamic(isDynamic), nil
 
-	case schema.DataType_FloatVector:
+	case schemapb.DataType_FloatVector:
 		vectors := fd.GetVectors()
-		x, ok := vectors.GetData().(*schema.VectorField_FloatVector)
+		x, ok := vectors.GetData().(*schemapb.VectorField_FloatVector)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
 		}
@@ -317,9 +330,9 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 		}
 		return NewColumnFloatVector(fd.GetFieldName(), dim, vector), nil
 
-	case schema.DataType_BinaryVector:
+	case schemapb.DataType_BinaryVector:
 		vectors := fd.GetVectors()
-		x, ok := vectors.GetData().(*schema.VectorField_BinaryVector)
+		x, ok := vectors.GetData().(*schemapb.VectorField_BinaryVector)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
 		}
@@ -340,9 +353,9 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 		}
 		return NewColumnBinaryVector(fd.GetFieldName(), dim, vector), nil
 
-	case schema.DataType_Float16Vector:
+	case schemapb.DataType_Float16Vector:
 		vectors := fd.GetVectors()
-		x, ok := vectors.GetData().(*schema.VectorField_Float16Vector)
+		x, ok := vectors.GetData().(*schemapb.VectorField_Float16Vector)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
 		}
@@ -359,9 +372,9 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 		}
 		return NewColumnFloat16Vector(fd.GetFieldName(), dim, vector), nil
 
-	case schema.DataType_BFloat16Vector:
+	case schemapb.DataType_BFloat16Vector:
 		vectors := fd.GetVectors()
-		x, ok := vectors.GetData().(*schema.VectorField_Bfloat16Vector)
+		x, ok := vectors.GetData().(*schemapb.VectorField_Bfloat16Vector)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
 		}
@@ -377,7 +390,7 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 			vector = append(vector, v)
 		}
 		return NewColumnBFloat16Vector(fd.GetFieldName(), dim, vector), nil
-	case schema.DataType_SparseFloatVector:
+	case schemapb.DataType_SparseFloatVector:
 		sparseVectors := fd.GetVectors().GetSparseFloatVector()
 		if sparseVectors == nil {
 			return nil, errFieldDataTypeNotMatch
@@ -401,17 +414,17 @@ func FieldDataColumn(fd *schema.FieldData, begin, end int) (Column, error) {
 	}
 }
 
-func parseArrayData(fieldName string, elementType schema.DataType, fieldDataList []*schema.ScalarField) (Column, error) {
+func parseArrayData(fieldName string, elementType schemapb.DataType, fieldDataList []*schemapb.ScalarField) (Column, error) {
 
 	switch elementType {
-	case schema.DataType_Bool:
+	case schemapb.DataType_Bool:
 		var data [][]bool
 		for _, fd := range fieldDataList {
 			data = append(data, fd.GetBoolData().GetData())
 		}
 		return NewColumnBoolArray(fieldName, data), nil
 
-	case schema.DataType_Int8:
+	case schemapb.DataType_Int8:
 		var data [][]int8
 		for _, fd := range fieldDataList {
 			raw := fd.GetIntData().GetData()
@@ -423,7 +436,7 @@ func parseArrayData(fieldName string, elementType schema.DataType, fieldDataList
 		}
 		return NewColumnInt8Array(fieldName, data), nil
 
-	case schema.DataType_Int16:
+	case schemapb.DataType_Int16:
 		var data [][]int16
 		for _, fd := range fieldDataList {
 			raw := fd.GetIntData().GetData()
@@ -435,35 +448,35 @@ func parseArrayData(fieldName string, elementType schema.DataType, fieldDataList
 		}
 		return NewColumnInt16Array(fieldName, data), nil
 
-	case schema.DataType_Int32:
+	case schemapb.DataType_Int32:
 		var data [][]int32
 		for _, fd := range fieldDataList {
 			data = append(data, fd.GetIntData().GetData())
 		}
 		return NewColumnInt32Array(fieldName, data), nil
 
-	case schema.DataType_Int64:
+	case schemapb.DataType_Int64:
 		var data [][]int64
 		for _, fd := range fieldDataList {
 			data = append(data, fd.GetLongData().GetData())
 		}
 		return NewColumnInt64Array(fieldName, data), nil
 
-	case schema.DataType_Float:
+	case schemapb.DataType_Float:
 		var data [][]float32
 		for _, fd := range fieldDataList {
 			data = append(data, fd.GetFloatData().GetData())
 		}
 		return NewColumnFloatArray(fieldName, data), nil
 
-	case schema.DataType_Double:
+	case schemapb.DataType_Double:
 		var data [][]float64
 		for _, fd := range fieldDataList {
 			data = append(data, fd.GetDoubleData().GetData())
 		}
 		return NewColumnDoubleArray(fieldName, data), nil
 
-	case schema.DataType_VarChar, schema.DataType_String:
+	case schemapb.DataType_VarChar, schemapb.DataType_String:
 		var data [][][]byte
 		for _, fd := range fieldDataList {
 			strs := fd.GetStringData().GetData()
@@ -483,15 +496,15 @@ func parseArrayData(fieldName string, elementType schema.DataType, fieldDataList
 
 // getIntData get int32 slice from result field data
 // also handles LongData bug (see also https://github.com/milvus-io/milvus/issues/23850)
-func getIntData(fd *schema.FieldData) (*schema.ScalarField_IntData, bool) {
+func getIntData(fd *schemapb.FieldData) (*schemapb.ScalarField_IntData, bool) {
 	switch data := fd.GetScalars().GetData().(type) {
-	case *schema.ScalarField_IntData:
+	case *schemapb.ScalarField_IntData:
 		return data, true
-	case *schema.ScalarField_LongData:
+	case *schemapb.ScalarField_LongData:
 		// only alway empty LongData for backward compatibility
 		if len(data.LongData.GetData()) == 0 {
-			return &schema.ScalarField_IntData{
-				IntData: &schema.IntArray{},
+			return &schemapb.ScalarField_IntData{
+				IntData: &schemapb.IntArray{},
 			}, true
 		}
 		return nil, false
@@ -500,12 +513,12 @@ func getIntData(fd *schema.FieldData) (*schema.ScalarField_IntData, bool) {
 	}
 }
 
-// FieldDataColumn converts schema.FieldData to vector Column
-func FieldDataVector(fd *schema.FieldData) (Column, error) {
+// FieldDataColumn converts schemapb.FieldData to vector Column
+func FieldDataVector(fd *schemapb.FieldData) (Column, error) {
 	switch fd.GetType() {
-	case schema.DataType_FloatVector:
+	case schemapb.DataType_FloatVector:
 		vectors := fd.GetVectors()
-		x, ok := vectors.GetData().(*schema.VectorField_FloatVector)
+		x, ok := vectors.GetData().(*schemapb.VectorField_FloatVector)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
 		}
@@ -518,9 +531,9 @@ func FieldDataVector(fd *schema.FieldData) (Column, error) {
 			vector = append(vector, v)
 		}
 		return NewColumnFloatVector(fd.GetFieldName(), dim, vector), nil
-	case schema.DataType_BinaryVector:
+	case schemapb.DataType_BinaryVector:
 		vectors := fd.GetVectors()
-		x, ok := vectors.GetData().(*schema.VectorField_BinaryVector)
+		x, ok := vectors.GetData().(*schemapb.VectorField_BinaryVector)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
 		}
@@ -537,9 +550,9 @@ func FieldDataVector(fd *schema.FieldData) (Column, error) {
 			vector = append(vector, v)
 		}
 		return NewColumnBinaryVector(fd.GetFieldName(), dim, vector), nil
-	case schema.DataType_Float16Vector:
+	case schemapb.DataType_Float16Vector:
 		vectors := fd.GetVectors()
-		x, ok := vectors.GetData().(*schema.VectorField_Float16Vector)
+		x, ok := vectors.GetData().(*schemapb.VectorField_Float16Vector)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
 		}
@@ -552,9 +565,9 @@ func FieldDataVector(fd *schema.FieldData) (Column, error) {
 			vector = append(vector, v)
 		}
 		return NewColumnFloat16Vector(fd.GetFieldName(), dim, vector), nil
-	case schema.DataType_BFloat16Vector:
+	case schemapb.DataType_BFloat16Vector:
 		vectors := fd.GetVectors()
-		x, ok := vectors.GetData().(*schema.VectorField_Bfloat16Vector)
+		x, ok := vectors.GetData().(*schemapb.VectorField_Bfloat16Vector)
 		if !ok {
 			return nil, errFieldDataTypeNotMatch
 		}
diff --git a/entity/schema.go b/entity/schema.go
index d9395f3..6b31bc0 100644
--- a/entity/schema.go
+++ b/entity/schema.go
@@ -55,6 +55,7 @@ type Schema struct {
 	AutoID             bool
 	Fields             []*Field
 	EnableDynamicField bool
+	pkField            *Field
 }
 
 // NewSchema creates an empty schema object.
@@ -86,6 +87,9 @@ func (s *Schema) WithDynamicFieldEnabled(dynamicEnabled bool) *Schema {
 
 // WithField adds a field into schema and returns schema itself.
 func (s *Schema) WithField(f *Field) *Schema {
+	if f.PrimaryKey {
+		s.pkField = f
+	}
 	s.Fields = append(s.Fields, f)
 	return s
 }
@@ -112,7 +116,11 @@ func (s *Schema) ReadProto(p *schema.CollectionSchema) *Schema {
 	s.CollectionName = p.GetName()
 	s.Fields = make([]*Field, 0, len(p.GetFields()))
 	for _, fp := range p.GetFields() {
-		s.Fields = append(s.Fields, NewField().ReadProto(fp))
+		field := NewField().ReadProto(fp)
+		if field.PrimaryKey {
+			s.pkField = field
+		}
+		s.Fields = append(s.Fields, field)
 	}
 	s.EnableDynamicField = p.GetEnableDynamicField()
 	return s
@@ -120,12 +128,15 @@ func (s *Schema) ReadProto(p *schema.CollectionSchema) *Schema {
 
 // PKFieldName returns pk field name for this schema.
 func (s *Schema) PKFieldName() string {
-	for _, field := range s.Fields {
-		if field.PrimaryKey {
-			return field.Name
-		}
+	if s.pkField == nil {
+		return ""
 	}
-	return ""
+	return s.pkField.Name
+}
+
+// PKField returns PK Field schema for this schema.
+func (s *Schema) PKField() *Field {
+	return s.pkField
 }
 
 // Field represent field schema in milvus


Test patch:
diff --git a/entity/columns_test.go b/entity/columns_test.go
index 833d788..c9b79c1 100644
--- a/entity/columns_test.go
+++ b/entity/columns_test.go
@@ -42,12 +42,27 @@ func TestIDColumns(t *testing.T) {
 	dataLen := rand.Intn(100) + 1
 	base := rand.Intn(5000) // id start point
 
+	intPKCol := NewSchema().WithField(
+		NewField().WithName("pk").WithIsPrimaryKey(true).WithDataType(FieldTypeInt64),
+	)
+	strPKCol := NewSchema().WithField(
+		NewField().WithName("pk").WithIsPrimaryKey(true).WithDataType(FieldTypeVarChar),
+	)
+
 	t.Run("nil id", func(t *testing.T) {
-		_, err := IDColumns(nil, 0, -1)
-		assert.NotNil(t, err)
+		col, err := IDColumns(intPKCol, nil, 0, -1)
+		assert.NoError(t, err)
+		assert.EqualValues(t, 0, col.Len())
+		col, err = IDColumns(strPKCol, nil, 0, -1)
+		assert.NoError(t, err)
+		assert.EqualValues(t, 0, col.Len())
 		idField := &schema.IDs{}
-		_, err = IDColumns(idField, 0, -1)
-		assert.NotNil(t, err)
+		col, err = IDColumns(intPKCol, idField, 0, -1)
+		assert.NoError(t, err)
+		assert.EqualValues(t, 0, col.Len())
+		col, err = IDColumns(strPKCol, idField, 0, -1)
+		assert.NoError(t, err)
+		assert.EqualValues(t, 0, col.Len())
 	})
 
 	t.Run("int ids", func(t *testing.T) {
@@ -62,12 +77,12 @@ func TestIDColumns(t *testing.T) {
 				},
 			},
 		}
-		column, err := IDColumns(idField, 0, dataLen)
+		column, err := IDColumns(intPKCol, idField, 0, dataLen)
 		assert.Nil(t, err)
 		assert.NotNil(t, column)
 		assert.Equal(t, dataLen, column.Len())
 
-		column, err = IDColumns(idField, 0, -1) // test -1 method
+		column, err = IDColumns(intPKCol, idField, 0, -1) // test -1 method
 		assert.Nil(t, err)
 		assert.NotNil(t, column)
 		assert.Equal(t, dataLen, column.Len())
@@ -84,12 +99,12 @@ func TestIDColumns(t *testing.T) {
 				},
 			},
 		}
-		column, err := IDColumns(idField, 0, dataLen)
+		column, err := IDColumns(strPKCol, idField, 0, dataLen)
 		assert.Nil(t, err)
 		assert.NotNil(t, column)
 		assert.Equal(t, dataLen, column.Len())
 
-		column, err = IDColumns(idField, 0, -1) // test -1 method
+		column, err = IDColumns(strPKCol, idField, 0, -1) // test -1 method
 		assert.Nil(t, err)
 		assert.NotNil(t, column)
 		assert.Equal(t, dataLen, column.Len())
diff --git a/test/testcases/groupby_search_test.go b/test/testcases/groupby_search_test.go
index 04ee32f..0b26321 100644
--- a/test/testcases/groupby_search_test.go
+++ b/test/testcases/groupby_search_test.go
@@ -134,20 +134,19 @@ func TestSearchGroupByFloatDefault(t *testing.T) {
 				// verify each topK entity is the top1 of the whole group
 				hitsNum := 0
 				total := 0
-				for _, rs := range resGroupBy {
-					for i := 0; i < rs.ResultCount; i++ {
-						groupByValue, _ := rs.GroupByValue.Get(i)
-						pkValue, _ := rs.IDs.GetAsInt64(i)
+				for i := 0; i < common.DefaultNq; i++ {
+					for j := 0; j < resGroupBy[i].ResultCount; j++ {
+						groupByValue, _ := resGroupBy[i].GroupByValue.Get(j)
+						pkValue, _ := resGroupBy[i].IDs.GetAsInt64(j)
 						var expr string
 						if groupByField == "varchar" {
 							expr = fmt.Sprintf("%s == '%v' ", groupByField, groupByValue)
 						} else {
 							expr = fmt.Sprintf("%s == %v", groupByField, groupByValue)
 						}
-
 						// 	search filter with groupByValue is the top1
 						resFilter, _ := mc.Search(ctx, collName, []string{}, expr, []string{common.DefaultIntFieldName,
-							groupByField}, queryVec, common.DefaultFloatVecFieldName, metricType, 1, sp)
+							groupByField}, []entity.Vector{queryVec[i]}, common.DefaultFloatVecFieldName, metricType, 1, sp)
 						filterTop1Pk, _ := resFilter[0].IDs.GetAsInt64(0)
 						//log.Printf("Search top1 with %s: groupByValue: %v, pkValue: %d. The returned pk by filter search is: %d",
 						//	groupByField, groupByValue, pkValue, filterTop1Pk)
@@ -209,27 +208,30 @@ func TestGroupBySearchSparseVector(t *testing.T) {
 		// verify each topK entity is the top1 of the whole group
 		hitsNum := 0
 		total := 0
-		for _, rs := range resGroupBy {
-			for i := 0; i < rs.ResultCount; i++ {
-				groupByValue, _ := rs.GroupByValue.Get(i)
-				pkValue, _ := rs.IDs.GetAsInt64(i)
-				expr := fmt.Sprintf("%s == '%v' ", common.DefaultVarcharFieldName, groupByValue)
-
-				// 	search filter with groupByValue is the top1
-				resFilter, _ := mc.Search(ctx, collName, []string{}, expr, []string{common.DefaultIntFieldName,
-					common.DefaultVarcharFieldName}, queryVec, common.DefaultSparseVecFieldName, entity.IP, 1, sp)
-				filterTop1Pk, _ := resFilter[0].IDs.GetAsInt64(0)
-				if filterTop1Pk == pkValue {
-					hitsNum += 1
+		for i := 0; i < common.DefaultNq; i++ {
+			if resGroupBy[i].ResultCount > 0 {
+				for j := 0; j < resGroupBy[i].ResultCount; j++ {
+					groupByValue, _ := resGroupBy[i].GroupByValue.Get(j)
+					pkValue, _ := resGroupBy[i].IDs.GetAsInt64(j)
+					expr := fmt.Sprintf("%s == '%v' ", common.DefaultVarcharFieldName, groupByValue)
+					// 	search filter with groupByValue is the top1
+					resFilter, _ := mc.Search(ctx, collName, []string{}, expr, []string{common.DefaultIntFieldName,
+						common.DefaultVarcharFieldName}, []entity.Vector{queryVec[i]}, common.DefaultSparseVecFieldName, entity.IP, 1, sp)
+					filterTop1Pk, _ := resFilter[0].IDs.GetAsInt64(0)
+					log.Printf("Search top1 with %s: groupByValue: %v, pkValue: %d. The returned pk by filter search is: %d",
+						common.DefaultVarcharFieldName, groupByValue, pkValue, filterTop1Pk)
+					if filterTop1Pk == pkValue {
+						hitsNum += 1
+					}
+					total += 1
 				}
-				total += 1
 			}
 		}
 
 		// verify hits rate
 		hitsRate := float32(hitsNum) / float32(total)
 		_str := fmt.Sprintf("GroupBy search with field %s, nq=%d and limit=%d , then hitsNum= %d, hitsRate=%v\n",
-			common.DefaultSparseVecFieldName, common.DefaultNq, common.DefaultTopK, hitsNum, hitsRate)
+			common.DefaultVarcharFieldName, common.DefaultNq, common.DefaultTopK, hitsNum, hitsRate)
 		log.Println(_str)
 		require.GreaterOrEqualf(t, hitsRate, float32(0.8), _str)
 	}
@@ -340,19 +342,18 @@ func TestSearchGroupByFloatGrowing(t *testing.T) {
 				client.WithSearchQueryConsistencyLevel(entity.ClStrong))
 
 			// verify each topK entity is the top1 in the group
-			for _, rs := range resGroupBy {
-				for i := 0; i < rs.ResultCount; i++ {
-					groupByValue, _ := rs.GroupByValue.Get(i)
-					pkValue, _ := rs.IDs.GetAsInt64(i)
+			for i := 0; i < common.DefaultNq; i++ {
+				for j := 0; j < resGroupBy[i].ResultCount; j++ {
+					groupByValue, _ := resGroupBy[i].GroupByValue.Get(j)
+					pkValue, _ := resGroupBy[i].IDs.GetAsInt64(j)
 					var expr string
 					if groupByField == "varchar" {
 						expr = fmt.Sprintf("%s == '%v' ", groupByField, groupByValue)
 					} else {
 						expr = fmt.Sprintf("%s == %v", groupByField, groupByValue)
 					}
-
 					resFilter, _ := mc.Search(ctx, collName, []string{}, expr, []string{common.DefaultIntFieldName,
-						groupByField}, queryVec, common.DefaultFloatVecFieldName, metricType, 1, sp, client.WithSearchQueryConsistencyLevel(entity.ClStrong))
+						groupByField}, []entity.Vector{queryVec[i]}, common.DefaultFloatVecFieldName, metricType, 1, sp, client.WithSearchQueryConsistencyLevel(entity.ClStrong))
 
 					// search filter with groupByValue is the top1
 					filterTop1Pk, _ := resFilter[0].IDs.GetAsInt64(0)
diff --git a/test/testcases/search_test.go b/test/testcases/search_test.go
index 598953b..df642b9 100644
--- a/test/testcases/search_test.go
+++ b/test/testcases/search_test.go
@@ -9,6 +9,7 @@ import (
 	"testing"
 	"time"
 
+	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
 	"github.com/milvus-io/milvus-sdk-go/v2/client"
@@ -164,7 +165,7 @@ func TestSearchEmptyCollection(t *testing.T) {
 
 		// search vector
 		sp, _ := entity.NewIndexHNSWSearchParam(74)
-		searchRes, _ := mc.Search(
+		searchRes, errSearch := mc.Search(
 			ctx, collName,
 			[]string{common.DefaultPartition},
 			"",
@@ -176,7 +177,8 @@ func TestSearchEmptyCollection(t *testing.T) {
 			common.DefaultTopK,
 			sp,
 		)
-		require.Len(t, searchRes, 0)
+		common.CheckErr(t, errSearch, true)
+		common.CheckSearchResult(t, searchRes, common.DefaultNq, 0)
 	}
 }
 
@@ -212,9 +214,7 @@ func TestSearchEmptyCollection2(t *testing.T) {
 		resSearch, errSearch := mc.Search(ctx, collName, []string{}, "", []string{"*"}, nv.queryVec, nv.fieldName,
 			nv.metricType, common.DefaultTopK, sp)
 		common.CheckErr(t, errSearch, true)
-		for _, res := range resSearch {
-			require.Nil(t, res)
-		}
+		common.CheckSearchResult(t, resSearch, common.DefaultNq, 0)
 	}
 }
 
@@ -274,8 +274,6 @@ func TestSearchEmptyPartitions(t *testing.T) {
 		nq0IDs := searchResult[0].IDs.(*entity.ColumnInt64).Data()
 		nq1IDs := searchResult[1].IDs.(*entity.ColumnInt64).Data()
 		common.CheckSearchResult(t, searchResult, 2, common.DefaultTopK)
-		log.Println(nq0IDs)
-		log.Println(nq1IDs)
 		require.Contains(t, nq0IDs, vecColumnDefault.IdsColumn.(*entity.ColumnInt64).Data()[0])
 		require.Contains(t, nq1IDs, vecColumnPartition.IdsColumn.(*entity.ColumnInt64).Data()[0])
 	}
@@ -1190,7 +1188,10 @@ func TestSearchArrayFieldExpr(t *testing.T) {
 			sp,
 		)
 		common.CheckErr(t, errSearchEmpty, true)
-		require.Empty(t, searchRes)
+		require.Len(t, searchRes, common.DefaultNq)
+		for _, resultSet := range searchRes {
+			assert.EqualValues(t, 0, resultSet.ResultCount)
+		}
 	}
 }
 
@@ -1593,7 +1594,6 @@ func TestSearchMultiVectors(t *testing.T) {
 }
 
 func TestSearchSparseVector(t *testing.T) {
-	t.Skip("https://github.com/milvus-io/milvus-sdk-go/issues/725")
 	t.Parallel()
 	idxInverted := entity.NewGenericIndex(common.DefaultSparseVecFieldName, "SPARSE_INVERTED_INDEX", map[string]string{"drop_ratio_build": "0.2", "metric_type": "IP"})
 	idxWand := entity.NewGenericIndex(common.DefaultSparseVecFieldName, "SPARSE_WAND", map[string]string{"drop_ratio_build": "0.3", "metric_type": "IP"})
@@ -1623,10 +1623,15 @@ func TestSearchSparseVector(t *testing.T) {
 		resSearch, errSearch := mc.Search(ctx, collName, []string{}, "", []string{"*"}, queryVec, common.DefaultSparseVecFieldName,
 			entity.IP, common.DefaultTopK, sp)
 		common.CheckErr(t, errSearch, true)
-		common.CheckSearchResult(t, resSearch, common.DefaultNq, common.DefaultTopK)
+		require.Len(t, resSearch, common.DefaultNq)
 		outputFields := []string{common.DefaultIntFieldName, common.DefaultVarcharFieldName, common.DefaultFloatVecFieldName,
 			common.DefaultSparseVecFieldName, common.DefaultDynamicFieldName}
-		common.CheckOutputFields(t, resSearch[0].Fields, outputFields)
+		for _, res := range resSearch {
+			require.LessOrEqual(t, res.ResultCount, common.DefaultTopK)
+			if res.ResultCount == common.DefaultTopK {
+				common.CheckOutputFields(t, resSearch[0].Fields, outputFields)
+			}
+		}
 	}
 }
 
@@ -1681,7 +1686,6 @@ func TestSearchInvalidSparseVector(t *testing.T) {
 	}
 }
 
-// TODO https://github.com/milvus-io/milvus-sdk-go/issues/725
 func TestSearchEmptySparseCollection(t *testing.T) {
 	t.Parallel()
 	idxInverted := entity.NewGenericIndex(common.DefaultSparseVecFieldName, "SPARSE_INVERTED_INDEX", map[string]string{"drop_ratio_build": "0.2", "metric_type": "IP"})
@@ -1710,11 +1714,7 @@ func TestSearchEmptySparseCollection(t *testing.T) {
 		resSearch, errSearch := mc.Search(ctx, collName, []string{}, "", []string{"*"}, queryVec, common.DefaultSparseVecFieldName,
 			entity.IP, common.DefaultTopK, sp)
 		common.CheckErr(t, errSearch, true)
-		require.Empty(t, resSearch)
-		//require.Len(t, resSearch, common.DefaultNq)
-		//for _, res := range resSearch {
-		//	require.Nil(t, res)
-		//}
+		common.CheckSearchResult(t, resSearch, common.DefaultNq, 0)
 	}
 }
 
@@ -1748,12 +1748,16 @@ func TestSearchSparseVectorPagination(t *testing.T) {
 		resSearch, errSearch := mc.Search(ctx, collName, []string{}, "", []string{"*"}, queryVec, common.DefaultSparseVecFieldName,
 			entity.IP, common.DefaultTopK, sp)
 		common.CheckErr(t, errSearch, true)
+		require.Len(t, resSearch, common.DefaultNq)
 
 		pageSearch, errSearch := mc.Search(ctx, collName, []string{}, "", []string{"*"}, queryVec, common.DefaultSparseVecFieldName,
 			entity.IP, 5, sp, client.WithOffset(5))
 		common.CheckErr(t, errSearch, true)
+		require.Len(t, pageSearch, common.DefaultNq)
 		for i := 0; i < len(resSearch); i++ {
-			require.Equal(t, resSearch[i].IDs.(*entity.ColumnInt64).Data()[5:], pageSearch[i].IDs.(*entity.ColumnInt64).Data())
+			if resSearch[i].ResultCount == common.DefaultTopK && pageSearch[i].ResultCount == 5 {
+				require.Equal(t, resSearch[i].IDs.(*entity.ColumnInt64).Data()[5:], pageSearch[i].IDs.(*entity.ColumnInt64).Data())
+			}
 		}
 	}
 }