github.com/milvus-io/milvus-sdk-go/v2@v2.4.1/client/index.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  	"fmt"
    17  	"strconv"
    18  	"time"
    19  
    20  	"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
    21  	"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
    22  	"github.com/milvus-io/milvus-sdk-go/v2/entity"
    23  )
    24  
    25  const (
    26  	mmapKey = "mmap.enabled"
    27  )
    28  
    29  func (c *GrpcClient) checkCollField(ctx context.Context, collName string, fieldName string, filters ...func(string, string, *entity.Field) error) error {
    30  	if err := c.checkCollectionExists(ctx, collName); err != nil {
    31  		return err
    32  	}
    33  	coll, err := c.DescribeCollection(ctx, collName)
    34  	if err != nil {
    35  		return err
    36  	}
    37  	var f *entity.Field
    38  	for _, field := range coll.Schema.Fields {
    39  		if field.Name == fieldName {
    40  			f = field
    41  			for _, filter := range filters {
    42  				if err := filter(collName, fieldName, f); err != nil {
    43  					return err
    44  				}
    45  			}
    46  			break
    47  		}
    48  	}
    49  	if f == nil {
    50  		return fmt.Errorf("field %s of collection %s does not exist", fieldName, collName)
    51  	}
    52  	return nil
    53  }
    54  
    55  func isVectorField(collName, fieldName string, f *entity.Field) error {
    56  	if f.DataType != entity.FieldTypeFloatVector && f.DataType != entity.FieldTypeBinaryVector {
    57  		return fmt.Errorf("field %s of collection %s is not vector field", fieldName, collName)
    58  	}
    59  	return nil
    60  }
    61  
    62  type indexDef struct {
    63  	name           string
    64  	fieldName      string
    65  	collectionName string
    66  	params         []*commonpb.KeyValuePair
    67  	MsgBase        *commonpb.MsgBase
    68  }
    69  
    70  // IndexOption is the predefined function to alter index def.
    71  // shared among create, describe, drop indexes operations.
    72  type IndexOption func(*indexDef)
    73  
    74  // WithIndexName returns an IndexOption with customized index name.
    75  func WithIndexName(name string) IndexOption {
    76  	return func(def *indexDef) {
    77  		def.name = name
    78  	}
    79  }
    80  
    81  func WithIndexMsgBase(msgBase *commonpb.MsgBase) IndexOption {
    82  	return func(def *indexDef) {
    83  		def.MsgBase = msgBase
    84  	}
    85  }
    86  
    87  func WithMmap(enabled bool) IndexOption {
    88  	return func(id *indexDef) {
    89  		id.params = append(id.params, &commonpb.KeyValuePair{
    90  			Key:   mmapKey,
    91  			Value: strconv.FormatBool(enabled),
    92  		})
    93  	}
    94  }
    95  
    96  func getIndexDef(opts ...IndexOption) indexDef {
    97  	idxDef := indexDef{}
    98  	for _, opt := range opts {
    99  		opt(&idxDef)
   100  	}
   101  	return idxDef
   102  }
   103  
   104  // CreateIndex create index for collection
   105  // Deprecated please use CreateIndexV2 instead.
   106  func (c *GrpcClient) CreateIndex(ctx context.Context, collName string, fieldName string,
   107  	idx entity.Index, async bool, opts ...IndexOption) error {
   108  	if c.Service == nil {
   109  		return ErrClientNotReady
   110  	}
   111  	if err := c.checkCollField(ctx, collName, fieldName); err != nil {
   112  		return err
   113  	}
   114  
   115  	idxDef := getIndexDef(opts...)
   116  
   117  	req := &milvuspb.CreateIndexRequest{
   118  		Base:           idxDef.MsgBase,
   119  		DbName:         "", // reserved
   120  		CollectionName: collName,
   121  		FieldName:      fieldName,
   122  		IndexName:      idxDef.name,
   123  		ExtraParams:    entity.MapKvPairs(idx.Params()),
   124  	}
   125  
   126  	req.ExtraParams = append(req.ExtraParams, idxDef.params...)
   127  
   128  	resp, err := c.Service.CreateIndex(ctx, req)
   129  	if err != nil {
   130  		return err
   131  	}
   132  	if err = handleRespStatus(resp); err != nil {
   133  		return err
   134  	}
   135  	if !async { // sync mode, wait index building result
   136  		for {
   137  			idxDesc, err := c.describeIndex(ctx, collName, fieldName, opts...)
   138  			if err != nil {
   139  				return err
   140  			}
   141  			for _, desc := range idxDesc {
   142  				if (idxDef.name == "" && desc.GetFieldName() == fieldName) || idxDef.name == desc.GetIndexName() {
   143  					switch desc.GetState() {
   144  					case commonpb.IndexState_Finished:
   145  						return nil
   146  					case commonpb.IndexState_Failed:
   147  						return fmt.Errorf("create index failed, reason: %s", desc.GetIndexStateFailReason())
   148  					}
   149  				}
   150  			}
   151  
   152  			time.Sleep(100 * time.Millisecond) // wait 100ms
   153  		}
   154  	}
   155  	return nil
   156  }
   157  
   158  // AlterIndex modifies the index params
   159  func (c *GrpcClient) AlterIndex(ctx context.Context, collName string, indexName string, opts ...IndexOption) error {
   160  	if c.Service == nil {
   161  		return ErrClientNotReady
   162  	}
   163  
   164  	idxDef := getIndexDef(opts...)
   165  
   166  	req := &milvuspb.AlterIndexRequest{
   167  		Base:           idxDef.MsgBase,
   168  		DbName:         "", // reserved
   169  		CollectionName: collName,
   170  		IndexName:      indexName,
   171  		ExtraParams:    idxDef.params,
   172  	}
   173  
   174  	resp, err := c.Service.AlterIndex(ctx, req)
   175  	if err != nil {
   176  		return err
   177  	}
   178  	return handleRespStatus(resp)
   179  }
   180  
   181  // DescribeIndex describe index
   182  func (c *GrpcClient) DescribeIndex(ctx context.Context, collName string, fieldName string, opts ...IndexOption) ([]entity.Index, error) {
   183  	if c.Service == nil {
   184  		return []entity.Index{}, ErrClientNotReady
   185  	}
   186  	if err := c.checkCollField(ctx, collName, fieldName); err != nil {
   187  		return []entity.Index{}, err
   188  	}
   189  
   190  	idxDesc, err := c.describeIndex(ctx, collName, fieldName, opts...)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  
   195  	indexes := make([]entity.Index, 0, len(idxDesc))
   196  	for _, info := range idxDesc {
   197  		if fieldName != "" && info.GetFieldName() != fieldName {
   198  			continue
   199  		}
   200  		params := entity.KvPairsMap(info.Params)
   201  		it := params["index_type"] // TODO change to const
   202  		idx := entity.NewGenericIndex(
   203  			info.IndexName,
   204  			entity.IndexType(it),
   205  			params,
   206  		)
   207  		indexes = append(indexes, idx)
   208  	}
   209  	return indexes, nil
   210  }
   211  
   212  // DropIndex drop index from collection
   213  // Deprecate please use DropIndexV2 instead.
   214  func (c *GrpcClient) DropIndex(ctx context.Context, collName string, fieldName string, opts ...IndexOption) error {
   215  	if c.Service == nil {
   216  		return ErrClientNotReady
   217  	}
   218  
   219  	idxDef := getIndexDef(opts...)
   220  	req := &milvuspb.DropIndexRequest{
   221  		Base:           idxDef.MsgBase,
   222  		DbName:         "", //reserved,
   223  		CollectionName: collName,
   224  		FieldName:      fieldName,
   225  		IndexName:      idxDef.name,
   226  	}
   227  	if idxDef.name != "" {
   228  		req.IndexName = idxDef.name
   229  	}
   230  
   231  	resp, err := c.Service.DropIndex(ctx, req)
   232  	if err != nil {
   233  		return err
   234  	}
   235  	return handleRespStatus(resp)
   236  }
   237  
   238  // GetIndexState get index state
   239  func (c *GrpcClient) GetIndexState(ctx context.Context, collName string, fieldName string, opts ...IndexOption) (entity.IndexState, error) {
   240  	if c.Service == nil {
   241  		return entity.IndexState(commonpb.IndexState_Failed), ErrClientNotReady
   242  	}
   243  	if err := c.checkCollField(ctx, collName, fieldName); err != nil {
   244  		return entity.IndexState(commonpb.IndexState_IndexStateNone), err
   245  	}
   246  
   247  	idxDef := getIndexDef(opts...)
   248  	req := &milvuspb.GetIndexStateRequest{
   249  		DbName:         "",
   250  		CollectionName: collName,
   251  		FieldName:      fieldName,
   252  		IndexName:      idxDef.name,
   253  	}
   254  	resp, err := c.Service.GetIndexState(ctx, req)
   255  	if err != nil {
   256  		return entity.IndexState(commonpb.IndexState_IndexStateNone), err
   257  	}
   258  	if err := handleRespStatus(resp.GetStatus()); err != nil {
   259  		return entity.IndexState(commonpb.IndexState_IndexStateNone), err
   260  	}
   261  
   262  	return entity.IndexState(resp.GetState()), nil
   263  }
   264  
   265  // GetIndexBuildProgress get index building progress
   266  func (c *GrpcClient) GetIndexBuildProgress(ctx context.Context, collName string, fieldName string, opts ...IndexOption) (total, indexed int64, err error) {
   267  	if c.Service == nil {
   268  		return 0, 0, ErrClientNotReady
   269  	}
   270  	if err := c.checkCollField(ctx, collName, fieldName); err != nil {
   271  		return 0, 0, err
   272  	}
   273  
   274  	idxDef := getIndexDef(opts...)
   275  	req := &milvuspb.GetIndexBuildProgressRequest{
   276  		DbName:         "",
   277  		CollectionName: collName,
   278  		FieldName:      fieldName,
   279  		IndexName:      idxDef.name,
   280  	}
   281  	resp, err := c.Service.GetIndexBuildProgress(ctx, req)
   282  	if err != nil {
   283  		return 0, 0, err
   284  	}
   285  	if err = handleRespStatus(resp.GetStatus()); err != nil {
   286  		return 0, 0, err
   287  	}
   288  	return resp.GetTotalRows(), resp.GetIndexedRows(), nil
   289  }
   290  
   291  func (c *GrpcClient) describeIndex(ctx context.Context, collName string, fieldName string, opts ...IndexOption) ([]*milvuspb.IndexDescription, error) {
   292  	idxDef := getIndexDef(opts...)
   293  	req := &milvuspb.DescribeIndexRequest{
   294  		CollectionName: collName,
   295  		FieldName:      fieldName,
   296  		IndexName:      idxDef.name,
   297  	}
   298  
   299  	resp, err := c.Service.DescribeIndex(ctx, req)
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  	if err := handleRespStatus(resp.GetStatus()); err != nil {
   304  		return nil, err
   305  	}
   306  
   307  	return resp.GetIndexDescriptions(), nil
   308  }