Repository: milvus-io/milvus-sdk-go
Pull request #147 (2021-07-16 09:39:49)

Add CalcDistance api support
Add CalcDistance API
Add Data function for scalar columns

Solves: #121 

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/147#" 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/147#" title="Author self-approved">congqixia</a>*, *<a href="https://github.com/milvus-io/milvus-sdk-go/pull/147#issuecomment-881317394" 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/CHANGELOG.md b/CHANGELOG.md
index 16d8a85..03e0c3b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,15 @@
 
 Please mark all changes in CHANGELOG.md and mark related issue(s) from Github
 
+# v2.0.0-alpha.2(WIP)
+
+## Bug
+
+-  \#144 Fix some break test and example
+
+## Feature
+-  \#121 Add CalcDistance API support
+
 # v2.0.0-alpha.1
 
 ## Bug
diff --git a/client/client.go b/client/client.go
index fe9a911..a67e16f 100644
--- a/client/client.go
+++ b/client/client.go
@@ -83,6 +83,10 @@ type Client interface {
 	// Search search with bool expression
 	Search(ctx context.Context, collName string, partitions []string,
 		expr string, outputFields []string, vectors []entity.Vector, vectorField string, metricType entity.MetricType, topK int, sp entity.SearchParam) ([]SearchResult, error)
+
+	// CalcDistanceWithIDs calculate the distance between vectors specified by ids provided
+	CalcDistanceWithIDs(ctx context.Context, collName string, partitions []string, fieldName string,
+		metricType entity.MetricType, idsl, idsr entity.Column) (entity.Column, error)
 }
 
 // SearchResult search result
diff --git a/client/client_grpc_dm.go b/client/client_grpc_dm.go
index 61e38d9..70cb82f 100644
--- a/client/client_grpc_dm.go
+++ b/client/client_grpc_dm.go
@@ -22,6 +22,7 @@ import (
 	"github.com/golang/protobuf/proto"
 	"github.com/milvus-io/milvus-sdk-go/v2/entity"
 	"github.com/milvus-io/milvus-sdk-go/v2/internal/proto/common"
+	"github.com/milvus-io/milvus-sdk-go/v2/internal/proto/schema"
 	"github.com/milvus-io/milvus-sdk-go/v2/internal/proto/server"
 )
 
@@ -235,6 +236,109 @@ func (c *grpcClient) GetQuerySegmentInfo(ctx context.Context, collName string) (
 	return segments, nil
 }
 
