func (c *grpcClient) ReleaseCollection(ctx context.Context, collName string) error {
	if c.service == nil {
		return ErrClientNotReady
	}
	if err := c.checkCollectionExists(ctx, collName); err != nil {
		return err
	}
	req := &server.ReleaseCollectionRequest{
		DbName:         "",  
		CollectionName: collName,
	}
	resp, err := c.service.ReleaseCollection(ctx, req)
	if err != nil {
		return err
	}
	return handleRespStatus(resp)
}
ReleaseCollection release loaded collection
package client
import (
	"context"
	"errors"
	"fmt"
	"time"
	"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/server"
	"google.golang.org/grpc"
)
type grpcClient struct {
	conn    *grpc.ClientConn            
	service server.MilvusServiceClient  
}
func (c *grpcClient) connect(ctx context.Context, addr string, opts ...grpc.DialOption) error {
	if len(opts) == 0 {
		opts = append(opts, grpc.WithInsecure(),
			grpc.WithBlock(),                 
			grpc.WithTimeout(2*time.Second))  
	}
	conn, err := grpc.DialContext(ctx, addr, opts...)
	if err != nil {
		return err
	}
	c.conn = conn
	c.service = server.NewMilvusServiceClient(c.conn)
	return nil
}
func (c *grpcClient) Close() error {
	if c.conn != nil {
		err := c.conn.Close()
		c.conn = nil
		return err
	}
	return nil
}
func handleRespStatus(status *common.Status) error {
	if status == nil {
		return ErrStatusNil
	}
	if status.ErrorCode != common.ErrorCode_Success {
		if status.GetReason() != "" {
			return ErrServiceFailed(errors.New(status.GetReason()))
		}
		return ErrServiceFailed(errors.New("service failed"))
	}
	return nil
}
func (c *grpcClient) ListCollections(ctx context.Context) ([]*entity.Collection, error) {
	if c.service == nil {
		return []*entity.Collection{}, ErrClientNotReady
	}
	req := &server.ShowCollectionsRequest{
		DbName:    "",
		TimeStamp: 0,  
	}
	resp, err := c.service.ShowCollections(ctx, req)
	if err != nil {
		return []*entity.Collection{}, err
	}
	err = handleRespStatus(resp.GetStatus())
	if err != nil {
		return []*entity.Collection{}, err
	}
	collections := make([]*entity.Collection, 0, len(resp.GetCollectionIds()))
	for idx, item := range resp.CollectionIds {
		collection := &entity.Collection{
			ID:   item,
			Name: resp.GetCollectionNames()[idx],
		}
		collections = append(collections, collection)
	}
	return collections, nil
}
func (c *grpcClient) CreateCollection(ctx context.Context, collSchema *entity.Schema, shardNum int32) error {
	if c.service == nil {
		return ErrClientNotReady
	}
	if err := validateSchema(collSchema); err != nil {
		return err
	}
	has, err := c.HasCollection(ctx, collSchema.CollectionName)
	if err != nil {
		return err
	}
	if has {
		return fmt.Errorf("collection %s already exist", collSchema.CollectionName)
	}
	sch := collSchema.ProtoMessage()
	bs, err := proto.Marshal(sch)
	if err != nil {
		return err
	}
	req := &server.CreateCollectionRequest{
		DbName:         "",  
		CollectionName: collSchema.CollectionName,
		Schema:         bs,
		ShardsNum:      shardNum,
	}
	resp, err := c.service.CreateCollection(ctx, req)
	if err != nil {
		return err
	}
	err = handleRespStatus(resp)
	if err != nil {
		return nil
	}
	return nil
}
func validateSchema(sch *entity.Schema) error {
	if sch == nil {
		return errors.New("nil schema")
	}
	if sch.CollectionName == "" {
		return errors.New("collection name cannot be empty")
	}
	primaryKey := false
	autoID := false
	vectors := 0
	for _, field := range sch.Fields {
		if field.PrimaryKey {
			if primaryKey {  
				return errors.New("only one primary key only")
			}
			if field.DataType != entity.FieldTypeInt64 {  
				return errors.New("only int64 column can be primary key for now")
			}
			primaryKey = true
		}
		if field.AutoID {
			if autoID {
				return errors.New("only one auto id is available")
			}
			if field.DataType != entity.FieldTypeInt64 {
				return errors.New("only int64 column can be auto generated id")
			}
			autoID = true
		}
		if field.DataType == entity.FieldTypeFloatVector || field.DataType == entity.FieldTypeBinaryVector {
			vectors++
		}
	}
	if vectors <= 0 {
		return errors.New("vector field not set")
	}
	return nil
}
func (c *grpcClient) checkCollectionExists(ctx context.Context, collName string) error {
	has, err := c.HasCollection(ctx, collName)
	if err != nil {
		return err
	}
	if !has {
		return collNotExistsErr(collName)
	}
	return nil
}
func (c *grpcClient) DescribeCollection(ctx context.Context, collName string) (*entity.Collection, error) {
	if c.service == nil {
		return nil, ErrClientNotReady
	}
	if err := c.checkCollectionExists(ctx, collName); err != nil {
		return nil, err
	}
	req := &server.DescribeCollectionRequest{
		CollectionName: collName,
	}
	resp, err := c.service.DescribeCollection(ctx, req)
	if err != nil {
		return nil, err
	}
	err = handleRespStatus(resp.GetStatus())
	if err != nil {
		return nil, err
	}
	collection := &entity.Collection{
		ID:               resp.GetCollectionID(),
		Schema:           (&entity.Schema{}).ReadProto(resp.GetSchema()),
		PhysicalChannels: resp.GetPhysicalChannelNames(),
		VirtualChannels:  resp.GetVirtualChannelNames(),
	}
	collection.Name = collection.Schema.CollectionName
	return collection, nil
}
func (c *grpcClient) DropCollection(ctx context.Context, collName string) error {
	if c.service == nil {
		return ErrClientNotReady
	}
	if err := c.checkCollectionExists(ctx, collName); err != nil {
		return err
	}
	req := &server.DropCollectionRequest{
		CollectionName: collName,
	}
	resp, err := c.service.DropCollection(ctx, req)
	if err != nil {
		return err
	}
	return handleRespStatus(resp)
}
func (c *grpcClient) HasCollection(ctx context.Context, collName string) (bool, error) {
	if c.service == nil {
		return false, ErrClientNotReady
	}
	req := &server.HasCollectionRequest{
		DbName:         "",  
		CollectionName: collName,
		TimeStamp:      0,  
	}
	resp, err := c.service.HasCollection(ctx, req)
	if err != nil {
		return false, err
	}
	if err := handleRespStatus(resp.GetStatus()); err != nil {
		return false, err
	}
	return resp.GetValue(), nil
}
func (c *grpcClient) GetCollectionStatistics(ctx context.Context, collName string) (map[string]string, error) {
	if c.service == nil {
		return nil, ErrClientNotReady
	}
	if err := c.checkCollectionExists(ctx, collName); err != nil {
		return nil, err
	}
	req := &server.GetCollectionStatisticsRequest{
		CollectionName: collName,
	}
	resp, err := c.service.GetCollectionStatistics(ctx, req)
	if err != nil {
		return nil, err
	}
	if err := handleRespStatus(resp.GetStatus()); err != nil {
		return nil, err
	}
	return entity.KvPairsMap(resp.GetStats()), nil
}
func (c *grpcClient) ShowCollection(ctx context.Context, collName string) (*entity.Collection, error) {
	if c.service == nil {
		return nil, ErrClientNotReady
	}
	if err := c.checkCollectionExists(ctx, collName); err != nil {
		return nil, err
	}
	req := &server.ShowCollectionsRequest{
		Type:            server.ShowType_InMemory,
		CollectionNames: []string{collName},
	}
	resp, err := c.service.ShowCollections(ctx, req)
	if err != nil {
		return nil, err
	}
	if err := handleRespStatus(resp.GetStatus()); err != nil {
		return nil, err
	}
	if len(resp.CollectionIds) != 1 || len(resp.InMemoryPercentages) != 1 {
		return nil, errors.New("response len not valid")
	}
	return &entity.Collection{
		Loaded: resp.InMemoryPercentages[0] == 100,  
	}, nil
}
func (c *grpcClient) LoadCollection(ctx context.Context, collName string, async bool) error {
	if c.service == nil {
		return ErrClientNotReady
	}
	if err := c.checkCollectionExists(ctx, collName); err != nil {
		return err
	}
	req := &server.LoadCollectionRequest{
		CollectionName: collName,
	}
	resp, err := c.service.LoadCollection(ctx, req)
	if err != nil {
		return err
	}
	if err := handleRespStatus(resp); err != nil {
		return err
	}
	if !async {
		for {
			select {
			case <-ctx.Done():
				return errors.New("context deadline exceeded")
			default:
			}
			coll, err := c.ShowCollection(ctx, collName)
			if err != nil {
				return err
			}
			if coll.Loaded {
				break
			}
			time.Sleep(200 * time.Millisecond)  
		}
	}
	return nil
}