github.com/milvus-io/milvus-sdk-go/v2@v2.4.1/test/testcases/partition_key_test.go (about)

     1  //go:build L0
     2  
     3  package testcases
     4  
     5  import (
     6  	"fmt"
     7  	"math/rand"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/milvus-io/milvus-sdk-go/v2/client"
    15  	"github.com/milvus-io/milvus-sdk-go/v2/entity"
    16  
    17  	"github.com/milvus-io/milvus-sdk-go/v2/test/common"
    18  )
    19  
    20  // test enable partition key with int64 field
    21  func TestPartitionKeyDefaultInt64(t *testing.T) {
    22  	ctx := createContext(t, time.Second*common.DefaultTimeout)
    23  	mc := createMilvusClient(ctx, t)
    24  
    25  	// fields
    26  	partitionKeyFieldName := "partitionKeyField"
    27  	partitionKeyField := common.GenField(partitionKeyFieldName, entity.FieldTypeInt64,
    28  		common.WithIsPartitionKey(true), common.WithMaxLength(common.MaxLength))
    29  
    30  	// schema
    31  	schema := common.GenSchema(common.GenRandomString(6), false, common.GenDefaultFields(false))
    32  	schema.WithField(partitionKeyField)
    33  
    34  	// create collection and check partition key
    35  	err := mc.CreateCollection(ctx, schema, common.DefaultShards, client.WithPartitionNum(10))
    36  	common.CheckErr(t, err, true)
    37  
    38  	// insert data
    39  	partitionKeyColumn := common.GenColumnData(0, common.DefaultNb, entity.FieldTypeInt64, partitionKeyFieldName)
    40  	pkColumn, floatColumn, vecColumn := common.GenDefaultColumnData(0, common.DefaultNb, common.DefaultDim)
    41  	ids, errInsert := mc.Insert(ctx, schema.CollectionName, "", pkColumn, floatColumn, vecColumn, partitionKeyColumn)
    42  	common.CheckErr(t, errInsert, true)
    43  	require.Equalf(t, common.DefaultNb, ids.Len(), fmt.Sprintf("Expected insert result equal to %d, actual %d", common.DefaultNb, ids.Len()))
    44  
    45  	// flush and create index and load collection
    46  	mc.Flush(ctx, schema.CollectionName, true)
    47  	idx, _ := entity.NewIndexHNSW(entity.L2, 8, 96)
    48  	mc.CreateIndex(ctx, schema.CollectionName, common.DefaultFloatVecFieldName, idx, false)
    49  	mc.LoadCollection(ctx, schema.CollectionName, false)
    50  
    51  	// query filter partition key field and other field
    52  	queryIds := []entity.Column{
    53  		common.GenColumnData(0, 10, entity.FieldTypeInt64, partitionKeyFieldName),
    54  		common.GenColumnData(0, 10, entity.FieldTypeInt64, common.DefaultIntFieldName),
    55  	}
    56  	for _, queryID := range queryIds {
    57  		queryResult, errQuery := mc.QueryByPks(ctx, schema.CollectionName, []string{},
    58  			queryID,
    59  			[]string{common.DefaultIntFieldName},
    60  		)
    61  		common.CheckErr(t, errQuery, true)
    62  		common.CheckQueryResult(t, queryResult, []entity.Column{common.GenColumnData(0, 10, entity.FieldTypeInt64, common.DefaultIntFieldName)})
    63  	}
    64  
    65  	// search vector with expr: in, ==, >, non-partition-key field
    66  	sp, _ := entity.NewIndexHNSWSearchParam(64)
    67  	exprs := []string{
    68  		fmt.Sprintf("%s < 1000", partitionKeyFieldName),
    69  		fmt.Sprintf("%s == 1000", partitionKeyFieldName),
    70  		fmt.Sprintf("%s in [99, 199, 299, 399, 499, 599, 699, 799, 899, 999, 300, 789, 525, 22]", partitionKeyFieldName),
    71  		fmt.Sprintf("%s < 1000.0 && %s > 5", common.DefaultFloatFieldName, partitionKeyFieldName),
    72  	}
    73  	for _, expr := range exprs {
    74  		// expr filter
    75  		searchResult, errSearch := mc.Search(
    76  			ctx, schema.CollectionName,
    77  			[]string{},
    78  			expr,
    79  			[]string{common.DefaultIntFieldName},
    80  			common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector),
    81  			common.DefaultFloatVecFieldName,
    82  			entity.L2,
    83  			common.DefaultTopK,
    84  			sp,
    85  		)
    86  		common.CheckErr(t, errSearch, true)
    87  		if strings.Contains(expr, "==") {
    88  			common.CheckSearchResult(t, searchResult, common.DefaultNq, 1)
    89  		} else {
    90  			common.CheckSearchResult(t, searchResult, common.DefaultNq, common.DefaultTopK)
    91  		}
    92  		for _, res := range searchResult {
    93  			for _, id := range res.IDs.(*entity.ColumnInt64).Data() {
    94  				require.LessOrEqualf(t, id, int64(1000), "The id search returned is expected to <= 1000")
    95  			}
    96  		}
    97  	}
    98  }
    99  
   100  // test enable partition key with varchar field
   101  func TestPartitionKeyDefaultVarchar(t *testing.T) {
   102  	ctx := createContext(t, time.Second*common.DefaultTimeout)
   103  	mc := createMilvusClient(ctx, t)
   104  
   105  	// fields
   106  	partitionKeyFieldName := "partitionKeyField"
   107  	partitionKeyField := common.GenField(partitionKeyFieldName, entity.FieldTypeVarChar,
   108  		common.WithIsPartitionKey(true), common.WithMaxLength(common.MaxLength))
   109  
   110  	// schema
   111  	schema := common.GenSchema(common.GenRandomString(6), false, common.GenDefaultFields(false))
   112  	schema.WithField(partitionKeyField)
   113  
   114  	// create collection and check partition key
   115  	err := mc.CreateCollection(ctx, schema, common.DefaultShards, client.WithPartitionNum(10))
   116  	common.CheckErr(t, err, true)
   117  
   118  	// insert data
   119  	partitionKeyColumn := common.GenColumnData(0, common.DefaultNb, entity.FieldTypeVarChar, partitionKeyFieldName)
   120  	pkColumn, floatColumn, vecColumn := common.GenDefaultColumnData(0, common.DefaultNb, common.DefaultDim)
   121  	ids, errInsert := mc.Insert(ctx, schema.CollectionName, "", pkColumn, floatColumn, vecColumn, partitionKeyColumn)
   122  	common.CheckErr(t, errInsert, true)
   123  	require.Equalf(t, common.DefaultNb, ids.Len(), fmt.Sprintf("Expected insert result equal to %d, actual %d", common.DefaultNb, ids.Len()))
   124  
   125  	// flush and create index and load collection
   126  	mc.Flush(ctx, schema.CollectionName, true)
   127  	idx, _ := entity.NewIndexHNSW(entity.L2, 8, 96)
   128  	mc.CreateIndex(ctx, schema.CollectionName, common.DefaultFloatVecFieldName, idx, false)
   129  	mc.LoadCollection(ctx, schema.CollectionName, false)
   130  
   131  	// query filter partition key field and other field
   132  	queryIds := []entity.Column{
   133  		common.GenColumnData(0, 10, entity.FieldTypeVarChar, partitionKeyFieldName),
   134  		common.GenColumnData(0, 10, entity.FieldTypeInt64, common.DefaultIntFieldName),
   135  	}
   136  	for _, queryID := range queryIds {
   137  		queryResult, errQuery := mc.QueryByPks(ctx, schema.CollectionName, []string{},
   138  			queryID,
   139  			[]string{common.DefaultIntFieldName},
   140  		)
   141  		common.CheckErr(t, errQuery, true)
   142  		common.CheckQueryResult(t, queryResult, []entity.Column{common.GenColumnData(0, 10, entity.FieldTypeInt64, common.DefaultIntFieldName)})
   143  	}
   144  
   145  	// search vector with expr: in, ==, >, non-partition-key field
   146  	sp, _ := entity.NewIndexHNSWSearchParam(64)
   147  	exprs := []string{
   148  		fmt.Sprintf("%s < '9'", partitionKeyFieldName),
   149  		fmt.Sprintf("%s == '1000'", partitionKeyFieldName),
   150  		fmt.Sprintf("%s in ['99', '199', '299', '399', '499', '599', '699', '799', '899', '999', '300', '789', '525', '22']", partitionKeyFieldName),
   151  		fmt.Sprintf("%s < 1000.0 && %s > '5'", common.DefaultFloatFieldName, partitionKeyFieldName),
   152  	}
   153  	for _, expr := range exprs {
   154  		// expr filter
   155  		searchResult, errSearch := mc.Search(
   156  			ctx, schema.CollectionName,
   157  			[]string{},
   158  			expr,
   159  			[]string{common.DefaultIntFieldName},
   160  			common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector),
   161  			common.DefaultFloatVecFieldName,
   162  			entity.L2,
   163  			common.DefaultTopK,
   164  			sp,
   165  		)
   166  		common.CheckErr(t, errSearch, true)
   167  		if strings.Contains(expr, "==") {
   168  			common.CheckSearchResult(t, searchResult, common.DefaultNq, 1)
   169  		} else {
   170  			common.CheckSearchResult(t, searchResult, common.DefaultNq, common.DefaultTopK)
   171  		}
   172  	}
   173  }
   174  
   175  func TestPartitionKeyInvalidNumPartition(t *testing.T) {
   176  	ctx := createContext(t, time.Second*common.DefaultTimeout)
   177  	mc := createMilvusClient(ctx, t)
   178  
   179  	// prepare field and schema
   180  	partitionKeyFieldName := "partitionKeyField"
   181  	partitionKeyField := common.GenField(partitionKeyFieldName, entity.FieldTypeInt64, common.WithIsPartitionKey(true))
   182  
   183  	// schema
   184  	collName := common.GenRandomString(6)
   185  	schema := common.GenSchema(collName, false, common.GenDefaultFields(false))
   186  	schema.WithField(partitionKeyField)
   187  
   188  	invalidNumPartitionStruct := []struct {
   189  		numPartitions int64
   190  		errMsg        string
   191  	}{
   192  		{common.MaxPartitionNum + 1, "exceeds max configuration (4096)"},
   193  		{-1, "the specified partitions should be greater than 0 if partition key is used"},
   194  	}
   195  	for _, npStruct := range invalidNumPartitionStruct {
   196  
   197  		// create collection and check partition key
   198  		err := mc.CreateCollection(ctx, schema, common.DefaultShards, client.WithPartitionNum(npStruct.numPartitions))
   199  		common.CheckErr(t, err, false, npStruct.errMsg)
   200  	}
   201  
   202  	// PartitionNum is 0, actually default 64 partitions
   203  	err := mc.CreateCollection(ctx, schema, common.DefaultShards, client.WithPartitionNum(0))
   204  	common.CheckErr(t, err, true)
   205  	partitions, _ := mc.ShowPartitions(ctx, collName)
   206  	require.Equal(t, len(partitions), common.DefaultPartitionNum)
   207  }
   208  
   209  func TestPartitionKeyNumPartition(t *testing.T) {
   210  	// test set num partition range [1, 4096]
   211  	// set num partition
   212  	t.Parallel()
   213  
   214  	numPartitionsValues := []int64{
   215  		1,
   216  		128,
   217  		64,
   218  		4096,
   219  	}
   220  	for _, numPartitionsValue := range numPartitionsValues {
   221  		ctx := createContext(t, time.Second*common.DefaultTimeout)
   222  		mc := createMilvusClient(ctx, t)
   223  
   224  		// prepare field and schema
   225  		partitionKeyFieldName := "partitionKeyField"
   226  		partitionKeyField := common.GenField(partitionKeyFieldName, entity.FieldTypeInt64, common.WithIsPartitionKey(true))
   227  
   228  		// schema
   229  		collName := common.GenRandomString(6)
   230  		schema := common.GenSchema(collName, false, common.GenDefaultFields(false))
   231  		schema.WithField(partitionKeyField)
   232  
   233  		// create collection and check partition key
   234  		err := mc.CreateCollection(ctx, schema, common.DefaultShards, client.WithPartitionNum(numPartitionsValue))
   235  		common.CheckErr(t, err, true)
   236  
   237  		// insert and query search
   238  		collections, _ := mc.ListCollections(ctx)
   239  		common.CheckContainsCollection(t, collections, collName)
   240  	}
   241  
   242  }
   243  
   244  // test partition key on invalid field
   245  func TestPartitionKeyNotSupportFieldType(t *testing.T) {
   246  	t.Parallel()
   247  	// current only support int64 and varchar field
   248  	ctx := createContext(t, time.Second*common.DefaultTimeout)
   249  	mc := createMilvusClient(ctx, t)
   250  
   251  	notSupportPartitionKeyFieldType := []entity.FieldType{
   252  		entity.FieldTypeBool,
   253  		entity.FieldTypeInt8,
   254  		entity.FieldTypeInt16,
   255  		entity.FieldTypeInt32,
   256  		entity.FieldTypeFloat,
   257  		entity.FieldTypeDouble,
   258  		entity.FieldTypeJSON,
   259  		entity.FieldTypeBinaryVector,
   260  		entity.FieldTypeFloatVector,
   261  	}
   262  
   263  	for _, pkf := range notSupportPartitionKeyFieldType {
   264  		// prepare field and schema
   265  		partitionKeyFieldName := "partitionKeyField"
   266  		partitionKeyField := common.GenField(partitionKeyFieldName, pkf, common.WithIsPartitionKey(true))
   267  
   268  		// schema
   269  		collName := common.GenRandomString(6)
   270  		schema := common.GenSchema(collName, false, common.GenDefaultFields(false))
   271  		schema.WithField(partitionKeyField)
   272  
   273  		// create collection and check partition key
   274  		err := mc.CreateCollection(ctx, schema, common.DefaultShards, client.WithPartitionNum(10))
   275  		common.CheckErr(t, err, false, "the data type of partition key should be Int64 or VarChar")
   276  	}
   277  }
   278  
   279  // test multi partition key fields -> error
   280  func TestPartitionKeyMultiFields(t *testing.T) {
   281  	ctx := createContext(t, time.Second*common.DefaultTimeout)
   282  	mc := createMilvusClient(ctx, t)
   283  
   284  	// multi partition key field
   285  	partitionKeyField1 := common.GenField("intPartitionKeyField", entity.FieldTypeInt64, common.WithIsPartitionKey(true))
   286  	partitionKeyField2 := common.GenField("varcharPartitionKeyField", entity.FieldTypeInt64, common.WithIsPartitionKey(true))
   287  
   288  	// schema
   289  	collName := common.GenRandomString(6)
   290  	schema := common.GenSchema(collName, false, common.GenDefaultFields(false))
   291  	schema.WithField(partitionKeyField1).WithField(partitionKeyField2)
   292  
   293  	// create collection and check partition key
   294  	err := mc.CreateCollection(ctx, schema, common.DefaultShards, client.WithPartitionNum(10))
   295  	common.CheckErr(t, err, false, "there are more than one partition key")
   296  }
   297  
   298  func TestPartitionNumWhenDisablePartitionKey(t *testing.T) {
   299  	ctx := createContext(t, time.Second*common.DefaultTimeout)
   300  	mc := createMilvusClient(ctx, t)
   301  
   302  	// schema
   303  	collName := common.GenRandomString(6)
   304  	schema := common.GenSchema(collName, false, common.GenDefaultFields(false))
   305  
   306  	// create collection and check partition key
   307  	err := mc.CreateCollection(ctx, schema, common.DefaultShards, client.WithPartitionNum(10))
   308  	common.CheckErr(t, err, false, "num_partitions should only be specified with partition key field enabled")
   309  }
   310  
   311  // test operate partition related after enable partition key -> error expect has partition
   312  func TestPartitionKeyPartitionOperation(t *testing.T) {
   313  	ctx := createContext(t, time.Second*common.DefaultTimeout)
   314  	mc := createMilvusClient(ctx, t)
   315  
   316  	// multi partition key field
   317  	partitionKeyField := common.GenField("partitionKeyField", entity.FieldTypeInt64, common.WithIsPartitionKey(true))
   318  
   319  	// schema
   320  	collName := common.GenRandomString(6)
   321  	schema := common.GenSchema(collName, true, common.GenDefaultFields(true))
   322  	schema.WithField(partitionKeyField)
   323  
   324  	// create collection and check partition key
   325  	partitionNum := 10
   326  	err := mc.CreateCollection(ctx, schema, common.DefaultShards, client.WithPartitionNum(int64(partitionNum)))
   327  	common.CheckErr(t, err, true)
   328  
   329  	// list partitions -> success
   330  	partitions, err := mc.ShowPartitions(ctx, collName)
   331  	common.CheckErr(t, err, true)
   332  	require.Lenf(t, partitions, partitionNum, fmt.Sprintf("Expected collection has %d partitions, actually %d.", partitionNum, len(partitions)))
   333  
   334  	// has partition -> success
   335  	has, err := mc.HasPartition(ctx, collName, partitions[0].Name)
   336  	require.True(t, has)
   337  	common.CheckErr(t, err, true)
   338  
   339  	// create partition -> error
   340  	err = mc.CreatePartition(ctx, collName, common.GenRandomString(4))
   341  	common.CheckErr(t, err, false, "disable create partition if partition key mode is used")
   342  
   343  	// drop partition -> error
   344  	err = mc.DropPartition(ctx, collName, partitions[2].Name)
   345  	common.CheckErr(t, err, false, "disable drop partition if partition key mode is used")
   346  
   347  	// load partition -> error
   348  	err = mc.LoadPartitions(ctx, collName, []string{partitions[0].Name}, true)
   349  	common.CheckErr(t, err, false, "disable load partitions if partition key mode is used")
   350  
   351  	// release partition -> error
   352  	err = mc.ReleasePartitions(ctx, collName, []string{partitions[0].Name})
   353  	common.CheckErr(t, err, false, "disable release partitions if partition key mode is used")
   354  
   355  	// insert into partition -> error
   356  	partitionKeyColumn := common.GenColumnData(0, common.DefaultNb, entity.FieldTypeInt64, partitionKeyField.Name)
   357  	_, floatColumn, vecColumn := common.GenDefaultColumnData(0, common.DefaultNb, common.DefaultDim)
   358  	_, err = mc.Insert(ctx, schema.CollectionName, partitions[0].Name, floatColumn, vecColumn, partitionKeyColumn)
   359  	common.CheckErr(t, err, false, "not support manually specifying the partition names if partition key mode is used")
   360  
   361  	// InsertRows into partition -> error
   362  	vector := make([]float32, 0, common.DefaultDim)
   363  	for j := 0; j < int(common.DefaultDim); j++ {
   364  		vector = append(vector, rand.Float32())
   365  	}
   366  	row := struct {
   367  		Int64             int64     `json:"int64" milvus:"name:int64"`
   368  		Float             float32   `json:"float" milvus:"name:float"`
   369  		FloatVec          []float32 `json:"floatVec" milvus:"name:floatVec"`
   370  		PartitionKeyField int64     `json:"partitionKeyField" milvus:"name:partitionKeyField"`
   371  	}{int64(1), float32(1), vector, int64(2)}
   372  	_, err = mc.InsertRows(ctx, collName, partitions[0].Name, []interface{}{row})
   373  	common.CheckErr(t, err, false, "not support manually specifying the partition names if partition key mode is used")
   374  
   375  	// delete from partition -> error
   376  	err = mc.DeleteByPks(ctx, collName, partitions[2].Name, entity.NewColumnInt64(common.DefaultIntFieldName, []int64{0, 1}))
   377  	common.CheckErr(t, err, false, "not support manually specifying the partition names if partition key mode is used")
   378  
   379  	// bulk insert -> error
   380  	_, err = mc.BulkInsert(ctx, collName, partitions[0].Name, []string{""})
   381  	common.CheckErr(t, err, false, "not allow to set partition name for collection with partition key: importing data failed")
   382  
   383  	// query partitions -> error
   384  	_, err = mc.QueryByPks(
   385  		ctx, collName,
   386  		[]string{partitions[0].Name},
   387  		entity.NewColumnInt64(common.DefaultIntFieldName, []int64{0}), []string{})
   388  	common.CheckErr(t, err, false, "not support manually specifying the partition names if partition key mode is used")
   389  
   390  	// search
   391  	sp, _ := entity.NewIndexHNSWSearchParam(74)
   392  	_, err = mc.Search(
   393  		ctx, collName, []string{partitions[0].Name}, "", []string{},
   394  		common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector), common.DefaultFloatVecFieldName,
   395  		entity.L2, 1, sp)
   396  	common.CheckErr(t, err, false, "not support manually specifying the partition names if partition key mode is used")
   397  }