+// CalcDistanceWithIDs calculate distance between vectors
+func (c *grpcClient) CalcDistanceWithIDs(ctx context.Context, collName string, partitions []string, fieldName string,
+	metricType entity.MetricType, idsl, idsr entity.Column) (entity.Column, error) {
+	if c.service == nil {
+		return nil, ErrClientNotReady
+	}
+
+	if idsl == nil || idsr == nil {
+		return nil, errors.New("ids cannot be nil")
+	}
+
+	if idsl.Len() != idsl.Len() {
+		return nil, errors.New("ids length not match")
+	}
+	if idsl.Type() != idsl.Type() {
+		return nil, errors.New("ids type not match")
+	}
+
+	// describe collection to check id provided is primary key
+	coll, err := c.DescribeCollection(ctx, collName)
+	if err != nil {
+		return nil, err
+	}
+
+	if !(isCollectionPrimaryKey(coll, idsl) && isCollectionPrimaryKey(coll, idsr)) {
+		return nil, errors.New("column(s) passed is no the primary key of the collection")
+	}
+
+	colToIDs := func(col entity.Column) *server.VectorIDs {
+		switch col.Type() {
+		case entity.FieldTypeInt64:
+			int64Column, ok := col.(*entity.ColumnInt64)
+			if !ok {
+				return nil // server shall report error
+			}
+			return &server.VectorIDs{
+				CollectionName: collName,
+				PartitionNames: partitions,
+				FieldName:      fieldName,
+				IdArray: &schema.IDs{
+					IdField: &schema.IDs_IntId{
+						IntId: &schema.LongArray{
+							Data: int64Column.Data(),
+						},
+					},
+				},
+			}
+		case entity.FieldTypeString:
+			//NOT supported yet
+			stringColumn, ok := col.(*entity.ColumnString)
+			if !ok {
+				return nil // server shall report error
+			}
+			return &server.VectorIDs{
+				CollectionName: collName,
+				PartitionNames: partitions,
+				FieldName:      fieldName,
+				IdArray: &schema.IDs{
+					IdField: &schema.IDs_StrId{
+						StrId: &schema.StringArray{
+							Data: stringColumn.Data(),
+						},
+					},
+				},
+			}
+
+		}
+		return nil
+	}
+
+	req := &server.CalcDistanceRequest{
+		OpLeft: &server.VectorsArray{
+			Array: &server.VectorsArray_IdArray{
+				IdArray: colToIDs(idsl),
+			},
+		},
+		OpRight: &server.VectorsArray{
+			Array: &server.VectorsArray_IdArray{
+				IdArray: colToIDs(idsr),
+			},
+		},
+		Params: entity.MapKvPairs(map[string]string{
+			"metric": string(metricType),
+		}),
+	}
+	resp, err := c.service.CalcDistance(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	if err := handleRespStatus(resp.GetStatus()); err != nil {
+		return nil, err
+	}
+
+	if fd := resp.GetFloatDist(); fd != nil {
+		return entity.NewColumnFloat("distance", fd.GetData()), nil
+	}
+	if id := resp.GetIntDist(); id != nil {
+		return entity.NewColumnInt32("distance", id.GetData()), nil
+	}
+
+	return nil, errors.New("distance field not supported")
+}
+
 func vector2PlaceholderGroupBytes(vectors []entity.Vector) []byte {
 	phg := &server.PlaceholderGroup{
 		Placeholders: []*server.PlaceholderValue{
@@ -257,3 +361,20 @@ func vector2Placeholder(vectors []entity.Vector) *server.PlaceholderValue {
 	}
 	return ph
 }
+
+func isCollectionPrimaryKey(coll *entity.Collection, column entity.Column) bool {
+	if coll == nil || coll.Schema == nil || column == nil {
+		return false
+	}
+
+	// temporary check logic, since only one primary field is supported
+	for _, field := range coll.Schema.Fields {
+		if field.PrimaryKey {
+			if field.Name == column.Name() && field.DataType == column.Type() {
+				return true
+			}
+			return false
+		}
+	}
+	return false
+}
diff --git a/entity/columns_binaryvector.go b/entity/columns_binaryvector.go
index a1cf457..611ccc3 100755
--- a/entity/columns_binaryvector.go
+++ b/entity/columns_binaryvector.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go genrated at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go genrated at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
diff --git a/entity/columns_bool.go b/entity/columns_bool.go
index 2ae8e90..7b9acc5 100755
--- a/entity/columns_bool.go
+++ b/entity/columns_bool.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go generate at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go generate at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -63,6 +63,11 @@ func (c *ColumnBool) ValueByIdx(idx int) (bool, error) {
 	return c.values[idx], nil
 }
 
+// Data returns column data
+func (c *ColumnBool) Data() []bool {
+	return c.values
+}
+
 // NewColumnBool auto generated constructor
 func NewColumnBool(name string, values []bool) *ColumnBool {
 	return &ColumnBool {
diff --git a/entity/columns_double.go b/entity/columns_double.go
index f0f1a87..a38c418 100755
--- a/entity/columns_double.go
+++ b/entity/columns_double.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go generate at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go generate at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -63,6 +63,11 @@ func (c *ColumnDouble) ValueByIdx(idx int) (float64, error) {
 	return c.values[idx], nil
 }
 
+// Data returns column data
+func (c *ColumnDouble) Data() []float64 {
+	return c.values
+}
+
 // NewColumnDouble auto generated constructor
 func NewColumnDouble(name string, values []float64) *ColumnDouble {
 	return &ColumnDouble {
diff --git a/entity/columns_float.go b/entity/columns_float.go
index 00876cf..6302b7e 100755
--- a/entity/columns_float.go
+++ b/entity/columns_float.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go generate at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go generate at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -63,6 +63,11 @@ func (c *ColumnFloat) ValueByIdx(idx int) (float32, error) {
 	return c.values[idx], nil
 }
 
+// Data returns column data
+func (c *ColumnFloat) Data() []float32 {
+	return c.values
+}
+
 // NewColumnFloat auto generated constructor
 func NewColumnFloat(name string, values []float32) *ColumnFloat {
 	return &ColumnFloat {
diff --git a/entity/columns_floatvector.go b/entity/columns_floatvector.go
index c100481..b230c9b 100755
--- a/entity/columns_floatvector.go
+++ b/entity/columns_floatvector.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go genrated at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go genrated at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
diff --git a/entity/columns_int16.go b/entity/columns_int16.go
index 114ce3d..8fe6a32 100755
--- a/entity/columns_int16.go
+++ b/entity/columns_int16.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go generate at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go generate at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -63,6 +63,11 @@ func (c *ColumnInt16) ValueByIdx(idx int) (int16, error) {
 	return c.values[idx], nil
 }
 
+// Data returns column data
+func (c *ColumnInt16) Data() []int16 {
+	return c.values
+}
+
 // NewColumnInt16 auto generated constructor
 func NewColumnInt16(name string, values []int16) *ColumnInt16 {
 	return &ColumnInt16 {
diff --git a/entity/columns_int32.go b/entity/columns_int32.go
index 47b668f..5d891e7 100755
--- a/entity/columns_int32.go
+++ b/entity/columns_int32.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go generate at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go generate at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -63,6 +63,11 @@ func (c *ColumnInt32) ValueByIdx(idx int) (int32, error) {
 	return c.values[idx], nil
 }
 
+// Data returns column data
+func (c *ColumnInt32) Data() []int32 {
+	return c.values
+}
+
 // NewColumnInt32 auto generated constructor
 func NewColumnInt32(name string, values []int32) *ColumnInt32 {
 	return &ColumnInt32 {
diff --git a/entity/columns_int64.go b/entity/columns_int64.go
index 8bd0726..c8ee56f 100755
--- a/entity/columns_int64.go
+++ b/entity/columns_int64.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go generate at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go generate at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -63,6 +63,11 @@ func (c *ColumnInt64) ValueByIdx(idx int) (int64, error) {
 	return c.values[idx], nil
 }
 
+// Data returns column data
+func (c *ColumnInt64) Data() []int64 {
+	return c.values
+}
+
 // NewColumnInt64 auto generated constructor
 func NewColumnInt64(name string, values []int64) *ColumnInt64 {
 	return &ColumnInt64 {
diff --git a/entity/columns_int8.go b/entity/columns_int8.go
index ac4ccb5..2c7eae0 100755
--- a/entity/columns_int8.go
+++ b/entity/columns_int8.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go generate at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go generate at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -63,6 +63,11 @@ func (c *ColumnInt8) ValueByIdx(idx int) (int8, error) {
 	return c.values[idx], nil
 }
 
+// Data returns column data
+func (c *ColumnInt8) Data() []int8 {
+	return c.values
+}
+
 // NewColumnInt8 auto generated constructor
 func NewColumnInt8(name string, values []int8) *ColumnInt8 {
 	return &ColumnInt8 {
diff --git a/entity/columns_string.go b/entity/columns_string.go
index 2fc3791..83fec82 100755
--- a/entity/columns_string.go
+++ b/entity/columns_string.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go generate at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go generate at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -63,6 +63,11 @@ func (c *ColumnString) ValueByIdx(idx int) (string, error) {
 	return c.values[idx], nil
 }
 
+// Data returns column data
+func (c *ColumnString) Data() []string {
+	return c.values
+}
+
 // NewColumnString auto generated constructor
 func NewColumnString(name string, values []string) *ColumnString {
 	return &ColumnString {
diff --git a/entity/gen/gen.go b/entity/gen/gen.go
index 0f568ad..76f4fc7 100644
--- a/entity/gen/gen.go
+++ b/entity/gen/gen.go
@@ -21,7 +21,7 @@ import (
 	"text/template"
 	"time"
 
-	"github.com/milvus-io/milvus-sdk-go/entity"
+	"github.com/milvus-io/milvus-sdk-go/v2/entity"
 )
 
 var scalarColumnTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT
@@ -89,6 +89,11 @@ func (c *Column{{.TypeName}}) ValueByIdx(idx int) ({{.TypeDef}}, error) {
 	return c.values[idx], nil
 }
 
+// Data returns column data
+func (c *Column{{.TypeName}}) Data() []{{.TypeDef}} {
+	return c.values
+}
+
 // NewColumn{{.TypeName}} auto generated constructor
 func NewColumn{{.TypeName}}(name string, values []{{.TypeDef}}) *Column{{.TypeName}} {
 	return &Column{{.TypeName}} {
@@ -212,6 +217,7 @@ func TestColumn{{.TypeName}}(t *testing.T) {
 		assert.Equal(t, columnName, column.Name())
 		assert.Equal(t, FieldType{{.TypeName}}, column.Type())
 		assert.Equal(t, columnLen, column.Len())
+		assert.EqualValues(t, v, column.Data())
 	})
 
 	t.Run("test column field data", func(t *testing.T) {
diff --git a/entity/genidx/genidx.go b/entity/genidx/genidx.go
index 125120f..3fd05ef 100644
--- a/entity/genidx/genidx.go
+++ b/entity/genidx/genidx.go
@@ -21,7 +21,7 @@ import (
 	"text/template"
 	"time"
 
-	"github.com/milvus-io/milvus-sdk-go/entity"
+	"github.com/milvus-io/milvus-sdk-go/v2/entity"
 )
 
 var indexTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT


Test patch:
diff --git a/client/client_grpc_dd_test.go b/client/client_grpc_dd_test.go
index 03deea8..eb8b15c 100644
--- a/client/client_grpc_dd_test.go
+++ b/client/client_grpc_dd_test.go
@@ -34,6 +34,7 @@ func TestGrpcClientNil(t *testing.T) {
 		mt := m.Type                                   // type of function
 		if m.Name == "Close" || m.Name == "Connect" || // skip connect & close
 			m.Name == "Search" || // type alias MetricType treated as string
+			m.Name == "CalcDistanceWithIDs" ||
 			m.Name == "Insert" { // complex methods with ...
 			continue
 		}
diff --git a/client/client_grpc_dm_test.go b/client/client_grpc_dm_test.go
index 897be11..7b95986 100644
--- a/client/client_grpc_dm_test.go
+++ b/client/client_grpc_dm_test.go
@@ -137,6 +137,197 @@ func TestGrpcSearch(t *testing.T) {
 	assert.NotNil(t, results)
 }
 
+func TestGrpcCalcDistanceWithIDs(t *testing.T) {
+	ctx := context.Background()
+	t.Run("bad client calls CalcDistance", func(t *testing.T) {
+		c := &grpcClient{}
+		r, err := c.CalcDistanceWithIDs(ctx, testCollectionName, []string{}, "vector", entity.L2, nil, nil)
+		assert.Nil(t, r)
+		assert.NotNil(t, err)
+		assert.EqualValues(t, ErrClientNotReady, err)
+	})
+
+	c := testClient(ctx, t)
+	mock.setInjection(mDescribeCollection, func(_ context.Context, raw proto.Message) (proto.Message, error) {
+		req, ok := raw.(*server.DescribeCollectionRequest)
+		resp := &server.DescribeCollectionResponse{}
+		if !ok {
+			s, err := badRequestStatus()
+			resp.Status = s
+			return resp, err
+		}
+		assert.Equal(t, testCollectionName, req.GetCollectionName())
+
+		sch := defaultSchema()
+		resp.Schema = sch.ProtoMessage()
+
+		s, err := successStatus()
+		resp.Status = s
+		return resp, err
+	})
+	t.Run("call with ctx done", func(t *testing.T) {
+		ctxDone, cancel := context.WithCancel(context.Background())
+		cancel()
+
+		r, err := c.CalcDistanceWithIDs(ctxDone, testCollectionName, []string{}, "vector", entity.L2,
+			entity.NewColumnInt64("int64", []int64{1}), entity.NewColumnInt64("int64", []int64{1}))
+		assert.Nil(t, r)
+		assert.NotNil(t, err)
+	})
+
+	t.Run("invalid ids call", func(t *testing.T) {
+		r, err := c.CalcDistanceWithIDs(ctx, testCollectionName, []string{}, "vector", entity.L2,
+			nil, nil)
+		assert.Nil(t, r)
+		assert.NotNil(t, err)
+
+		r, err = c.CalcDistanceWithIDs(ctx, testCollectionName, []string{}, "vector", entity.L2,
+			entity.NewColumnInt64("non-exists", []int64{1}), entity.NewColumnInt64("non-exists", []int64{1}))
+		assert.Nil(t, r)
+		assert.NotNil(t, err)
+
+		r, err = c.CalcDistanceWithIDs(ctx, testCollectionName, []string{}, "vector", entity.L2,
+			entity.NewColumnInt64("non-exists", []int64{1}), entity.NewColumnInt64("non-exists", []int64{1}))
+	})
+
+	t.Run("valid calls", func(t *testing.T) {
+		mock.setInjection(mCalcDistanceIDs, func(_ context.Context, raw proto.Message) (proto.Message, error) {
+			req, ok := raw.(*server.CalcDistanceRequest)
+			resp := &server.CalcDistanceResults{}
+			if !ok {
+				s, err := badRequestStatus()
+				resp.Status = s
+				return resp, err
+			}
+			idsLeft := req.GetOpLeft().GetIdArray()
+			idsRight := req.GetOpRight().GetIdArray()
+			assert.NotNil(t, idsLeft)
+			assert.NotNil(t, idsRight)
+
+			assert.Equal(t, testCollectionName, idsLeft.CollectionName)
+			assert.Equal(t, testCollectionName, idsRight.CollectionName)
+
+			// only int ids supported for now TODO update string test cases
+			assert.NotNil(t, idsLeft.IdArray.GetIntId())
+			assert.NotNil(t, idsRight.IdArray.GetIntId())
+
+			// this injection returns float distance
+			dl := len(idsLeft.IdArray.GetIntId().Data)
+
+			resp.Array = &server.CalcDistanceResults_FloatDist{
+				FloatDist: &schema.FloatArray{
+					Data: make([]float32, dl),
+				},
+			}
+
+			s, err := successStatus()
+			resp.Status = s
+			return resp, err
+		})
+		r, err := c.CalcDistanceWithIDs(ctx, testCollectionName, []string{}, "vector", entity.L2,
+			entity.NewColumnInt64("int64", []int64{1}), entity.NewColumnInt64("int64", []int64{1}))
+		assert.Nil(t, err)
+		assert.NotNil(t, r)
+
+		// test IntDistance,
+		mock.setInjection(mCalcDistanceIDs, func(_ context.Context, raw proto.Message) (proto.Message, error) {
+			req, ok := raw.(*server.CalcDistanceRequest)
+			resp := &server.CalcDistanceResults{}
+			if !ok {
+				s, err := badRequestStatus()
+				resp.Status = s
+				return resp, err
+			}
+			idsLeft := req.GetOpLeft().GetIdArray()
+			idsRight := req.GetOpRight().GetIdArray()
+			assert.NotNil(t, idsLeft)
+			assert.NotNil(t, idsRight)
+
+			assert.Equal(t, testCollectionName, idsLeft.CollectionName)
+			assert.Equal(t, testCollectionName, idsRight.CollectionName)
+
+			// only int ids supported for now TODO update string test cases
+			assert.NotNil(t, idsLeft.IdArray.GetIntId())
+			assert.NotNil(t, idsRight.IdArray.GetIntId())
+
+			// this injection returns float distance
+			dl := len(idsLeft.IdArray.GetIntId().Data)
+
+			resp.Array = &server.CalcDistanceResults_IntDist{
+				IntDist: &schema.IntArray{
+					Data: make([]int32, dl),
+				},
+			}
+
+			s, err := successStatus()
+			resp.Status = s
+			return resp, err
+		})
+		r, err = c.CalcDistanceWithIDs(ctx, testCollectionName, []string{}, "vector", entity.HAMMING,
+			entity.NewColumnInt64("int64", []int64{1}), entity.NewColumnInt64("int64", []int64{1}))
+		assert.Nil(t, err)
+		assert.NotNil(t, r)
+
+		// test str id
+		mock.setInjection(mDescribeCollection, func(_ context.Context, raw proto.Message) (proto.Message, error) {
+			req, ok := raw.(*server.DescribeCollectionRequest)
+			resp := &server.DescribeCollectionResponse{}
+			if !ok {
+				s, err := badRequestStatus()
+				resp.Status = s
+				return resp, err
+			}
+			assert.Equal(t, testCollectionName, req.GetCollectionName())
+
+			sch := defaultSchema()
+			sch.Fields[0].DataType = entity.FieldTypeString
+			sch.Fields[0].Name = "str"
+			resp.Schema = sch.ProtoMessage()
+
+			s, err := successStatus()
+			resp.Status = s
+			return resp, err
+		})
+		mock.setInjection(mCalcDistanceIDs, func(_ context.Context, raw proto.Message) (proto.Message, error) {
+			req, ok := raw.(*server.CalcDistanceRequest)
+			resp := &server.CalcDistanceResults{}
+			if !ok {
+				s, err := badRequestStatus()
+				resp.Status = s
+				return resp, err
+			}
+			idsLeft := req.GetOpLeft().GetIdArray()
+			idsRight := req.GetOpRight().GetIdArray()
+			assert.NotNil(t, idsLeft)
+			assert.NotNil(t, idsRight)
+
+			assert.Equal(t, testCollectionName, idsLeft.CollectionName)
+			assert.Equal(t, testCollectionName, idsRight.CollectionName)
+
+			// only int ids supported for now TODO update string test cases
+			assert.NotNil(t, idsLeft.IdArray.GetStrId())
+			assert.NotNil(t, idsRight.IdArray.GetStrId())
+
+			// this injection returns float distance
+			dl := len(idsLeft.IdArray.GetStrId().Data)
+
+			resp.Array = &server.CalcDistanceResults_FloatDist{
+				FloatDist: &schema.FloatArray{
+					Data: make([]float32, dl),
+				},
+			}
+
+			s, err := successStatus()
+			resp.Status = s
+			return resp, err
+		})
+		r, err = c.CalcDistanceWithIDs(ctx, testCollectionName, []string{}, "vector", entity.L2,
+			entity.NewColumnString("str", []string{"1"}), entity.NewColumnString("str", []string{"1"}))
+		assert.Nil(t, err)
+		assert.NotNil(t, r)
+	})
+}
+
 func generateFloatVector(num, dim int) [][]float32 {
 	r := make([][]float32, 0, num)
 	for i := 0; i < num; i++ {
diff --git a/client/client_mock_test.go b/client/client_mock_test.go
index fc50737..45123a1 100644
--- a/client/client_mock_test.go
+++ b/client/client_mock_test.go
@@ -123,9 +123,10 @@ const (
 	mGetIndexState         serviceMethod = 23
 	mGetIndexBuildProgress serviceMethod = 24
 
-	mInsert serviceMethod = 30
-	mFlush  serviceMethod = 31
-	mSearch serviceMethod = 32
+	mInsert          serviceMethod = 30
+	mFlush           serviceMethod = 31
+	mSearch          serviceMethod = 32
+	mCalcDistanceIDs serviceMethod = 33
 
 	mGetPersistentSegmentInfo serviceMethod = 98
 	mGetQuerySegmentInfo      serviceMethod = 99
@@ -425,7 +426,12 @@ func (m *mockServer) RegisterLink(_ context.Context, _ *server.RegisterLinkReque
 	panic("not implemented") // TODO: Implement
 }
 
-func (m *mockServer) CalcDistance(_ context.Context, req *server.CalcDistanceRequest) (*server.CalcDistanceResults, error) {
+func (m *mockServer) CalcDistance(ctx context.Context, req *server.CalcDistanceRequest) (*server.CalcDistanceResults, error) {
+	f := m.getInjection(mCalcDistanceIDs)
+	if f != nil {
+		r, err := f(ctx, req)
+		return r.(*server.CalcDistanceResults), err
+	}
 	resp := &server.CalcDistanceResults{}
 	s, err := successStatus()
 	resp.Status = s
diff --git a/entity/columns_binaryvector_test.go b/entity/columns_binaryvector_test.go
index dba0278..028eca3 100755
--- a/entity/columns_binaryvector_test.go
+++ b/entity/columns_binaryvector_test.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go genrated at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go genrated at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
diff --git a/entity/columns_bool_test.go b/entity/columns_bool_test.go
index baef879..7304c50 100755
--- a/entity/columns_bool_test.go
+++ b/entity/columns_bool_test.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go genrated at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go genrated at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -35,6 +35,7 @@ func TestColumnBool(t *testing.T) {
 		assert.Equal(t, columnName, column.Name())
 		assert.Equal(t, FieldTypeBool, column.Type())
 		assert.Equal(t, columnLen, column.Len())
+		assert.EqualValues(t, v, column.Data())
 	})
 
 	t.Run("test column field data", func(t *testing.T) {
diff --git a/entity/columns_double_test.go b/entity/columns_double_test.go
index adae127..03049de 100755
--- a/entity/columns_double_test.go
+++ b/entity/columns_double_test.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go genrated at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go genrated at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -35,6 +35,7 @@ func TestColumnDouble(t *testing.T) {
 		assert.Equal(t, columnName, column.Name())
 		assert.Equal(t, FieldTypeDouble, column.Type())
 		assert.Equal(t, columnLen, column.Len())
+		assert.EqualValues(t, v, column.Data())
 	})
 
 	t.Run("test column field data", func(t *testing.T) {
diff --git a/entity/columns_float_test.go b/entity/columns_float_test.go
index 5630cba..c076bc4 100755
--- a/entity/columns_float_test.go
+++ b/entity/columns_float_test.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go genrated at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go genrated at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -35,6 +35,7 @@ func TestColumnFloat(t *testing.T) {
 		assert.Equal(t, columnName, column.Name())
 		assert.Equal(t, FieldTypeFloat, column.Type())
 		assert.Equal(t, columnLen, column.Len())
+		assert.EqualValues(t, v, column.Data())
 	})
 
 	t.Run("test column field data", func(t *testing.T) {
diff --git a/entity/columns_floatvector_test.go b/entity/columns_floatvector_test.go
index a2d7a4a..e522189 100755
--- a/entity/columns_floatvector_test.go
+++ b/entity/columns_floatvector_test.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go genrated at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go genrated at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
diff --git a/entity/columns_int16_test.go b/entity/columns_int16_test.go
index ec53cbe..d298180 100755
--- a/entity/columns_int16_test.go
+++ b/entity/columns_int16_test.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go genrated at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go genrated at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -35,6 +35,7 @@ func TestColumnInt16(t *testing.T) {
 		assert.Equal(t, columnName, column.Name())
 		assert.Equal(t, FieldTypeInt16, column.Type())
 		assert.Equal(t, columnLen, column.Len())
+		assert.EqualValues(t, v, column.Data())
 	})
 
 	t.Run("test column field data", func(t *testing.T) {
diff --git a/entity/columns_int32_test.go b/entity/columns_int32_test.go
index 2a8612c..2361899 100755
--- a/entity/columns_int32_test.go
+++ b/entity/columns_int32_test.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go genrated at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go genrated at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -35,6 +35,7 @@ func TestColumnInt32(t *testing.T) {
 		assert.Equal(t, columnName, column.Name())
 		assert.Equal(t, FieldTypeInt32, column.Type())
 		assert.Equal(t, columnLen, column.Len())
+		assert.EqualValues(t, v, column.Data())
 	})
 
 	t.Run("test column field data", func(t *testing.T) {
diff --git a/entity/columns_int64_test.go b/entity/columns_int64_test.go
index efb8f7d..87b49c5 100755
--- a/entity/columns_int64_test.go
+++ b/entity/columns_int64_test.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go genrated at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go genrated at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -35,6 +35,7 @@ func TestColumnInt64(t *testing.T) {
 		assert.Equal(t, columnName, column.Name())
 		assert.Equal(t, FieldTypeInt64, column.Type())
 		assert.Equal(t, columnLen, column.Len())
+		assert.EqualValues(t, v, column.Data())
 	})
 
 	t.Run("test column field data", func(t *testing.T) {
diff --git a/entity/columns_int8_test.go b/entity/columns_int8_test.go
index 2d62655..dacea86 100755
--- a/entity/columns_int8_test.go
+++ b/entity/columns_int8_test.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go genrated at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go genrated at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -35,6 +35,7 @@ func TestColumnInt8(t *testing.T) {
 		assert.Equal(t, columnName, column.Name())
 		assert.Equal(t, FieldTypeInt8, column.Type())
 		assert.Equal(t, columnLen, column.Len())
+		assert.EqualValues(t, v, column.Data())
 	})
 
 	t.Run("test column field data", func(t *testing.T) {
diff --git a/entity/columns_string_test.go b/entity/columns_string_test.go
index 60f2bdc..2e0d470 100755
--- a/entity/columns_string_test.go
+++ b/entity/columns_string_test.go
@@ -1,5 +1,5 @@
 // Code generated by go generate; DO NOT EDIT
-// This file is generated by go genrated at 2021-07-15 15:55:19.25274366 +0800 CST m=+0.002663389
+// This file is generated by go genrated at 2021-07-16 12:46:23.174531837 +0800 CST m=+0.002896366
 
 //Package entity defines entities used in sdk
 package entity 
@@ -35,6 +35,7 @@ func TestColumnString(t *testing.T) {
 		assert.Equal(t, columnName, column.Name())
 		assert.Equal(t, FieldTypeString, column.Type())
 		assert.Equal(t, columnLen, column.Len())
+		assert.EqualValues(t, v, column.Data())
 	})
 
 	t.Run("test column field data", func(t *testing.T) {