milvus/tests/go_client/testcases/collection_test.go
Tianx 2c0c5ef41e
feat: timestamptz expression & index & timezone (#44080)
issue: https://github.com/milvus-io/milvus/issues/27467

>My plan is as follows.
>- [x] M1 Create collection with timestamptz field
>- [x] M2 Insert timestamptz field data
>- [x] M3 Retrieve timestamptz field data
>- [x] M4 Implement handoff
>- [x] M5 Implement compare operator
>- [x] M6 Implement extract operator
 >- [x] M8 Support database/collection level default timezone
>- [x] M7 Support STL-SORT index for datatype timestamptz

---

The third PR of issue: https://github.com/milvus-io/milvus/issues/27467,
which completes M5, M6, M7, M8 described above.

## M8 Default Timezone

We will be able to use alter_collection() and alter_database() in a
future Python SDK release to modify the default timezone at the
collection or database level.

For insert requests, the timezone will be resolved using the following
order of precedence: String Literal-> Collection Default -> Database
Default.
For retrieval requests, the timezone will be resolved in this order:
Query Parameters -> Collection Default -> Database Default.
In both cases, the final fallback timezone is UTC.


## M5: Comparison Operators

We can now use the following expression format to filter on the
timestamptz field:

- `timestamptz_field [+/- INTERVAL 'interval_string'] {comparison_op}
ISO 'iso_string' `

- The interval_string follows the ISO 8601 duration format, for example:
P1Y2M3DT1H2M3S.

- The iso_string follows the ISO 8601 timestamp format, for example:
2025-01-03T00:00:00+08:00.

- Example expressions: "tsz + INTERVAL 'P0D' != ISO
'2025-01-03T00:00:00+08:00'" or "tsz != ISO
'2025-01-03T00:00:00+08:00'".

## M6: Extract

We will be able to extract sepecific time filed by kwargs in a future
Python SDK release.
The key is `time_fields`, and value should be one or more of "year,
month, day, hour, minute, second, microsecond", seperated by comma or
space. Then the result of each record would be an array of int64.



## M7: Indexing Support

Expressions without interval arithmetic can be accelerated using an
STL-SORT index. However, expressions that include interval arithmetic
cannot be indexed. This is because the result of an interval calculation
depends on the specific timestamp value. For example, adding one month
to a date in February results in a different number of added days than
adding one month to a date in March.

--- 

After this PR, the input / output type of timestamptz would be iso
string. Timestampz would be stored as timestamptz data, which is int64_t
finally.

> for more information, see https://en.wikipedia.org/wiki/ISO_8601

---------

Signed-off-by: xtx <xtianx@smail.nju.edu.cn>
2025-09-23 10:24:12 +08:00

1241 lines
61 KiB
Go

package testcases
import (
"fmt"
"strconv"
"testing"
"time"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/client/v2/index"
client "github.com/milvus-io/milvus/client/v2/milvusclient"
"github.com/milvus-io/milvus/pkg/v2/log"
"github.com/milvus-io/milvus/tests/go_client/common"
hp "github.com/milvus-io/milvus/tests/go_client/testcases/helper"
)
var prefix = "collection"
// test create default floatVec and binaryVec collection
func TestCreateCollection(t *testing.T) {
t.Parallel()
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
for _, collectionFieldsType := range []hp.CollectionFieldsType{hp.Int64Vec, hp.VarcharBinary, hp.Int64VarcharSparseVec, hp.AllFields} {
fields := hp.FieldsFact.GenFieldsForCollection(collectionFieldsType, hp.TNewFieldOptions())
schema := hp.GenSchema(hp.TNewSchemaOption().TWithFields(fields))
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(schema.CollectionName, schema))
common.CheckErr(t, err, true)
// has collections and verify
has, err := mc.HasCollection(ctx, client.NewHasCollectionOption(schema.CollectionName))
common.CheckErr(t, err, true)
require.True(t, has)
// list collections and verify
collections, err := mc.ListCollections(ctx, client.NewListCollectionOption())
common.CheckErr(t, err, true)
require.Contains(t, collections, schema.CollectionName)
}
}
// fast: create -> index -> load
func TestCreateCollectionFast(t *testing.T) {
// test collection property mmap
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// create collection option: WithConsistencyLevel Strong,
collName := common.GenRandomString("alter", 6)
err := mc.CreateCollection(ctx, client.SimpleCreateCollectionOptions(collName, common.DefaultDim))
common.CheckErr(t, err, true)
// verify collection option
coll, _ := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(collName))
require.Equal(t, entity.FieldTypeInt64, coll.Schema.PKField().DataType)
// load -> insert
prepare, _ := hp.CollPrepare.InsertData(ctx, t, mc, hp.NewInsertParams(coll.Schema), hp.TNewDataOption())
prepare.FlushData(ctx, t, mc, collName)
countRes, err := mc.Query(ctx, client.NewQueryOption(collName).WithFilter("").WithOutputFields(common.QueryCountFieldName).WithConsistencyLevel(entity.ClStrong))
common.CheckErr(t, err, true)
count, _ := countRes.Fields[0].GetAsInt64(0)
require.EqualValues(t, common.DefaultNb, count)
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector)
resSearch, err := mc.Search(ctx, client.NewSearchOption(collName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong))
common.CheckErr(t, err, true)
common.CheckSearchResult(t, resSearch, common.DefaultNq, common.DefaultLimit)
}
func TestCreateCollectionFastOption(t *testing.T) {
// test create collection fast with option: ConsistencyLevel, varcharPk, indexOption
// Collection AutoID not works !!!, please set it on the field side~
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// create collection option: WithConsistencyLevel Strong,
collName := common.GenRandomString("alter", 6)
index := index.NewHNSWIndex(entity.COSINE, 8, 96)
indexOption := client.NewCreateIndexOption(collName, common.DefaultFastVector, index)
err := mc.CreateCollection(ctx, client.SimpleCreateCollectionOptions(collName, common.DefaultDim).WithDynamicSchema(true).
WithConsistencyLevel(entity.ClStrong).WithIndexOptions(indexOption).WithVarcharPK(true, 10))
common.CheckErr(t, err, true)
// verify collection option
coll, _ := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(collName))
require.Equal(t, entity.ClStrong, coll.ConsistencyLevel)
require.Equal(t, entity.FieldTypeVarChar, coll.Schema.PKField().DataType)
t.Log("https://github.com/milvus-io/milvus/issues/39524")
// descIdx, _ := mc.DescribeIndex(ctx, client.NewDescribeIndexOption(collName, common.DefaultFastVector))
// common.CheckIndex(t, descIdx, index, common.TNewCheckIndexOpt(common.DefaultNb))
// insert
hp.CollPrepare.InsertData(ctx, t, mc, hp.NewInsertParams(coll.Schema), hp.TNewDataOption())
countRes, err := mc.Query(ctx, client.NewQueryOption(collName).
WithFilter("").WithOutputFields(common.QueryCountFieldName))
common.CheckErr(t, err, true)
count, _ := countRes.Fields[0].GetAsInt64(0)
require.EqualValues(t, common.DefaultNb, count)
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector)
resSearch, err := mc.Search(ctx, client.NewSearchOption(collName, common.DefaultLimit, vectors).WithOutputFields(common.DefaultDynamicNumberField))
common.CheckErr(t, err, true)
common.CheckSearchResult(t, resSearch, common.DefaultNq, common.DefaultLimit)
common.CheckOutputFields(t, []string{common.DefaultDynamicNumberField}, resSearch[0].Fields)
}
func TestCreateAutoIdCollectionField(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
int64Field := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true).WithIsAutoID(true)
varcharField := entity.NewField().WithName(common.DefaultVarcharFieldName).WithDataType(entity.FieldTypeVarChar).WithIsPrimaryKey(true).WithIsAutoID(true).WithMaxLength(common.MaxLength)
for _, pkField := range []*entity.Field{int64Field, varcharField} {
// pk field with name
collName := common.GenRandomString(prefix, 6)
schema := entity.NewSchema().WithName(collName).WithField(pkField).WithField(vecField)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, true)
// verify field name
coll, err := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(collName))
common.CheckErr(t, err, true)
require.True(t, coll.Schema.AutoID)
require.True(t, coll.Schema.Fields[0].AutoID)
// insert
vecColumn := hp.GenColumnData(common.DefaultNb, vecField.DataType, *hp.TNewDataOption())
_, err = mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, vecColumn))
common.CheckErr(t, err, true)
}
}
// create collection and specify shard num
func TestCreateCollectionShards(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
int64Field := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true).WithIsAutoID(true)
for _, shard := range []int32{-1, 0, 2, 16} {
// pk field with name
collName := common.GenRandomString(prefix, 6)
schema := entity.NewSchema().WithName(collName).WithField(int64Field).WithField(vecField)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema).WithShardNum(shard))
common.CheckErr(t, err, true)
// verify field name
coll, err := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(collName))
common.CheckErr(t, err, true)
if shard < 1 {
shard = 1
}
require.Equal(t, shard, coll.ShardNum)
}
}
// test create auto collection with schema
func TestCreateAutoIdCollectionSchema(t *testing.T) {
t.Skip("waiting for valid AutoId from schema params")
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
for _, pkFieldType := range []entity.FieldType{entity.FieldTypeVarChar, entity.FieldTypeInt64} {
pkField := entity.NewField().WithName("pk").WithDataType(pkFieldType).WithIsPrimaryKey(true).WithMaxLength(common.MaxLength)
// pk field with name
schema := entity.NewSchema().WithName(collName).WithField(pkField).WithField(vecField).WithAutoID(true)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, true)
// verify field name
coll, err := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(collName))
common.CheckErr(t, err, true)
log.Info("schema autoID", zap.Bool("schemaAuto", coll.Schema.AutoID))
log.Info("field autoID", zap.Bool("fieldAuto", coll.Schema.Fields[0].AutoID))
// insert
vecColumn := hp.GenColumnData(common.DefaultNb, vecField.DataType, *hp.TNewDataOption())
_, err = mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, vecColumn))
common.CheckErr(t, err, false, "field pk not passed")
}
}
// test create auto collection with collection option
func TestCreateAutoIdCollection(t *testing.T) {
t.Skip("https://github.com/milvus-io/milvus/issues/39523")
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
for _, pkFieldType := range []entity.FieldType{entity.FieldTypeVarChar, entity.FieldTypeInt64} {
pkField := entity.NewField().WithName("pk").WithDataType(pkFieldType).WithIsPrimaryKey(true).WithMaxLength(common.MaxLength)
// pk field with name
schema := entity.NewSchema().WithName(collName).WithField(pkField).WithField(vecField).WithAutoID(true)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema).WithAutoID(true))
common.CheckErr(t, err, true)
// verify field name
coll, err := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(collName))
common.CheckErr(t, err, true)
log.Info("schema autoID", zap.Bool("schemaAuto", coll.Schema.AutoID))
log.Info("field autoID", zap.Bool("fieldAuto", coll.Schema.Fields[0].AutoID))
// insert
vecColumn := hp.GenColumnData(common.DefaultNb, vecField.DataType, *hp.TNewDataOption())
_, err = mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, vecColumn))
common.CheckErr(t, err, false, "field pk not passed")
}
}
func TestCreateJsonCollection(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
pkField := entity.NewField().WithName(common.DefaultVarcharFieldName).WithDataType(entity.FieldTypeVarChar).WithIsPrimaryKey(true).WithMaxLength(common.MaxLength)
jsonField := entity.NewField().WithName(common.DefaultJSONFieldName).WithDataType(entity.FieldTypeJSON)
// pk field with name
schema := entity.NewSchema().WithName(collName).WithField(pkField).WithField(vecField).WithField(jsonField)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, true)
// verify field name
has, err := mc.HasCollection(ctx, client.NewHasCollectionOption(schema.CollectionName))
common.CheckErr(t, err, true)
require.True(t, has)
}
func TestCreateArrayCollections(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
pkField := entity.NewField().WithName(common.DefaultVarcharFieldName).WithDataType(entity.FieldTypeVarChar).WithIsPrimaryKey(true).WithMaxLength(common.MaxLength)
schema := entity.NewSchema().WithName(collName).WithField(pkField).WithField(vecField)
for _, eleType := range hp.GetAllArrayElementType() {
arrayField := entity.NewField().WithName(hp.GetFieldNameByElementType(eleType)).WithDataType(entity.FieldTypeArray).WithElementType(eleType).WithMaxCapacity(common.MaxCapacity)
if eleType == entity.FieldTypeVarChar {
arrayField.WithMaxLength(common.MaxLength)
}
schema.WithField(arrayField)
}
// pk field with name
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, true)
// verify field name
has, err := mc.HasCollection(ctx, client.NewHasCollectionOption(schema.CollectionName))
common.CheckErr(t, err, true)
require.True(t, has)
}
// test create collection with partition key not supported field type
func TestCreateCollectionPartitionKey(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout*2)
mc := hp.CreateDefaultMilvusClient(ctx, t)
int64Field := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
for _, fieldType := range []entity.FieldType{entity.FieldTypeVarChar, entity.FieldTypeInt64} {
partitionKeyField := entity.NewField().WithName("par_key").WithDataType(fieldType).WithIsPartitionKey(true).WithMaxLength(common.TestMaxLen)
collName := common.GenRandomString(prefix, 6)
schema := entity.NewSchema().WithName(collName).WithField(int64Field).WithField(vecField).WithField(partitionKeyField)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, true)
coll, err := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(collName))
common.CheckErr(t, err, true)
for _, field := range coll.Schema.Fields {
if field.Name == "par_key" {
require.True(t, field.IsPartitionKey)
}
}
// verify partitions
partitions, err := mc.ListPartitions(ctx, client.NewListPartitionOption(collName))
require.Len(t, partitions, common.DefaultPartitionNum)
common.CheckErr(t, err, true)
}
}
// test create partition key collection WithPartitionNum
func TestCreateCollectionPartitionKeyNumPartition(t *testing.T) {
t.Skip("Waiting for WithPartitionNum")
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
int64Field := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
partitionKeyField := entity.NewField().WithName("par_key").WithDataType(entity.FieldTypeInt64).WithIsPartitionKey(true)
t.Parallel()
for _, numPartition := range []int64{1, 128, 64, 4096} {
collName := common.GenRandomString(prefix, 6)
schema := entity.NewSchema().WithName(collName).WithField(int64Field).WithField(vecField).WithField(partitionKeyField)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, true)
// verify partitions num
partitions, err := mc.ListPartitions(ctx, client.NewListPartitionOption(collName))
require.Len(t, partitions, int(numPartition))
common.CheckErr(t, err, true)
}
}
func TestCreateCollectionDynamicSchema(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
pkField := entity.NewField().WithName(common.DefaultVarcharFieldName).WithDataType(entity.FieldTypeVarChar).WithIsPrimaryKey(true).WithMaxLength(common.MaxLength)
schema := entity.NewSchema().WithName(collName).WithField(pkField).WithField(vecField).WithDynamicFieldEnabled(true)
// pk field with name
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, true)
// verify field name
has, err := mc.HasCollection(ctx, client.NewHasCollectionOption(schema.CollectionName))
common.CheckErr(t, err, true)
require.True(t, has)
coll, err := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(schema.CollectionName))
common.CheckErr(t, err, true)
require.True(t, coll.Schema.EnableDynamicField)
// insert dynamic
columnOption := *hp.TNewDataOption()
varcharColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeVarChar, columnOption)
vecColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeFloatVector, columnOption)
dynamicData := hp.GenDynamicColumnData(0, common.DefaultNb)
_, err = mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, varcharColumn, vecColumn).WithColumns(dynamicData...))
common.CheckErr(t, err, true)
}
func TestCreateCollectionDynamic(t *testing.T) {
t.Skip("waiting for dynamicField alignment")
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
pkField := entity.NewField().WithName(common.DefaultVarcharFieldName).WithDataType(entity.FieldTypeVarChar).WithIsPrimaryKey(true).WithMaxLength(common.MaxLength)
schema := entity.NewSchema().WithName(collName).WithField(pkField).WithField(vecField)
// pk field with name
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema).WithDynamicSchema(true))
common.CheckErr(t, err, true)
// verify field name
has, err := mc.HasCollection(ctx, client.NewHasCollectionOption(schema.CollectionName))
common.CheckErr(t, err, true)
require.True(t, has)
coll, err := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(schema.CollectionName))
log.Info("collection dynamic", zap.Bool("collectionSchema", coll.Schema.EnableDynamicField))
common.CheckErr(t, err, true)
// require.True(t, coll.Schema.Fields[0].IsDynamic)
// insert dynamic
columnOption := *hp.TNewDataOption()
varcharColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeVarChar, columnOption)
vecColumn := hp.GenColumnData(common.DefaultNb, entity.FieldTypeFloatVector, columnOption)
dynamicData := hp.GenDynamicColumnData(0, common.DefaultNb)
_, err = mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, varcharColumn, vecColumn).WithColumns(dynamicData...))
common.CheckErr(t, err, false, "field dynamicNumber does not exist")
}
func TestCreateCollectionAllFields(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
schema := entity.NewSchema().WithName(collName)
// gen all fields except sparse vector
fields := hp.FieldsFactory{}.GenFieldsForCollection(hp.AllFields, hp.TNewFieldOptions())
for _, field := range fields {
schema.WithField(field)
}
// pk field with name
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, true)
// verify field name
has, err := mc.HasCollection(ctx, client.NewHasCollectionOption(schema.CollectionName))
common.CheckErr(t, err, true)
require.True(t, has)
}
func TestCreateCollectionSparseVector(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
sparseVecField := entity.NewField().WithName(common.DefaultSparseVecFieldName).WithDataType(entity.FieldTypeSparseVector)
pkField := entity.NewField().WithName(common.DefaultVarcharFieldName).WithDataType(entity.FieldTypeVarChar).WithIsPrimaryKey(true).WithMaxLength(common.MaxLength)
schema := entity.NewSchema().WithName(collName).WithField(pkField).WithField(sparseVecField)
// pk field with name
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema).WithDynamicSchema(true))
common.CheckErr(t, err, true)
// verify field name
has, err := mc.HasCollection(ctx, client.NewHasCollectionOption(schema.CollectionName))
common.CheckErr(t, err, true)
require.True(t, has)
}
func TestCreateCollectionWithValidFieldName(t *testing.T) {
t.Parallel()
// connect
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// create collection with valid field name
for _, name := range common.GenValidNames() {
collName := common.GenRandomString(prefix, 6)
// pk field with name
pkField := entity.NewField().WithName(name).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
schema := entity.NewSchema().WithName(collName).WithField(pkField).WithField(vecField)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, true)
// verify field name
coll, err := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(collName))
common.CheckErr(t, err, true)
require.Equal(t, name, coll.Schema.Fields[0].Name)
}
}
func genDefaultSchema() *entity.Schema {
int64Pk := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
varchar := entity.NewField().WithName(common.DefaultVarcharFieldName).WithDataType(entity.FieldTypeVarChar).WithMaxLength(common.TestMaxLen)
floatVec := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
binaryVec := entity.NewField().WithName(common.DefaultBinaryVecFieldName).WithDataType(entity.FieldTypeBinaryVector).WithDim(common.DefaultDim)
schema := entity.NewSchema().WithField(int64Pk).WithField(varchar).WithField(floatVec).WithField(binaryVec)
return schema
}
// create collection with valid name
func TestCreateCollectionWithValidName(t *testing.T) {
t.Parallel()
// connect
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
for _, name := range common.GenValidNames() {
schema := genDefaultSchema().WithName(name)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(name, schema))
common.CheckErr(t, err, true)
collections, err := mc.ListCollections(ctx, client.NewListCollectionOption())
common.CheckErr(t, err, true)
require.Contains(t, collections, name)
err = mc.DropCollection(ctx, client.NewDropCollectionOption(name))
common.CheckErr(t, err, true)
}
}
// create collection with invalid field name
func TestCreateCollectionWithInvalidFieldName(t *testing.T) {
t.Parallel()
// connect
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout*2)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// create collection with invalid field name
for _, invalidName := range common.GenInvalidNames() {
log.Debug("TestCreateCollectionWithInvalidFieldName", zap.String("fieldName", invalidName))
pkField := entity.NewField().WithName(invalidName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
vecField := entity.NewField().WithName("vec").WithDataType(entity.FieldTypeFloatVector).WithDim(128)
schema := entity.NewSchema().WithName("aaa").WithField(pkField).WithField(vecField)
collOpt := client.NewCreateCollectionOption("aaa", schema)
err := mc.CreateCollection(ctx, collOpt)
common.CheckErr(t, err, false, "field name should not be empty",
"The first character of a field name must be an underscore or letter",
"Field name cannot only contain numbers, letters, and underscores",
"The length of a field name must be less than 255 characters",
"Field name can only contain numbers, letters, and underscores")
}
}
// create collection with invalid collection name: invalid str, schemaName isn't equal to collectionName, schema name is empty
func TestCreateCollectionWithInvalidCollectionName(t *testing.T) {
t.Parallel()
// connect
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// create collection and schema no name
schema := genDefaultSchema()
err2 := mc.CreateCollection(ctx, client.NewCreateCollectionOption("", schema))
common.CheckErr(t, err2, false, "collection name should not be empty")
// create collection with invalid schema name
for _, invalidName := range common.GenInvalidNames() {
log.Debug("TestCreateCollectionWithInvalidCollectionName", zap.String("collectionName", invalidName))
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(invalidName, schema))
common.CheckErr(t, err, false, "collection name should not be empty",
"the first character of a collection name must be an underscore or letter",
"collection name can only contain numbers, letters and underscores",
fmt.Sprintf("the length of a collection name must be less than %d characters", common.MaxCollectionNameLen))
}
}
// create collection missing pk field or vector field
func TestCreateCollectionInvalidFields(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
type invalidFieldsStruct struct {
fields []*entity.Field
errMsg string
}
pkField := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
pkField2 := entity.NewField().WithName("pk").WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
varcharField := entity.NewField().WithName(common.DefaultVarcharFieldName).WithDataType(entity.FieldTypeVarChar)
stringField := entity.NewField().WithName("str").WithDataType(entity.FieldTypeString)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
noneField := entity.NewField().WithName("none").WithDataType(entity.FieldTypeNone)
invalidFields := []invalidFieldsStruct{
{fields: []*entity.Field{pkField}, errMsg: "schema does not contain vector field"},
{fields: []*entity.Field{vecField}, errMsg: "primary key is not specified"},
{fields: []*entity.Field{pkField, pkField2, vecField}, errMsg: "there are more than one primary key"},
{fields: []*entity.Field{pkField, vecField, noneField}, errMsg: "data type None is not valid"},
{fields: []*entity.Field{pkField, vecField, stringField}, errMsg: "string data type not supported yet, please use VarChar type instead"},
{fields: []*entity.Field{pkField, vecField, varcharField}, errMsg: "type param(max_length) should be specified for the field"},
}
collName := common.GenRandomString(prefix, 6)
for _, invalidField := range invalidFields {
schema := entity.NewSchema().WithName(collName)
for _, field := range invalidField.fields {
schema.WithField(field)
}
collOpt := client.NewCreateCollectionOption(collName, schema)
err := mc.CreateCollection(ctx, collOpt)
common.CheckErr(t, err, false, invalidField.errMsg)
}
}
// create autoID or not collection with non-int64 and non-varchar field
func TestCreateCollectionInvalidAutoPkField(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout*2)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// create collection with autoID true or not
collName := common.GenRandomString(prefix, 6)
for _, autoId := range [2]bool{true, false} {
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
// pk field type: non-int64 and non-varchar
for _, fieldType := range hp.GetInvalidPkFieldType() {
invalidPkField := entity.NewField().WithName("pk").WithDataType(fieldType).WithIsPrimaryKey(true)
schema := entity.NewSchema().WithName(collName).WithField(vecField).WithField(invalidPkField).WithAutoID(autoId)
errNonInt64Field := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, errNonInt64Field, false, "the data type of primary key should be Int64 or VarChar")
}
}
}
// test create collection with duplicate field name
func TestCreateCollectionDuplicateField(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// duplicate field
pkField := entity.NewField().WithName("id").WithDataType(entity.FieldTypeVarChar).WithIsPrimaryKey(true)
pkField2 := entity.NewField().WithName("id").WithDataType(entity.FieldTypeVarChar)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
// two vector fields have same name
collName := common.GenRandomString(prefix, 6)
schema := entity.NewSchema().WithName(collName).WithField(pkField).WithField(vecField).WithField(vecField)
errDupField := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, errDupField, false, "duplicated field name")
// two named "id" fields, one is pk field and other is scalar field
schema2 := entity.NewSchema().WithName(collName).WithField(pkField).WithField(pkField2).WithField(vecField).WithAutoID(true)
errDupField2 := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema2))
common.CheckErr(t, errDupField2, false, "duplicated field name")
}
// test create collection with partition key not supported field type
func TestCreateCollectionInvalidPartitionKeyType(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout*2)
mc := hp.CreateDefaultMilvusClient(ctx, t)
int64Field := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
collName := common.GenRandomString(prefix, 6)
for _, fieldType := range hp.GetInvalidPartitionKeyFieldType() {
log.Debug("TestCreateCollectionInvalidPartitionKeyType", zap.Any("partitionKeyFieldType", fieldType))
partitionKeyField := entity.NewField().WithName("parKey").WithDataType(fieldType).WithIsPartitionKey(true)
if fieldType == entity.FieldTypeArray {
partitionKeyField.WithElementType(entity.FieldTypeInt64)
}
schema := entity.NewSchema().WithName(collName).WithField(int64Field).WithField(vecField).WithField(partitionKeyField)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, "the data type of partition key should be Int64 or VarChar")
}
}
// partition key field cannot be primary field, d can only be one partition key field
func TestCreateCollectionPartitionKeyPk(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
int64Field := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true).WithIsPartitionKey(true)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
collName := common.GenRandomString(prefix, 6)
schema := entity.NewSchema().WithName(collName).WithField(int64Field).WithField(vecField)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, "the partition key field must not be primary field")
}
// can only be one partition key field
func TestCreateCollectionPartitionKeyNum(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
int64Field := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
collName := common.GenRandomString(prefix, 6)
pkField1 := entity.NewField().WithName("pk_1").WithDataType(entity.FieldTypeInt64).WithIsPartitionKey(true)
pkField2 := entity.NewField().WithName("pk_2").WithDataType(entity.FieldTypeVarChar).WithMaxLength(common.TestMaxLen).WithIsPartitionKey(true)
schema := entity.NewSchema().WithName(collName).WithField(int64Field).WithField(vecField).WithField(pkField1).WithField(pkField2)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, "there are more than one partition key")
}
func TestPartitionKeyInvalidNumPartition(t *testing.T) {
t.Skip("Waiting for num partition")
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// prepare field and schema
int64Field := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
pkField1 := entity.NewField().WithName("partitionKeyField").WithDataType(entity.FieldTypeInt64).WithIsPartitionKey(true)
// schema
collName := common.GenRandomString(prefix, 6)
schema := entity.NewSchema().WithName(collName).WithField(int64Field).WithField(vecField).WithField(pkField1)
invalidNumPartitionStruct := []struct {
numPartitions int64
errMsg string
}{
{common.MaxPartitionNum + 1, "exceeds max configuration (1024)"},
{-1, "the specified partitions should be greater than 0 if partition key is used"},
}
for _, npStruct := range invalidNumPartitionStruct {
// create collection with num partitions
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, npStruct.errMsg)
}
}
// test create collection with multi auto id
func TestCreateCollectionMultiAutoId(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
schema := entity.NewSchema().WithField(
entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true).WithIsAutoID(true)).WithField(
entity.NewField().WithName("dupInt").WithDataType(entity.FieldTypeInt64).WithIsAutoID(true)).WithField(
entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim),
).WithName(collName)
errMultiAuto := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, errMultiAuto, false, "only one field can speficy AutoID with true")
}
// test create collection with different autoId between pk field and schema
func TestCreateCollectionInconsistentAutoId(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
for _, autoId := range []bool{true, false} {
log.Debug("TestCreateCollectionInconsistentAutoId", zap.Bool("autoId", autoId))
collName := common.GenRandomString(prefix, 6)
// field and schema have opposite autoID
schema := entity.NewSchema().WithField(
entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true).WithIsAutoID(autoId)).WithField(
entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim),
).WithName(collName).WithAutoID(!autoId)
// create collection
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, true)
// describe collection
coll, err := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(collName))
common.CheckErr(t, err, true)
require.EqualValues(t, autoId, coll.Schema.AutoID)
for _, field := range coll.Schema.Fields {
if field.Name == common.DefaultInt64FieldName {
require.EqualValues(t, autoId, coll.Schema.Fields[0].AutoID)
}
}
}
}
// create collection with field or schema description
func TestCreateCollectionDescription(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// gen field with description
pkDesc := "This is pk field"
schemaDesc := "This is schema"
collName := common.GenRandomString(prefix, 6)
pkField := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true).WithDescription(pkDesc)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
schema := entity.NewSchema().WithName(collName).WithField(pkField).WithField(vecField).WithDescription(schemaDesc)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, true)
coll, err := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(collName))
common.CheckErr(t, err, true)
require.EqualValues(t, schemaDesc, coll.Schema.Description)
for _, field := range coll.Schema.Fields {
if field.Name == common.DefaultInt64FieldName {
require.Equal(t, pkDesc, field.Description)
} else {
require.Empty(t, field.Description)
}
}
}
// test invalid dim of binary field
func TestCreateBinaryCollectionInvalidDim(t *testing.T) {
t.Parallel()
type invalidDimStruct struct {
dim int64
errMsg string
}
invalidDims := []invalidDimStruct{
{dim: 10, errMsg: "should be multiple of 8"},
{dim: 0, errMsg: "should be in range 2 ~ 32768"},
{dim: 1, errMsg: "should be in range 2 ~ 32768"},
{dim: common.MaxDim * 9, errMsg: "binary vector dimension should be in range 2 ~ 262144"},
{dim: common.MaxDim*8 + 1, errMsg: "binary vector dimension should be multiple of 8"},
}
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
for _, invalidDim := range invalidDims {
log.Debug("TestCreateBinaryCollectionInvalidDim", zap.Int64("dim", invalidDim.dim))
collName := common.GenRandomString(prefix, 6)
// field and schema have opposite autoID
schema := entity.NewSchema().WithField(
entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)).WithField(
entity.NewField().WithName(common.DefaultBinaryVecFieldName).WithDataType(entity.FieldTypeBinaryVector).WithDim(invalidDim.dim),
).WithName(collName)
// create collection
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, invalidDim.errMsg)
}
}
// test invalid dim of float vector
func TestCreateFloatCollectionInvalidDim(t *testing.T) {
t.Parallel()
type invalidDimStruct struct {
dim string
errMsg string
}
invalidDims := []invalidDimStruct{
{dim: "0", errMsg: "should be in range 2 ~ 32768"},
{dim: "1", errMsg: "should be in range 2 ~ 32768"},
{dim: "", errMsg: "invalid syntax"},
{dim: "中文", errMsg: "invalid syntax"},
{dim: "%$#", errMsg: "invalid syntax"},
{dim: fmt.Sprintf("%d", common.MaxDim+1), errMsg: "float vector dimension should be in range 2 ~ 32768"},
}
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
for _, vecType := range []entity.FieldType{entity.FieldTypeFloatVector, entity.FieldTypeFloat16Vector, entity.FieldTypeBFloat16Vector} {
for _, invalidDim := range invalidDims {
log.Debug("TestCreateBinaryCollectionInvalidDim", zap.String("dim", invalidDim.dim))
collName := common.GenRandomString(prefix, 6)
schema := entity.NewSchema().WithField(
entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)).WithField(
entity.NewField().WithName("pk").WithDataType(vecType).WithTypeParams(entity.TypeParamDim, invalidDim.dim),
).WithName(collName)
// create collection
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, invalidDim.errMsg)
}
}
}
func TestCreateVectorWithoutDim(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
vecFieldName := "vec"
schema := entity.NewSchema().WithField(
entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)).WithField(
entity.NewField().WithName(vecFieldName).WithDataType(entity.FieldTypeFloatVector),
).WithName(collName)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, "dimension is not defined in field type params")
}
// specify dim for sparse vector -> error
func TestCreateCollectionSparseVectorWithDim(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
schema := entity.NewSchema().WithField(
entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)).WithField(
entity.NewField().WithName("sparse").WithDataType(entity.FieldTypeSparseVector).WithDim(common.DefaultDim),
).WithName(collName)
// create collection
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, "dim should not be specified for sparse vector field sparse")
}
func TestCreateArrayFieldInvalidCapacity(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
pkField := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
arrayField := entity.NewField().WithName(common.DefaultArrayFieldName).WithDataType(entity.FieldTypeArray).WithElementType(entity.FieldTypeFloat)
schema := entity.NewSchema().WithName(collName).WithField(pkField).WithField(vecField).WithField(arrayField)
// create collection
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, "type param(max_capacity) should be specified for array field")
// invalid Capacity
for _, invalidCapacity := range []int64{-1, 0, common.MaxCapacity + 1} {
arrayField.WithMaxCapacity(invalidCapacity)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, "the maximum capacity specified for a Array should be in (0, 4096]")
}
}
// test create collection varchar array with invalid max length
func TestCreateVarcharArrayInvalidLength(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
pkField := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
arrayVarcharField := entity.NewField().WithName(common.DefaultArrayFieldName).WithDataType(entity.FieldTypeArray).WithElementType(entity.FieldTypeVarChar).WithMaxCapacity(100)
schema := entity.NewSchema().WithName(collName).WithField(pkField).WithField(vecField).WithField(arrayVarcharField)
// create collection
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, "type param(max_length) should be specified for the field")
// invalid Capacity
for _, invalidLength := range []int64{-1, 0, common.MaxLength + 1} {
arrayVarcharField.WithMaxLength(invalidLength)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, "the maximum length specified for the field(array) should be in (0, 65535]")
}
}
// test create collection varchar array with invalid max length
func TestCreateVarcharInvalidLength(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
varcharField := entity.NewField().WithName(common.DefaultVarcharFieldName).WithDataType(entity.FieldTypeVarChar).WithIsPrimaryKey(true)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
schema := entity.NewSchema().WithName(collName).WithField(varcharField).WithField(vecField)
// create collection
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, "type param(max_length) should be specified for the field")
// invalid Capacity
for _, invalidLength := range []int64{-1, 0, common.MaxLength + 1} {
varcharField.WithMaxLength(invalidLength)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, "the maximum length specified for the field(varchar) should be in (0, 65535]")
}
}
func TestCreateArrayNotSupportedFieldType(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
// not supported ElementType: Array, Json, FloatVector, BinaryVector
pkField := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
for _, fieldType := range []entity.FieldType{entity.FieldTypeArray, entity.FieldTypeJSON, entity.FieldTypeBinaryVector, entity.FieldTypeFloatVector} {
field := entity.NewField().WithName("array").WithDataType(entity.FieldTypeArray).WithElementType(fieldType)
schema := entity.NewSchema().WithName(collName).WithField(pkField).WithField(vecField).WithField(field)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, fmt.Sprintf("element type %s is not supported", fieldType.Name()))
}
}
// the num of vector fields > default limit=4
func TestCreateMultiVectorExceed(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
pkField := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)
schema := entity.NewSchema().WithName(collName).WithField(pkField)
for i := 0; i < common.MaxVectorFieldNum+1; i++ {
vecField := entity.NewField().WithName(fmt.Sprintf("vec_%d", i)).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
schema.WithField(vecField)
}
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema))
common.CheckErr(t, err, false, fmt.Sprintf("maximum vector field's number should be limited to %d", common.MaxVectorFieldNum))
}
// func TestCreateCollection(t *testing.T) {}
func TestCreateCollectionInvalidShards(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
vecField := entity.NewField().WithName(common.DefaultFloatVecFieldName).WithDataType(entity.FieldTypeFloatVector).WithDim(common.DefaultDim)
int64Field := entity.NewField().WithName(common.DefaultInt64FieldName).WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true).WithIsAutoID(true)
for _, shard := range []int32{common.MaxShardNum + 1} {
// pk field with name
collName := common.GenRandomString(prefix, 6)
schema := entity.NewSchema().WithName(collName).WithField(int64Field).WithField(vecField)
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, schema).WithShardNum(shard))
common.CheckErr(t, err, false, fmt.Sprintf("maximum shards's number should be limited to %d", common.MaxShardNum))
}
}
func TestCreateCollectionInvalid(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
collName := common.GenRandomString(prefix, 6)
type mSchemaErr struct {
schema *entity.Schema
errMsg string
}
vecField := entity.NewField().WithName("vec").WithDataType(entity.FieldTypeFloatVector).WithDim(8)
mSchemaErrs := []mSchemaErr{
{schema: nil, errMsg: "schema does not contain vector field"},
{schema: entity.NewSchema().WithField(vecField), errMsg: "primary key is not specified"}, // no pk field
{schema: entity.NewSchema().WithField(vecField).WithField(entity.NewField()), errMsg: "primary key is not specified"},
{schema: entity.NewSchema().WithField(vecField).WithField(entity.NewField().WithIsPrimaryKey(true)), errMsg: "the data type of primary key should be Int64 or VarChar"},
{schema: entity.NewSchema().WithField(vecField).WithField(entity.NewField().WithIsPrimaryKey(true).WithDataType(entity.FieldTypeVarChar)), errMsg: "field name should not be empty"},
}
for _, mSchema := range mSchemaErrs {
err := mc.CreateCollection(ctx, client.NewCreateCollectionOption(collName, mSchema.schema))
common.CheckErr(t, err, false, mSchema.errMsg)
}
}
// test rename collection
func TestRenameCollection(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// create collection
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64Vec), hp.TNewFieldsOption(), hp.TNewSchemaOption())
// rename collection and verify
newName := common.GenRandomString("new", 6)
err := mc.RenameCollection(ctx, client.NewRenameCollectionOption(schema.CollectionName, newName))
common.CheckErr(t, err, true)
collections, _ := mc.ListCollections(ctx, client.NewListCollectionOption())
require.Contains(t, collections, newName)
require.NotContains(t, collections, schema.CollectionName)
_, err = mc.ListIndexes(ctx, client.NewListIndexOption(schema.CollectionName))
common.CheckErr(t, err, false, "collection not found")
schema.CollectionName = newName
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
prepare.FlushData(ctx, t, mc, schema.CollectionName)
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector)
resSearch, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors))
common.CheckErr(t, errSearch, true)
common.CheckSearchResult(t, resSearch, common.DefaultNq, common.DefaultLimit)
stats, err := mc.GetCollectionStats(ctx, client.NewGetCollectionStatsOption(newName))
common.CheckErr(t, err, true)
require.Equal(t, map[string]string{common.RowCount: strconv.Itoa(common.DefaultNb)}, stats)
}
// There are collections with the same name in different db. Rename one of them.
func TestRenameCollectionDb(t *testing.T) {
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// create collection
collectionName := common.GenRandomString("re", 6)
mc.CreateCollection(ctx, client.SimpleCreateCollectionOptions(collectionName, common.DefaultDim))
// create a database and use database
dbName := common.GenRandomString("db", 4)
mc.CreateDatabase(ctx, client.NewCreateDatabaseOption(dbName))
mc.UseDatabase(ctx, client.NewUseDatabaseOption(dbName))
mc.CreateCollection(ctx, client.SimpleCreateCollectionOptions(collectionName, common.DefaultDim))
// rename db collection rather than default db collection
newName := common.GenRandomString("new", 6)
err := mc.RenameCollection(ctx, client.NewRenameCollectionOption(collectionName, newName))
common.CheckErr(t, err, true)
collections, _ := mc.ListCollections(ctx, client.NewListCollectionOption())
require.Contains(t, collections, newName)
require.NotContains(t, collections, collectionName)
// verify default db collection
mc.UseDatabase(ctx, client.NewUseDatabaseOption(common.DefaultDb))
collectionsDefault, _ := mc.ListCollections(ctx, client.NewListCollectionOption())
require.Contains(t, collectionsDefault, collectionName)
require.NotContains(t, collectionsDefault, newName)
}
func TestRenameCollectionInvalidName(t *testing.T) {
// connect
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout*2)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// create collection
collectionName := common.GenRandomString("re", 6)
mc.CreateCollection(ctx, client.SimpleCreateCollectionOptions(collectionName, common.DefaultDim))
// rename collection with invalid name
for _, invalidName := range common.GenInvalidNames() {
log.Debug("TestCreateCollectionWithInvalidFieldName", zap.String("fieldName", invalidName))
err := mc.RenameCollection(ctx, client.NewRenameCollectionOption(collectionName, invalidName))
common.CheckErr(t, err, false, "collection name should not be empty",
"the first character of a collection name must be an underscore or letter",
"collection name can only contain numbers, letters and underscores",
"the length of a collection name must be less than 255 characters",
"collection name can only contain numbers, letters, and underscores")
}
}
func TestRenameCollectionAdvanced(t *testing.T) {
// connect
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout*2)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// create 2 collections
name1 := common.GenRandomString("name1", 6)
name2 := common.GenRandomString("name2", 6)
mc.CreateCollection(ctx, client.SimpleCreateCollectionOptions(name1, common.DefaultDim))
mc.CreateCollection(ctx, client.SimpleCreateCollectionOptions(name2, common.DefaultDim))
// rename: old name same with new name
err := mc.RenameCollection(ctx, client.NewRenameCollectionOption(name1, name1))
common.CheckErr(t, err, false, "duplicated new collection name")
// rename to a existed name
err = mc.RenameCollection(ctx, client.NewRenameCollectionOption(name1, name2))
common.CheckErr(t, err, false, "duplicated new collection name")
// rename a not existed collection
err = mc.RenameCollection(ctx, client.NewRenameCollectionOption(common.GenRandomString("a", 2), common.GenRandomString("b", 2)))
common.CheckErr(t, err, false, "collection not found")
}
// alter collection ttl property
func TestCollectionPropertyTtl(t *testing.T) {
// test collection property ttl
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// create collection
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64Vec), hp.TNewFieldsOption(), hp.TNewSchemaOption())
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
prepare.FlushData(ctx, t, mc, schema.CollectionName)
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
res, _ := mc.Query(ctx, client.NewQueryOption(schema.CollectionName).WithFilter("").WithOutputFields(common.QueryCountFieldName))
countBefore, _ := res.GetColumn(common.QueryCountFieldName).GetAsInt64(0)
require.EqualValues(t, common.DefaultNb, countBefore)
err := mc.AlterCollectionProperties(ctx, client.NewAlterCollectionPropertiesOption(schema.CollectionName).WithProperty(common.CollectionTTLSeconds, 2))
common.CheckErr(t, err, true)
coll, _ := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(schema.CollectionName))
require.Subset(t, coll.Properties, map[string]string{common.CollectionTTLSeconds: "2"})
time.Sleep(5 * time.Second)
res, _ = mc.Query(ctx, client.NewQueryOption(schema.CollectionName).WithFilter("").WithOutputFields(common.QueryCountFieldName))
countAfter, _ := res.GetColumn(common.QueryCountFieldName).GetAsInt64(0)
require.Contains(t, []int64{0, int64(common.DefaultNb)}, countAfter)
err = mc.DropCollectionProperties(ctx, client.NewDropCollectionPropertiesOption(schema.CollectionName, common.CollectionTTLSeconds))
common.CheckErr(t, err, true)
coll, _ = mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(schema.CollectionName))
require.Equal(t, 1, len(coll.Properties))
}
// create collection with property -> alter property -> writing and reading
func TestCollectionWithPropertyAlterMmap(t *testing.T) {
// test collection property mmap
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// create collection
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64Vec),
hp.TNewFieldsOption(), hp.TNewSchemaOption(), hp.TWithProperties(map[string]any{common.MmapEnabled: false}))
coll, _ := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(schema.CollectionName))
require.Subset(t, coll.Properties, map[string]string{common.MmapEnabled: "false"})
log.Info("TestCollectionPropertyMmap.DescribeCollection", zap.Any("properties", coll.Properties))
// alter properties
err := mc.AlterCollectionProperties(ctx, client.NewAlterCollectionPropertiesOption(schema.CollectionName).WithProperty(common.MmapEnabled, true))
common.CheckErr(t, err, true)
coll, _ = mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(schema.CollectionName))
require.Subset(t, coll.Properties, map[string]string{common.MmapEnabled: "true"})
// writing and reading
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
prepare.FlushData(ctx, t, mc, schema.CollectionName)
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
res, _ := mc.Query(ctx, client.NewQueryOption(schema.CollectionName).WithFilter("").WithOutputFields(common.QueryCountFieldName))
countBefore, _ := res.GetColumn(common.QueryCountFieldName).GetAsInt64(0)
require.EqualValues(t, common.DefaultNb, countBefore)
}
func TestCollectionPropertyMmap(t *testing.T) {
// test collection property mmap
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// create collection
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64Vec),
hp.TNewFieldsOption(), hp.TNewSchemaOption())
// alter properties
err := mc.AlterCollectionProperties(ctx, client.NewAlterCollectionPropertiesOption(schema.CollectionName).WithProperty(common.MmapEnabled, true))
common.CheckErr(t, err, true)
coll, _ := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(schema.CollectionName))
require.Subset(t, coll.Properties, map[string]string{common.MmapEnabled: "true"})
// writing and reading
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
prepare.FlushData(ctx, t, mc, schema.CollectionName)
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
res, _ := mc.Query(ctx, client.NewQueryOption(schema.CollectionName).WithFilter("").WithOutputFields(common.QueryCountFieldName))
countBefore, _ := res.GetColumn(common.QueryCountFieldName).GetAsInt64(0)
require.EqualValues(t, common.DefaultNb, countBefore)
err = mc.DropCollectionProperties(ctx, client.NewDropCollectionPropertiesOption(schema.CollectionName, common.MmapEnabled))
common.CheckErr(t, err, false, "can not delete mmap properties if collection loaded")
// release collection and drop property
mc.ReleaseCollection(ctx, client.NewReleaseCollectionOption(schema.CollectionName))
err = mc.DropCollectionProperties(ctx, client.NewDropCollectionPropertiesOption(schema.CollectionName, common.MmapEnabled))
common.CheckErr(t, err, true)
coll, _ = mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(schema.CollectionName))
require.Equal(t, 1, len(coll.Properties))
}
func TestCollectionFakeProperties(t *testing.T) {
// test collection property mmap
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
mc := hp.CreateDefaultMilvusClient(ctx, t)
// create collection with fake property
collName := common.GenRandomString("alter", 6)
err := mc.CreateCollection(ctx, client.SimpleCreateCollectionOptions(collName, common.DefaultDim).WithProperty("1", "bbb"))
common.CheckErr(t, err, true)
coll, _ := mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(collName))
require.Subset(t, coll.Properties, map[string]string{"1": "bbb"})
// alter collection with fake kjproperty
err = mc.AlterCollectionProperties(ctx, client.NewAlterCollectionPropertiesOption(collName).WithProperty("2", 1))
common.CheckErr(t, err, true)
coll, _ = mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(collName))
require.Subset(t, coll.Properties, map[string]string{"1": "bbb", "2": "1"})
err = mc.DropCollectionProperties(ctx, client.NewDropCollectionPropertiesOption(collName, "ccc"))
common.CheckErr(t, err, true)
coll, _ = mc.DescribeCollection(ctx, client.NewDescribeCollectionOption(collName))
require.Subset(t, coll.Properties, map[string]string{"1": "bbb", "2": "1"})
}