github.com/milvus-io/milvus-sdk-go/v2@v2.4.1/client/data.go (about)

     1  // Copyright (C) 2019-2021 Zilliz. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
     4  // with the License. You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software distributed under the License
     9  // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
    10  // or implied. See the License for the specific language governing permissions and limitations under the License.
    11  
    12  package client
    13  
    14  import (
    15  	"context"
    16  	"encoding/json"
    17  	"fmt"
    18  	"log"
    19  	"strconv"
    20  	"strings"
    21  
    22  	"github.com/cockroachdb/errors"
    23  
    24  	"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
    25  	"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
    26  	"github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
    27  	"github.com/milvus-io/milvus-sdk-go/v2/entity"
    28  	"github.com/milvus-io/milvus-sdk-go/v2/merr"
    29  )
    30  
    31  const (
    32  	offsetKey        = `offset`
    33  	limitKey         = `limit`
    34  	ignoreGrowingKey = `ignore_growing`
    35  	forTuningKey     = `for_tuning`
    36  	groupByKey       = `group_by_field`
    37  	iteratorKey      = `iterator`
    38  	reduceForBestKey = `reduce_stop_for_best`
    39  )
    40  
    41  func (c *GrpcClient) HybridSearch(ctx context.Context, collName string, partitions []string, limit int, outputFields []string, reranker Reranker, subRequests []*ANNSearchRequest, opts ...SearchQueryOptionFunc) ([]SearchResult, error) {
    42  	if c.Service == nil {
    43  		return nil, ErrClientNotReady
    44  	}
    45  
    46  	var schema *entity.Schema
    47  	collInfo, ok := MetaCache.getCollectionInfo(collName)
    48  	if !ok {
    49  		coll, err := c.DescribeCollection(ctx, collName)
    50  		if err != nil {
    51  			return nil, err
    52  		}
    53  		schema = coll.Schema
    54  		collInfo, _ = MetaCache.getCollectionInfo(collName)
    55  	} else {
    56  		schema = collInfo.Schema
    57  	}
    58  
    59  	sReqs := make([]*milvuspb.SearchRequest, 0, len(subRequests))
    60  	nq := 0
    61  	for _, subRequest := range subRequests {
    62  		r, err := subRequest.getMilvusSearchRequest(collInfo, opts...)
    63  		if err != nil {
    64  			return nil, err
    65  		}
    66  		r.CollectionName = collName
    67  		r.PartitionNames = partitions
    68  		r.OutputFields = outputFields
    69  		nq = len(subRequest.vectors)
    70  		sReqs = append(sReqs, r)
    71  	}
    72  
    73  	opt := &SearchQueryOption{}
    74  	for _, o := range opts {
    75  		o(opt)
    76  	}
    77  	params := reranker.GetParams()
    78  	params = append(params, &commonpb.KeyValuePair{Key: limitKey, Value: strconv.FormatInt(int64(limit), 10)})
    79  	params = append(params, &commonpb.KeyValuePair{Key: offsetKey, Value: strconv.FormatInt(int64(opt.Offset), 10)})
    80  
    81  	req := &milvuspb.HybridSearchRequest{
    82  		CollectionName:   collName,
    83  		PartitionNames:   partitions,
    84  		Requests:         sReqs,
    85  		OutputFields:     outputFields,
    86  		ConsistencyLevel: commonpb.ConsistencyLevel(collInfo.ConsistencyLevel),
    87  		RankParams:       params,
    88  	}
    89  
    90  	result, err := c.Service.HybridSearch(ctx, req)
    91  
    92  	err = merr.CheckRPCCall(result, err)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	return c.handleSearchResult(schema, outputFields, nq, result)
    98  }
    99  
   100  // Search with bool expression
   101  func (c *GrpcClient) Search(ctx context.Context, collName string, partitions []string,
   102  	expr string, outputFields []string, vectors []entity.Vector, vectorField string, metricType entity.MetricType, topK int, sp entity.SearchParam, opts ...SearchQueryOptionFunc) ([]SearchResult, error) {
   103  	if c.Service == nil {
   104  		return []SearchResult{}, ErrClientNotReady
   105  	}
   106  	var schema *entity.Schema
   107  	collInfo, ok := MetaCache.getCollectionInfo(collName)
   108  	if !ok {
   109  		coll, err := c.DescribeCollection(ctx, collName)
   110  		if err != nil {
   111  			return nil, err
   112  		}
   113  		schema = coll.Schema
   114  	} else {
   115  		schema = collInfo.Schema
   116  	}
   117  
   118  	option, err := makeSearchQueryOption(collName, opts...)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  	// 2. Request milvus Service
   123  	req, err := prepareSearchRequest(collName, partitions, expr, outputFields, vectors, vectorField, metricType, topK, sp, option)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	resp, err := c.Service.Search(ctx, req)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	if err := handleRespStatus(resp.GetStatus()); err != nil {
   133  		return nil, err
   134  	}
   135  	// 3. parse result into result
   136  	return c.handleSearchResult(schema, outputFields, len(vectors), resp)
   137  }
   138  
   139  func (c *GrpcClient) handleSearchResult(schema *entity.Schema, outputFields []string, nq int, resp *milvuspb.SearchResults) ([]SearchResult, error) {
   140  	sr := make([]SearchResult, 0, nq)
   141  	// parse result into result
   142  	results := resp.GetResults()
   143  	offset := 0
   144  	fieldDataList := results.GetFieldsData()
   145  	gb := results.GetGroupByFieldValue()
   146  
   147  	for i := 0; i < int(results.GetNumQueries()); i++ {
   148  		rc := int(results.GetTopks()[i]) // result entry count for current query
   149  		entry := SearchResult{
   150  			ResultCount: rc,
   151  			Scores:      results.GetScores()[offset : offset+rc],
   152  		}
   153  
   154  		entry.IDs, entry.Err = entity.IDColumns(schema, results.GetIds(), offset, offset+rc)
   155  		if entry.Err != nil {
   156  			continue
   157  		}
   158  		// parse group-by values
   159  		if gb != nil {
   160  			entry.GroupByValue, entry.Err = entity.FieldDataColumn(gb, offset, offset+rc)
   161  			if entry.Err != nil {
   162  				offset += rc
   163  				continue
   164  			}
   165  		}
   166  		entry.Fields, entry.Err = c.parseSearchResult(schema, outputFields, fieldDataList, i, offset, offset+rc)
   167  		sr = append(sr, entry)
   168  
   169  		offset += rc
   170  	}
   171  	return sr, nil
   172  }
   173  
   174  func (c *GrpcClient) parseSearchResult(sch *entity.Schema, outputFields []string, fieldDataList []*schemapb.FieldData, _, from, to int) ([]entity.Column, error) {
   175  	var wildcard bool
   176  	outputFields, wildcard = expandWildcard(sch, outputFields)
   177  	// duplicated name will have only one column now
   178  	outputSet := make(map[string]struct{})
   179  	for _, output := range outputFields {
   180  		outputSet[output] = struct{}{}
   181  	}
   182  	// fields := make(map[string]*schemapb.FieldData)
   183  	columns := make([]entity.Column, 0, len(outputFields))
   184  	var dynamicColumn *entity.ColumnJSONBytes
   185  	for _, fieldData := range fieldDataList {
   186  		column, err := entity.FieldDataColumn(fieldData, from, to)
   187  		if err != nil {
   188  			return nil, err
   189  		}
   190  		if fieldData.GetIsDynamic() {
   191  			var ok bool
   192  			dynamicColumn, ok = column.(*entity.ColumnJSONBytes)
   193  			if !ok {
   194  				return nil, errors.New("dynamic field not json")
   195  			}
   196  
   197  			// return json column only explicitly specified in output fields and not in wildcard mode
   198  			if _, ok := outputSet[fieldData.GetFieldName()]; !ok && !wildcard {
   199  				continue
   200  			}
   201  		}
   202  
   203  		// remove processed field
   204  		delete(outputSet, fieldData.GetFieldName())
   205  
   206  		columns = append(columns, column)
   207  	}
   208  
   209  	if len(outputSet) > 0 && dynamicColumn == nil {
   210  		var extraFields []string
   211  		for output := range outputSet {
   212  			extraFields = append(extraFields, output)
   213  		}
   214  		return nil, errors.Newf("extra output fields %v found and result does not dynamic field", extraFields)
   215  	}
   216  	// add dynamic column for extra fields
   217  	for outputField := range outputSet {
   218  		column := entity.NewColumnDynamic(dynamicColumn, outputField)
   219  		columns = append(columns, column)
   220  	}
   221  
   222  	return columns, nil
   223  }
   224  
   225  func expandWildcard(schema *entity.Schema, outputFields []string) ([]string, bool) {
   226  	wildcard := false
   227  	for _, outputField := range outputFields {
   228  		if outputField == "*" {
   229  			wildcard = true
   230  		}
   231  	}
   232  	if !wildcard {
   233  		return outputFields, false
   234  	}
   235  
   236  	set := make(map[string]struct{})
   237  	result := make([]string, 0, len(schema.Fields))
   238  	for _, field := range schema.Fields {
   239  		result = append(result, field.Name)
   240  		set[field.Name] = struct{}{}
   241  	}
   242  
   243  	// add dynamic fields output
   244  	for _, output := range outputFields {
   245  		if output == "*" {
   246  			continue
   247  		}
   248  		_, ok := set[output]
   249  		if !ok {
   250  			result = append(result, output)
   251  		}
   252  	}
   253  	return result, true
   254  }
   255  
   256  func PKs2Expr(backName string, ids entity.Column) string {
   257  	var expr string
   258  	var pkName = ids.Name()
   259  	if ids.Name() == "" {
   260  		pkName = backName
   261  	}
   262  	switch ids.Type() {
   263  	case entity.FieldTypeInt64:
   264  		expr = fmt.Sprintf("%s in %s", pkName, strings.Join(strings.Fields(fmt.Sprint(ids.FieldData().GetScalars().GetLongData().GetData())), ","))
   265  	case entity.FieldTypeVarChar:
   266  		data := ids.FieldData().GetScalars().GetData().(*schemapb.ScalarField_StringData).StringData.GetData()
   267  		for i := range data {
   268  			data[i] = fmt.Sprintf("\"%s\"", data[i])
   269  		}
   270  		expr = fmt.Sprintf("%s in [%s]", pkName, strings.Join(data, ","))
   271  	}
   272  	return expr
   273  }
   274  
   275  // Get grabs the inserted entities using the primary key from the Collection.
   276  func (c *GrpcClient) Get(ctx context.Context, collectionName string, ids entity.Column, opts ...GetOption) (ResultSet, error) {
   277  	if c.Service == nil {
   278  		return nil, ErrClientNotReady
   279  	}
   280  
   281  	o := &getOption{}
   282  	for _, opt := range opts {
   283  		opt(o)
   284  	}
   285  
   286  	if len(o.outputFields) == 0 {
   287  		coll, err := c.DescribeCollection(ctx, collectionName)
   288  		if err != nil {
   289  			return nil, err
   290  		}
   291  		for _, f := range coll.Schema.Fields {
   292  			o.outputFields = append(o.outputFields, f.Name)
   293  		}
   294  	}
   295  
   296  	return c.QueryByPks(ctx, collectionName, o.partitionNames, ids, o.outputFields)
   297  }
   298  
   299  // QueryByPks query record by specified primary key(s)
   300  func (c *GrpcClient) QueryByPks(ctx context.Context, collectionName string, partitionNames []string, ids entity.Column, outputFields []string, opts ...SearchQueryOptionFunc) (ResultSet, error) {
   301  	if c.Service == nil {
   302  		return nil, ErrClientNotReady
   303  	}
   304  	// check primary keys
   305  	if ids.Len() == 0 {
   306  		return nil, errors.New("ids len must not be zero")
   307  	}
   308  	if ids.Type() != entity.FieldTypeInt64 && ids.Type() != entity.FieldTypeVarChar { // string key not supported yet
   309  		return nil, errors.New("only int64 and varchar column can be primary key for now")
   310  	}
   311  
   312  	expr := PKs2Expr("", ids)
   313  
   314  	return c.Query(ctx, collectionName, partitionNames, expr, outputFields, opts...)
   315  }
   316  
   317  // Query performs query by expression.
   318  func (c *GrpcClient) Query(ctx context.Context, collectionName string, partitionNames []string, expr string, outputFields []string, opts ...SearchQueryOptionFunc) (ResultSet, error) {
   319  	if c.Service == nil {
   320  		return nil, ErrClientNotReady
   321  	}
   322  
   323  	var sch *entity.Schema
   324  	collInfo, ok := MetaCache.getCollectionInfo(collectionName)
   325  	if !ok {
   326  		coll, err := c.DescribeCollection(ctx, collectionName)
   327  		if err != nil {
   328  			return nil, err
   329  		}
   330  		sch = coll.Schema
   331  	} else {
   332  		sch = collInfo.Schema
   333  	}
   334  
   335  	option, err := makeSearchQueryOption(collectionName, opts...)
   336  	if err != nil {
   337  		return nil, err
   338  	}
   339  
   340  	req := &milvuspb.QueryRequest{
   341  		DbName:             "", // reserved field
   342  		CollectionName:     collectionName,
   343  		Expr:               expr,
   344  		OutputFields:       outputFields,
   345  		PartitionNames:     partitionNames,
   346  		GuaranteeTimestamp: option.GuaranteeTimestamp,
   347  	}
   348  	if option.Offset > 0 {
   349  		req.QueryParams = append(req.QueryParams, &commonpb.KeyValuePair{Key: offsetKey, Value: strconv.FormatInt(option.Offset, 10)})
   350  	}
   351  	if option.Limit > 0 {
   352  		req.QueryParams = append(req.QueryParams, &commonpb.KeyValuePair{Key: limitKey, Value: strconv.FormatInt(option.Limit, 10)})
   353  	}
   354  	if option.IgnoreGrowing {
   355  		req.QueryParams = append(req.QueryParams, &commonpb.KeyValuePair{Key: ignoreGrowingKey, Value: strconv.FormatBool(option.IgnoreGrowing)})
   356  	}
   357  	if option.isIterator {
   358  		req.QueryParams = append(req.QueryParams, &commonpb.KeyValuePair{Key: iteratorKey, Value: strconv.FormatBool(true)})
   359  	}
   360  	if option.reduceForBest {
   361  		req.QueryParams = append(req.QueryParams, &commonpb.KeyValuePair{Key: reduceForBestKey, Value: strconv.FormatBool(true)})
   362  	}
   363  
   364  	resp, err := c.Service.Query(ctx, req)
   365  	if err != nil {
   366  		return nil, err
   367  	}
   368  	err = handleRespStatus(resp.GetStatus())
   369  	if err != nil {
   370  		return nil, err
   371  	}
   372  
   373  	fieldsData := resp.GetFieldsData()
   374  
   375  	columns, err := c.parseSearchResult(sch, outputFields, fieldsData, 0, 0, -1) //entity.FieldDataColumn(fieldData, 0, -1)
   376  	if err != nil {
   377  		return nil, err
   378  	}
   379  
   380  	return columns, nil
   381  }
   382  
   383  func getPKField(schema *entity.Schema) *entity.Field {
   384  	for _, f := range schema.Fields {
   385  		if f.PrimaryKey {
   386  			return f
   387  		}
   388  	}
   389  	return nil
   390  }
   391  
   392  func getVectorField(schema *entity.Schema) *entity.Field {
   393  	for _, f := range schema.Fields {
   394  		if f.DataType == entity.FieldTypeFloatVector || f.DataType == entity.FieldTypeBinaryVector {
   395  			return f
   396  		}
   397  	}
   398  	return nil
   399  }
   400  
   401  func prepareSearchRequest(collName string, partitions []string,
   402  	expr string, outputFields []string, vectors []entity.Vector, vectorField string,
   403  	metricType entity.MetricType, topK int, sp entity.SearchParam, opt *SearchQueryOption) (*milvuspb.SearchRequest, error) {
   404  	params := sp.Params()
   405  	params[forTuningKey] = opt.ForTuning
   406  	bs, err := json.Marshal(params)
   407  	if err != nil {
   408  		return nil, err
   409  	}
   410  
   411  	spMap := map[string]string{
   412  		"anns_field":     vectorField,
   413  		"topk":           fmt.Sprintf("%d", topK),
   414  		"params":         string(bs),
   415  		"metric_type":    string(metricType),
   416  		"round_decimal":  "-1",
   417  		ignoreGrowingKey: strconv.FormatBool(opt.IgnoreGrowing),
   418  		offsetKey:        fmt.Sprintf("%d", opt.Offset),
   419  		groupByKey:       opt.GroupByField,
   420  	}
   421  	if opt.GroupByField != "" {
   422  		spMap[groupByKey] = opt.GroupByField
   423  	}
   424  	searchParams := entity.MapKvPairs(spMap)
   425  
   426  	req := &milvuspb.SearchRequest{
   427  		DbName:             "",
   428  		CollectionName:     collName,
   429  		PartitionNames:     partitions,
   430  		Dsl:                expr,
   431  		PlaceholderGroup:   vector2PlaceholderGroupBytes(vectors),
   432  		DslType:            commonpb.DslType_BoolExprV1,
   433  		OutputFields:       outputFields,
   434  		SearchParams:       searchParams,
   435  		GuaranteeTimestamp: opt.GuaranteeTimestamp,
   436  		Nq:                 int64(len(vectors)),
   437  	}
   438  	return req, nil
   439  }
   440  
   441  // GetPersistentSegmentInfo get persistent segment info
   442  func (c *GrpcClient) GetPersistentSegmentInfo(ctx context.Context, collName string) ([]*entity.Segment, error) {
   443  	if c.Service == nil {
   444  		return []*entity.Segment{}, ErrClientNotReady
   445  	}
   446  	req := &milvuspb.GetPersistentSegmentInfoRequest{
   447  		DbName:         "", // reserved
   448  		CollectionName: collName,
   449  	}
   450  	resp, err := c.Service.GetPersistentSegmentInfo(ctx, req)
   451  	if err != nil {
   452  		return []*entity.Segment{}, err
   453  	}
   454  	if err := handleRespStatus(resp.GetStatus()); err != nil {
   455  		return []*entity.Segment{}, err
   456  	}
   457  	segments := make([]*entity.Segment, 0, len(resp.GetInfos()))
   458  	for _, info := range resp.GetInfos() {
   459  		segments = append(segments, &entity.Segment{
   460  			ID:           info.GetSegmentID(),
   461  			CollectionID: info.GetCollectionID(),
   462  			ParititionID: info.GetPartitionID(),
   463  			NumRows:      info.GetNumRows(),
   464  			State:        info.GetState(),
   465  		})
   466  	}
   467  
   468  	return segments, nil
   469  }
   470  
   471  // GetQuerySegmentInfo get query query cluster segment loaded info
   472  func (c *GrpcClient) GetQuerySegmentInfo(ctx context.Context, collName string) ([]*entity.Segment, error) {
   473  	if c.Service == nil {
   474  		return []*entity.Segment{}, ErrClientNotReady
   475  	}
   476  	req := &milvuspb.GetQuerySegmentInfoRequest{
   477  		DbName:         "", // reserved
   478  		CollectionName: collName,
   479  	}
   480  	resp, err := c.Service.GetQuerySegmentInfo(ctx, req)
   481  	if err != nil {
   482  		return []*entity.Segment{}, err
   483  	}
   484  	if err := handleRespStatus(resp.GetStatus()); err != nil {
   485  		return []*entity.Segment{}, err
   486  	}
   487  
   488  	segments := make([]*entity.Segment, 0, len(resp.GetInfos()))
   489  	for _, info := range resp.GetInfos() {
   490  		segments = append(segments, &entity.Segment{
   491  			ID:           info.GetSegmentID(),
   492  			CollectionID: info.GetCollectionID(),
   493  			ParititionID: info.GetPartitionID(),
   494  			IndexID:      info.GetIndexID(),
   495  			NumRows:      info.GetNumRows(),
   496  		})
   497  	}
   498  
   499  	return segments, nil
   500  }
   501  
   502  func (c *GrpcClient) CalcDistance(ctx context.Context, collName string, partitions []string,
   503  	metricType entity.MetricType, opLeft, opRight entity.Column) (entity.Column, error) {
   504  	if c.Service == nil {
   505  		return nil, ErrClientNotReady
   506  	}
   507  	if opLeft == nil || opRight == nil {
   508  		return nil, errors.New("operators cannot be nil")
   509  	}
   510  
   511  	// check meta
   512  	if err := c.checkCollectionExists(ctx, collName); err != nil {
   513  		return nil, err
   514  	}
   515  	for _, partition := range partitions {
   516  		if err := c.checkPartitionExists(ctx, collName, partition); err != nil {
   517  			return nil, err
   518  		}
   519  	}
   520  	if err := c.checkCollField(ctx, collName, opLeft.Name(), isVectorField); err != nil {
   521  		return nil, err
   522  	}
   523  	if err := c.checkCollField(ctx, collName, opRight.Name(), isVectorField); err != nil {
   524  		return nil, err
   525  	}
   526  
   527  	req := &milvuspb.CalcDistanceRequest{
   528  		OpLeft:  columnToVectorsArray(collName, partitions, opLeft),
   529  		OpRight: columnToVectorsArray(collName, partitions, opRight),
   530  		Params: entity.MapKvPairs(map[string]string{
   531  			"metric": string(metricType),
   532  		}),
   533  	}
   534  	if req.OpLeft == nil || req.OpRight == nil {
   535  		return nil, errors.New("invalid operator passed")
   536  	}
   537  
   538  	resp, err := c.Service.CalcDistance(ctx, req)
   539  	if err != nil {
   540  		return nil, err
   541  	}
   542  	if err := handleRespStatus(resp.GetStatus()); err != nil {
   543  		return nil, err
   544  	}
   545  
   546  	if fd := resp.GetFloatDist(); fd != nil {
   547  		return entity.NewColumnFloat("distance", fd.GetData()), nil
   548  	}
   549  	if id := resp.GetIntDist(); id != nil {
   550  		return entity.NewColumnInt32("distance", id.GetData()), nil
   551  	}
   552  
   553  	return nil, errors.New("distance field not supported")
   554  }
   555  
   556  func columnToVectorsArray(collName string, partitions []string, column entity.Column) *milvuspb.VectorsArray {
   557  	result := &milvuspb.VectorsArray{}
   558  	switch column.Type() {
   559  	case entity.FieldTypeInt64: // int64 id
   560  		int64Column, ok := column.(*entity.ColumnInt64)
   561  		if !ok {
   562  			return nil // server shall report error
   563  		}
   564  		ids := &milvuspb.VectorIDs{
   565  			CollectionName: collName,
   566  			PartitionNames: partitions,
   567  			FieldName:      column.Name(), // TODO use field name or column name?
   568  			IdArray: &schemapb.IDs{
   569  				IdField: &schemapb.IDs_IntId{
   570  					IntId: &schemapb.LongArray{
   571  						Data: int64Column.Data(),
   572  					},
   573  				},
   574  			},
   575  		}
   576  		result.Array = &milvuspb.VectorsArray_IdArray{IdArray: ids}
   577  	case entity.FieldTypeString: // string id
   578  		stringColumn, ok := column.(*entity.ColumnString)
   579  		if !ok {
   580  			return nil
   581  		}
   582  		ids := &milvuspb.VectorIDs{
   583  			CollectionName: collName,
   584  			PartitionNames: partitions,
   585  			FieldName:      column.Name(),
   586  			IdArray: &schemapb.IDs{
   587  				IdField: &schemapb.IDs_StrId{
   588  					StrId: &schemapb.StringArray{
   589  						Data: stringColumn.Data(),
   590  					},
   591  				},
   592  			},
   593  		}
   594  		result.Array = &milvuspb.VectorsArray_IdArray{IdArray: ids}
   595  	case entity.FieldTypeFloatVector:
   596  		fvColumn, ok := column.(*entity.ColumnFloatVector)
   597  		if !ok {
   598  			return nil
   599  		}
   600  		fvdata := fvColumn.Data()
   601  		data := make([]float32, 0, fvColumn.Len()*fvColumn.Dim())
   602  		for _, row := range fvdata {
   603  			data = append(data, row...)
   604  		}
   605  		result.Array = &milvuspb.VectorsArray_DataArray{DataArray: &schemapb.VectorField{
   606  			Dim: int64(fvColumn.Dim()),
   607  			Data: &schemapb.VectorField_FloatVector{
   608  				FloatVector: &schemapb.FloatArray{
   609  					Data: data,
   610  				},
   611  			},
   612  		}}
   613  	case entity.FieldTypeBinaryVector:
   614  		bvColumn, ok := column.(*entity.ColumnBinaryVector)
   615  		if !ok {
   616  			return nil
   617  		}
   618  		bvdata := bvColumn.Data()
   619  		data := make([]byte, 0, bvColumn.Dim()*bvColumn.Len()/8)
   620  		for _, row := range bvdata {
   621  			data = append(data, row...)
   622  		}
   623  		result.Array = &milvuspb.VectorsArray_DataArray{DataArray: &schemapb.VectorField{
   624  			Dim: int64(bvColumn.Dim()),
   625  			Data: &schemapb.VectorField_BinaryVector{
   626  				BinaryVector: data,
   627  			},
   628  		}}
   629  	default:
   630  		return nil
   631  	}
   632  	return result
   633  }
   634  
   635  func isCollectionPrimaryKey(coll *entity.Collection, column entity.Column) bool {
   636  	if coll == nil || coll.Schema == nil || column == nil {
   637  		return false
   638  	}
   639  
   640  	// temporary check logic, since only one primary field is supported
   641  	for _, field := range coll.Schema.Fields {
   642  		if field.PrimaryKey {
   643  			if field.Name == column.Name() && field.DataType == column.Type() {
   644  				return true
   645  			}
   646  			return false
   647  		}
   648  	}
   649  	return false
   650  }
   651  
   652  // estRowSize estimate size per row for the specified schema
   653  func estRowSize(sch *entity.Schema, selected []string) int64 {
   654  	var total int64
   655  	for _, field := range sch.Fields {
   656  		if len(selected) > 0 {
   657  			found := false
   658  			for _, sel := range selected {
   659  				if field.Name == sel {
   660  					found = true
   661  					break
   662  				}
   663  			}
   664  			if !found {
   665  				continue
   666  			}
   667  		}
   668  		switch field.DataType {
   669  		case entity.FieldTypeBool:
   670  			total++
   671  		case entity.FieldTypeInt8:
   672  			total++
   673  		case entity.FieldTypeInt16:
   674  			total += 2
   675  		case entity.FieldTypeInt32:
   676  			total += 4
   677  		case entity.FieldTypeInt64:
   678  			total += 8
   679  		case entity.FieldTypeFloat:
   680  			total += 4
   681  		case entity.FieldTypeDouble:
   682  			total += 8
   683  		case entity.FieldTypeString:
   684  			// TODO string need varchar[max] syntax like limitation
   685  		case entity.FieldTypeVarChar:
   686  			maxLength, err := strconv.Atoi(field.TypeParams[entity.TypeParamMaxLength])
   687  			if err != nil {
   688  				log.Fatalf("got invalid varchar max length = %s", field.TypeParams[entity.TypeParamMaxLength])
   689  			}
   690  			total += int64(maxLength)
   691  		case entity.FieldTypeFloatVector:
   692  			dimStr := field.TypeParams[entity.TypeParamDim]
   693  			dim, _ := strconv.ParseInt(dimStr, 10, 64)
   694  			total += 4 * dim
   695  		case entity.FieldTypeBinaryVector:
   696  			dimStr := field.TypeParams[entity.TypeParamDim]
   697  			dim, _ := strconv.ParseInt(dimStr, 10, 64)
   698  			total += 4 * dim / 8
   699  		}
   700  	}
   701  	return total
   702  }