From f21ccdd5380f36ba8c8cec1aa41f53a6846032a8 Mon Sep 17 00:00:00 2001 From: ThreadDao Date: Thu, 14 Mar 2024 20:40:19 +0800 Subject: [PATCH] Add test cases for fp16, bf16, hybrid search Signed-off-by: ThreadDao --- client/ann_request.go | 3 +- examples/multivectors/main.go | 4 +- test/base/milvus_client.go | 11 + test/common/response_check.go | 18 +- test/common/utils.go | 293 ++++++++++++++++++++----- test/testcases/collection_test.go | 19 ++ test/testcases/compact_test.go | 22 +- test/testcases/delete_test.go | 46 +++- test/testcases/groupby_search_test.go | 91 ++++++-- test/testcases/hybrid_search_test.go | 303 ++++++++++++++++++++++++++ test/testcases/index_test.go | 134 +++++++++++- test/testcases/insert_test.go | 31 ++- test/testcases/load_release_test.go | 36 +++ test/testcases/main_test.go | 46 +++- test/testcases/option.go | 6 +- test/testcases/partition_key_test.go | 13 +- test/testcases/query_test.go | 39 +++- test/testcases/search_test.go | 131 +++++++++-- test/testcases/upsert_test.go | 13 +- 19 files changed, 1086 insertions(+), 173 deletions(-) create mode 100644 test/testcases/hybrid_search_test.go diff --git a/client/ann_request.go b/client/ann_request.go index df88aa479..26f7b61a8 100644 --- a/client/ann_request.go +++ b/client/ann_request.go @@ -20,11 +20,12 @@ type ANNSearchRequest struct { limit int } -func NewANNSearchRequest(fieldName string, metricsType entity.MetricType, vectors []entity.Vector, searchParam entity.SearchParam, limit int, options ...SearchQueryOptionFunc) *ANNSearchRequest { +func NewANNSearchRequest(fieldName string, metricsType entity.MetricType, expr string, vectors []entity.Vector, searchParam entity.SearchParam, limit int, options ...SearchQueryOptionFunc) *ANNSearchRequest { return &ANNSearchRequest{ fieldName: fieldName, vectors: vectors, metricType: metricsType, + expr: expr, searchParam: searchParam, limit: limit, options: options, diff --git a/examples/multivectors/main.go b/examples/multivectors/main.go index 05e7add18..78c8641bc 100644 --- a/examples/multivectors/main.go +++ b/examples/multivectors/main.go @@ -139,8 +139,8 @@ func main() { result, err = c.HybridSearch(ctx, collectionName, nil, topK, []string{keyCol, embeddingCol1, embeddingCol2}, client.NewRRFReranker(), []*client.ANNSearchRequest{ - client.NewANNSearchRequest(embeddingCol1, entity.L2, vec2search1, sp, topK), - client.NewANNSearchRequest(embeddingCol2, entity.L2, vec2search2, sp, topK), + client.NewANNSearchRequest(embeddingCol1, entity.L2, "", vec2search1, sp, topK), + client.NewANNSearchRequest(embeddingCol2, entity.L2, "", vec2search2, sp, topK), }) if err != nil { log.Fatalf("failed to search collection, err: %v", err) diff --git a/test/base/milvus_client.go b/test/base/milvus_client.go index 116ca956f..a5835ae01 100644 --- a/test/base/milvus_client.go +++ b/test/base/milvus_client.go @@ -410,6 +410,17 @@ func (mc *MilvusClient) Search(ctx context.Context, collName string, partitions return searchResult, err } +func (mc *MilvusClient) HybridSearch(ctx context.Context, collName string, partitions []string, limit int, outputFields []string, + reranker client.Reranker, subRequests []*client.ANNSearchRequest) ([]client.SearchResult, error) { + funcName := "HybridSearch" + preRequest(funcName, ctx, collName, partitions, limit, outputFields, reranker, subRequests) + + searchResult, err := mc.mClient.HybridSearch(ctx, collName, partitions, limit, outputFields, reranker, subRequests) + postResponse(funcName, err, searchResult) + + return searchResult, err +} + // QueryByPks query from collection func (mc *MilvusClient) QueryByPks(ctx context.Context, collName string, partitions []string, ids entity.Column, outputFields []string, opts ...client.SearchQueryOptionFunc) (client.ResultSet, error) { diff --git a/test/common/response_check.go b/test/common/response_check.go index 64027bd26..1b7a36a4b 100644 --- a/test/common/response_check.go +++ b/test/common/response_check.go @@ -3,6 +3,7 @@ package common import ( "fmt" "log" + "strings" "testing" "github.com/milvus-io/milvus-sdk-go/v2/client" @@ -15,11 +16,22 @@ func CheckErr(t *testing.T, actualErr error, expErrNil bool, expErrorMsg ...stri if expErrNil { require.NoError(t, actualErr) } else { - if len(expErrorMsg) == 0 { + switch len(expErrorMsg) { + case 0: log.Fatal("expect error message should not be empty") + case 1: + require.ErrorContains(t, actualErr, expErrorMsg[0]) + default: + contains := false + for i := 0; i < len(expErrorMsg); i++ { + if strings.Contains(actualErr.Error(), expErrorMsg[i]) { + contains = true + } + } + if !contains { + t.FailNow() + } } - require.NotEmpty(t, expErrorMsg[0]) - require.ErrorContains(t, actualErr, expErrorMsg[0]) } } diff --git a/test/common/utils.go b/test/common/utils.go index aa6fc949f..c5ed6f9e7 100644 --- a/test/common/utils.go +++ b/test/common/utils.go @@ -2,6 +2,7 @@ package common import ( "bytes" + "encoding/binary" "encoding/json" "fmt" "log" @@ -9,40 +10,48 @@ import ( "strconv" "strings" "time" + "unsafe" "github.com/milvus-io/milvus-sdk-go/v2/entity" ) // const default value for test const ( - DefaultIntFieldName = "int64" - DefaultFloatFieldName = "float" - DefaultVarcharFieldName = "varchar" - DefaultJSONFieldName = "json" - DefaultArrayFieldName = "array" - DefaultFloatVecFieldName = "floatVec" - DefaultBinaryVecFieldName = "binaryVec" - DefaultDynamicNumberField = "dynamicNumber" - DefaultDynamicStringField = "dynamicString" - DefaultDynamicBoolField = "dynamicBool" - DefaultDynamicListField = "dynamicList" - DefaultBoolArrayField = "boolArray" - DefaultInt8ArrayField = "int8Array" - DefaultInt16ArrayField = "int16Array" - DefaultInt32ArrayField = "int32Array" - DefaultInt64ArrayField = "int64Array" - DefaultFloatArrayField = "floatArray" - DefaultDoubleArrayField = "doubleArray" - DefaultVarcharArrayField = "varcharArray" - RowCount = "row_count" - DefaultTimeout = 120 - DefaultDim = int64(128) - DefaultShards = int32(2) - DefaultNb = 3000 - DefaultNq = 5 - DefaultTopK = 10 - TestCapacity = 100 // default array field capacity - TestMaxLen = 100 // default varchar field max length + DefaultIntFieldName = "int64" + DefaultInt8FieldName = "int8" + DefaultInt16FieldName = "int16" + DefaultInt32FieldName = "int32" + DefaultBoolFieldName = "bool" + DefaultFloatFieldName = "float" + DefaultDoubleFieldName = "double" + DefaultVarcharFieldName = "varchar" + DefaultJSONFieldName = "json" + DefaultArrayFieldName = "array" + DefaultFloatVecFieldName = "floatVec" + DefaultBinaryVecFieldName = "binaryVec" + DefaultFloat16VecFieldName = "fp16Vec" + DefaultBFloat16VecFieldName = "bf16Vec" + DefaultDynamicNumberField = "dynamicNumber" + DefaultDynamicStringField = "dynamicString" + DefaultDynamicBoolField = "dynamicBool" + DefaultDynamicListField = "dynamicList" + DefaultBoolArrayField = "boolArray" + DefaultInt8ArrayField = "int8Array" + DefaultInt16ArrayField = "int16Array" + DefaultInt32ArrayField = "int32Array" + DefaultInt64ArrayField = "int64Array" + DefaultFloatArrayField = "floatArray" + DefaultDoubleArrayField = "doubleArray" + DefaultVarcharArrayField = "varcharArray" + RowCount = "row_count" + DefaultTimeout = 120 + DefaultDim = int64(128) + DefaultShards = int32(2) + DefaultNb = 3000 + DefaultNq = 5 + DefaultTopK = 10 + TestCapacity = 100 // default array field capacity + TestMaxLen = 100 // default varchar field max length ) // const default value from milvus @@ -62,6 +71,9 @@ const ( DefaultRgCapacity = 1000000 RetentionDuration = 40 // common.retentionDuration MaxCapacity = 4096 // max array capacity + DefaultPartitionNum = 64 // default num_partitions + MaxTopK = 16384 + MaxVectorFieldNum = 4 ) var IndexStateValue = map[string]int32{ @@ -95,6 +107,37 @@ var AllArrayFieldsName = []string{ DefaultVarcharArrayField, } +var AllVectorsFieldsName = []string{ + DefaultFloatVecFieldName, + DefaultBinaryVecFieldName, + DefaultFloat16VecFieldName, + DefaultBFloat16VecFieldName, +} + +// return all fields name +func GetAllFieldsName(enableDynamicField bool, onlyScalar bool) []string { + allFieldName := []string{ + DefaultIntFieldName, + DefaultBoolFieldName, + DefaultInt8FieldName, + DefaultInt16FieldName, + DefaultInt32FieldName, + DefaultFloatFieldName, + DefaultDoubleFieldName, + DefaultVarcharFieldName, + DefaultJSONFieldName, + } + allFieldName = append(allFieldName, AllVectorsFieldsName...) + if enableDynamicField { + allFieldName = append(allFieldName, DefaultDynamicFieldName) + } + if onlyScalar { + return allFieldName + } + allFieldName = append(allFieldName, AllArrayFieldsName...) + return allFieldName +} + var r *rand.Rand func init() { @@ -141,6 +184,30 @@ func GenFloatVector(dim int64) []float32 { return vector } +func GenFloat16Vector(dim int64) []byte { + vector := make([]byte, 0, dim) + fp16Data := make([]byte, 2) + for i := 0; i < int(dim); i++ { + f := rand.Float32() + u32 := *(*uint32)(unsafe.Pointer(&f)) + binary.LittleEndian.PutUint16(fp16Data, uint16(u32>>16)) + vector = append(vector, fp16Data...) + } + return vector +} + +func GenBFloat16Vector(dim int64) []byte { + vector := make([]byte, 0, dim) + bf16Data := make([]byte, 2) + for i := 0; i < int(dim); i++ { + f := rand.Float32() + u32 := *(*uint32)(unsafe.Pointer(&f)) + binary.LittleEndian.PutUint16(bf16Data, uint16(u32>>16)) + vector = append(vector, bf16Data...) + } + return vector +} + func GenBinaryVector(dim int64) []byte { vector := make([]byte, dim/8) rand.Read(vector) @@ -206,16 +273,19 @@ func GenAllArrayFieldsWithCapacity(capacity int64) []*entity.Field { // GenAllFields gen fields with all scala field types func GenAllFields() []*entity.Field { allFields := []*entity.Field{ - GenField("int64", entity.FieldTypeInt64, WithIsPrimaryKey(true)), // int64 - GenField("bool", entity.FieldTypeBool), // bool - GenField("int8", entity.FieldTypeInt8), // int8 - GenField("int16", entity.FieldTypeInt16), // int16 - GenField("int32", entity.FieldTypeInt32), // int32 - GenField("float", entity.FieldTypeFloat), // float - GenField("double", entity.FieldTypeDouble), // double - GenField("varchar", entity.FieldTypeVarChar, WithMaxLength(MaxLength)), // varchar - GenField("json", entity.FieldTypeJSON), // json - GenField("floatVec", entity.FieldTypeFloatVector, WithDim(DefaultDim)), // float vector + GenField(DefaultIntFieldName, entity.FieldTypeInt64, WithIsPrimaryKey(true)), // int64 + GenField(DefaultBoolFieldName, entity.FieldTypeBool), // bool + GenField(DefaultInt8FieldName, entity.FieldTypeInt8), // int8 + GenField(DefaultInt16FieldName, entity.FieldTypeInt16), // int16 + GenField(DefaultInt32FieldName, entity.FieldTypeInt32), // int32 + GenField(DefaultFloatFieldName, entity.FieldTypeFloat), // float + GenField(DefaultDoubleFieldName, entity.FieldTypeDouble), // double + GenField(DefaultVarcharFieldName, entity.FieldTypeVarChar, WithMaxLength(MaxLength)), // varchar + GenField(DefaultJSONFieldName, entity.FieldTypeJSON), // json + GenField(DefaultFloatVecFieldName, entity.FieldTypeFloatVector, WithDim(DefaultDim)), // float vector + GenField(DefaultFloat16VecFieldName, entity.FieldTypeFloat16Vector, WithDim(DefaultDim)), // float16 vector + GenField(DefaultBFloat16VecFieldName, entity.FieldTypeBFloat16Vector, WithDim(DefaultDim)), // bf16 vector + GenField(DefaultBinaryVecFieldName, entity.FieldTypeBinaryVector, WithDim(DefaultDim)), // binary vector } allFields = append(allFields, GenAllArrayFields()...) return allFields @@ -312,6 +382,20 @@ func GenColumnData(start int, nb int, fieldType entity.FieldType, fieldName stri binaryVectors = append(binaryVectors, vec) } return entity.NewColumnBinaryVector(fieldName, int(opt.dim), binaryVectors) + case entity.FieldTypeFloat16Vector: + fp16Vectors := make([][]byte, 0, nb) + for i := start; i < start+nb; i++ { + vec := GenFloat16Vector(opt.dim) + fp16Vectors = append(fp16Vectors, vec) + } + return entity.NewColumnFloat16Vector(fieldName, int(opt.dim), fp16Vectors) + case entity.FieldTypeBFloat16Vector: + bf16Vectors := make([][]byte, 0, nb) + for i := start; i < start+nb; i++ { + vec := GenBFloat16Vector(opt.dim) + bf16Vectors = append(bf16Vectors, vec) + } + return entity.NewColumnBFloat16Vector(fieldName, int(opt.dim), bf16Vectors) default: return nil } @@ -424,6 +508,7 @@ func GenDefaultJSONData(columnName string, start int, nb int) *entity.ColumnJSON jsonValues := make([][]byte, 0, nb) var m interface{} for i := start; i < start+nb; i++ { + // kv value if i < (start+nb)/2 { if i%2 == 0 { m = JSONStruct{ @@ -439,6 +524,7 @@ func GenDefaultJSONData(columnName string, start int, nb int) *entity.ColumnJSON } } } else { + // int, float, string, list switch i % 4 { case 0: m = i @@ -492,23 +578,43 @@ func GenAllArrayData(start int, nb int, opts ...GenColumnDataOption) []entity.Co return data } -func GenAllFieldsData(start int, nb int, dim int64, opts ...GenColumnDataOption) []entity.Column { +func GenAllVectorsData(start int, nb int, dim int64, opts ...GenColumnDataOption) []entity.Column { opt := &genDataOpt{} for _, o := range opts { o(opt) } + // prepare data data := []entity.Column{ GenColumnData(start, nb, entity.FieldTypeInt64, "int64"), - GenColumnData(start, nb, entity.FieldTypeBool, "bool"), - GenColumnData(start, nb, entity.FieldTypeInt8, "int8"), - GenColumnData(start, nb, entity.FieldTypeInt16, "int16"), - GenColumnData(start, nb, entity.FieldTypeInt32, "int32"), - GenColumnData(start, nb, entity.FieldTypeFloat, "float"), - GenColumnData(start, nb, entity.FieldTypeDouble, "double"), - GenColumnData(start, nb, entity.FieldTypeVarChar, "varchar"), - GenDefaultJSONData("json", start, nb), GenColumnData(start, nb, entity.FieldTypeFloatVector, "floatVec", WithVectorDim(dim)), + GenColumnData(start, nb, entity.FieldTypeFloat16Vector, "fp16Vec", WithVectorDim(dim)), + GenColumnData(start, nb, entity.FieldTypeBFloat16Vector, "bf16Vec", WithVectorDim(dim)), + GenColumnData(start, nb, entity.FieldTypeBinaryVector, "binaryVec", WithVectorDim(dim)), + } + return data +} + +func GenAllFieldsData(start int, nb int, dim int64, opts ...GenColumnDataOption) []entity.Column { + opt := &genDataOpt{} + for _, o := range opts { + o(opt) + } + // prepare data + data := []entity.Column{ + GenColumnData(start, nb, entity.FieldTypeInt64, DefaultIntFieldName), + GenColumnData(start, nb, entity.FieldTypeBool, DefaultBoolFieldName), + GenColumnData(start, nb, entity.FieldTypeInt8, DefaultInt8FieldName), + GenColumnData(start, nb, entity.FieldTypeInt16, DefaultInt16FieldName), + GenColumnData(start, nb, entity.FieldTypeInt32, DefaultInt32FieldName), + GenColumnData(start, nb, entity.FieldTypeFloat, DefaultFloatFieldName), + GenColumnData(start, nb, entity.FieldTypeDouble, DefaultDoubleFieldName), + GenColumnData(start, nb, entity.FieldTypeVarChar, DefaultVarcharFieldName), + GenDefaultJSONData(DefaultJSONFieldName, start, nb), + GenColumnData(start, nb, entity.FieldTypeFloatVector, DefaultFloatVecFieldName, WithVectorDim(dim)), + GenColumnData(start, nb, entity.FieldTypeFloat16Vector, DefaultFloat16VecFieldName, WithVectorDim(dim)), + GenColumnData(start, nb, entity.FieldTypeBFloat16Vector, DefaultBFloat16VecFieldName, WithVectorDim(dim)), + GenColumnData(start, nb, entity.FieldTypeBinaryVector, DefaultBinaryVecFieldName, WithVectorDim(dim)), } data = append(data, GenAllArrayData(start, nb, opts...)...) return data @@ -869,6 +975,57 @@ func GenDefaultArrayRows(start int, nb int, dim int64, enableDynamicField bool, return rows } +func GenAllVectorsRows(start int, nb int, dim int64, enableDynamicField bool) []interface{} { + rows := make([]interface{}, 0, nb) + type BaseRow struct { + Int64 int64 `json:"int64" milvus:"name:int64"` + FloatVec []float32 `json:"floatVec" milvus:"name:floatVec"` + Float16Vec []byte `json:"fp16Vec" milvus:"name:fp16Vec"` + BFloat16Vec []byte `json:"bf16Vec" milvus:"name:bf16Vec"` + BinaryVec []byte `json:"binaryVec" milvus:"name:binaryVec"` + } + + type DynamicRow struct { + Int64 int64 `json:"int64" milvus:"name:int64"` + FloatVec []float32 `json:"floatVec" milvus:"name:floatVec"` + Float16Vec []byte `json:"fp16Vec" milvus:"name:fp16Vec"` + BFloat16Vec []byte `json:"bf16Vec" milvus:"name:bf16Vec"` + BinaryVec []byte `json:"binaryVec" milvus:"name:binaryVec"` + Dynamic Dynamic `json:"dynamic" milvus:"name:dynamic"` + } + + for i := start; i < start+nb; i++ { + baseRow := BaseRow{ + Int64: int64(i), + FloatVec: GenFloatVector(dim), + Float16Vec: GenFloat16Vector(dim), + BFloat16Vec: GenBFloat16Vector(dim), + BinaryVec: GenBinaryVector(dim), + } + // json and dynamic field + dynamicJSON := Dynamic{ + Number: int32(i), + String: strconv.Itoa(i), + Bool: i%2 == 0, + List: []int64{int64(i), int64(i + 1)}, + } + if enableDynamicField { + dynamicRow := DynamicRow{ + Int64: baseRow.Int64, + FloatVec: baseRow.FloatVec, + Float16Vec: baseRow.Float16Vec, + BFloat16Vec: baseRow.BFloat16Vec, + BinaryVec: baseRow.BinaryVec, + Dynamic: dynamicJSON, + } + rows = append(rows, dynamicRow) + } else { + rows = append(rows, &baseRow) + } + } + return rows +} + func GenAllFieldsRows(start int, nb int, dim int64, enableDynamicField bool, opts ...GenColumnDataOption) []interface{} { rows := make([]interface{}, 0, nb) @@ -884,6 +1041,9 @@ func GenAllFieldsRows(start int, nb int, dim int64, enableDynamicField bool, opt Varchar string `json:"varchar" milvus:"name:varchar"` JSON Dynamic `json:"json" milvus:"name:json"` FloatVec []float32 `json:"floatVec" milvus:"name:floatVec"` + Float16Vec []byte `json:"fp16Vec" milvus:"name:fp16Vec"` + BFloat16Vec []byte `json:"bf16Vec" milvus:"name:bf16Vec"` + BinaryVec []byte `json:"binaryVec" milvus:"name:binaryVec"` BoolArray []bool `json:"boolArray" milvus:"name:boolArray"` Int8Array []int8 `json:"int8Array" milvus:"name:int8Array"` Int16Array []int16 `json:"int16Array" milvus:"name:int16Array"` @@ -905,6 +1065,9 @@ func GenAllFieldsRows(start int, nb int, dim int64, enableDynamicField bool, opt Varchar string `json:"varchar" milvus:"name:varchar"` JSON Dynamic `json:"json" milvus:"name:json"` FloatVec []float32 `json:"floatVec" milvus:"name:floatVec"` + Float16Vec []byte `json:"fp16Vec" milvus:"name:fp16Vec"` + BFloat16Vec []byte `json:"bf16Vec" milvus:"name:bf16Vec"` + BinaryVec []byte `json:"binaryVec" milvus:"name:binaryVec"` BoolArray []bool `json:"boolArray" milvus:"name:boolArray"` Int8Array []int8 `json:"int8Array" milvus:"name:int8Array"` Int16Array []int16 `json:"int16Array" milvus:"name:int16Array"` @@ -928,6 +1091,9 @@ func GenAllFieldsRows(start int, nb int, dim int64, enableDynamicField bool, opt Double: float64(i), Varchar: strconv.Itoa(i), FloatVec: GenFloatVector(dim), + Float16Vec: GenFloat16Vector(dim), + BFloat16Vec: GenBFloat16Vector(dim), + BinaryVec: GenBinaryVector(dim), BoolArray: arrayRow.BoolArray, Int8Array: arrayRow.Int8Array, Int16Array: arrayRow.Int16Array, @@ -956,6 +1122,9 @@ func GenAllFieldsRows(start int, nb int, dim int64, enableDynamicField bool, opt Double: baseRow.Double, Varchar: baseRow.Varchar, FloatVec: baseRow.FloatVec, + Float16Vec: baseRow.Float16Vec, + BFloat16Vec: baseRow.BFloat16Vec, + BinaryVec: baseRow.BinaryVec, BoolArray: arrayRow.BoolArray, Int8Array: arrayRow.Int8Array, Int16Array: arrayRow.Int16Array, @@ -1067,8 +1236,9 @@ func GenAllFloatIndex() []entity.Index { idxIvfPq, _ := entity.NewIndexIvfPQ(metricType, nlist, 16, 8) idxHnsw, _ := entity.NewIndexHNSW(metricType, 8, 96) idxScann, _ := entity.NewIndexSCANN(metricType, 16, false) - idxDiskAnn, _ := entity.NewIndexDISKANN(metricType) - allFloatIndex = append(allFloatIndex, idxFlat, idxIvfFlat, idxIvfSq8, idxIvfPq, idxHnsw, idxScann, idxDiskAnn) + // TODO waiting for PR https://github.com/milvus-io/milvus/pull/30716 + //idxDiskAnn, _ := entity.NewIndexDISKANN(metricType) + allFloatIndex = append(allFloatIndex, idxFlat, idxIvfFlat, idxIvfSq8, idxIvfPq, idxHnsw, idxScann) } return allFloatIndex } @@ -1091,6 +1261,16 @@ func GenSearchVectors(nq int, dim int64, dataType entity.FieldType) []entity.Vec vector := GenBinaryVector(dim) vectors = append(vectors, entity.BinaryVector(vector)) } + case entity.FieldTypeFloat16Vector: + for i := 0; i < nq; i++ { + vector := GenFloat16Vector(dim) + vectors = append(vectors, entity.Float16Vector(vector)) + } + case entity.FieldTypeBFloat16Vector: + for i := 0; i < nq; i++ { + vector := GenBFloat16Vector(dim) + vectors = append(vectors, entity.BFloat16Vector(vector)) + } } return vectors } @@ -1103,13 +1283,14 @@ type InvalidExprStruct struct { } var InvalidExpressions = []InvalidExprStruct{ - {Expr: "id in [0]", ErrNil: true, ErrMsg: "fieldName(id) not found"}, // not exist field but no error - {Expr: "int64 in not [0]", ErrNil: false, ErrMsg: "cannot parse expression"}, // wrong term expr keyword - {Expr: "int64 < floatVec", ErrNil: false, ErrMsg: "not supported"}, // unsupported compare field - {Expr: "floatVec in [0]", ErrNil: false, ErrMsg: "cannot be casted to FloatVector"}, // value and field type mismatch - {Expr: fmt.Sprintf("%s == 1", DefaultJSONFieldName), ErrNil: true, ErrMsg: ""}, // hist empty - {Expr: fmt.Sprintf("%s like 'a%%' ", DefaultJSONFieldName), ErrNil: true, ErrMsg: ""}, // hist empty - {Expr: fmt.Sprintf("%s > 1", DefaultDynamicFieldName), ErrNil: true, ErrMsg: ""}, // hits empty + {Expr: "id in [0]", ErrNil: true, ErrMsg: "fieldName(id) not found"}, // not exist field but no error + {Expr: "int64 in not [0]", ErrNil: false, ErrMsg: "cannot parse expression"}, // wrong term expr keyword + {Expr: "int64 < floatVec", ErrNil: false, ErrMsg: "not supported"}, // unsupported compare field + {Expr: "floatVec in [0]", ErrNil: false, ErrMsg: "cannot be casted to FloatVector"}, // value and field type mismatch + {Expr: fmt.Sprintf("%s == 1", DefaultJSONFieldName), ErrNil: true, ErrMsg: ""}, // hist empty + {Expr: fmt.Sprintf("%s like 'a%%' ", DefaultJSONFieldName), ErrNil: true, ErrMsg: ""}, // hist empty + {Expr: fmt.Sprintf("%s like `a%%` ", DefaultJSONFieldName), ErrNil: false, ErrMsg: "cannot parse expression"}, // `` + {Expr: fmt.Sprintf("%s > 1", DefaultDynamicFieldName), ErrNil: true, ErrMsg: ""}, // hits empty {Expr: fmt.Sprintf("%s[\"dynamicList\"] == [2, 3]", DefaultDynamicFieldName), ErrNil: true, ErrMsg: ""}, {Expr: fmt.Sprintf("%s['a'] == [2, 3]", DefaultJSONFieldName), ErrNil: true, ErrMsg: ""}, // json field not exist {Expr: fmt.Sprintf("%s['number'] == [2, 3]", DefaultJSONFieldName), ErrNil: true, ErrMsg: ""}, // field exist but type not match diff --git a/test/testcases/collection_test.go b/test/testcases/collection_test.go index e8ccabbc6..0a592564c 100644 --- a/test/testcases/collection_test.go +++ b/test/testcases/collection_test.go @@ -610,6 +610,25 @@ func TestCreateCollectionAllFields(t *testing.T) { common.CheckErr(t, errCreateCollection, true) } +// the num of vector fields > default limit=4 +func TestCreateMultiVectorExceed(t *testing.T) { + ctx := createContext(t, time.Second*common.DefaultTimeout) + mc := createMilvusClient(ctx, t) + fields := []*entity.Field{ + common.GenField(common.DefaultIntFieldName, entity.FieldTypeInt64, common.WithIsPrimaryKey(true)), + } + for i := 0; i < common.MaxVectorFieldNum+1; i++ { + field := common.GenField(fmt.Sprintf("%s-%d", common.DefaultFloatVecFieldName, i), entity.FieldTypeFloatVector, common.WithDim(128)) + fields = append(fields, field) + } + collName := common.GenRandomString(6) + schema := common.GenSchema(collName, false, fields) + + // create collection + errCreateCollection := mc.CreateCollection(ctx, schema, common.DefaultShards) + common.CheckErr(t, errCreateCollection, false, "maximum vector field's number should be limited to 4") +} + // -- Get Collection Statistics -- func TestGetStaticsCollectionNotExisted(t *testing.T) { diff --git a/test/testcases/compact_test.go b/test/testcases/compact_test.go index fbfbbff55..47f0e9d8e 100644 --- a/test/testcases/compact_test.go +++ b/test/testcases/compact_test.go @@ -20,13 +20,15 @@ func TestCompact(t *testing.T) { mc := createMilvusClient(ctx, t) // create collection with 1 shard - collName := createDefaultCollection(ctx, t, mc, true, 1) - - // insert data - for i := 0; i < 2; i++ { - _, floatColumn, vecColumn := common.GenDefaultColumnData((i+1)*common.DefaultNb, common.DefaultNb, common.DefaultDim) - _, errInsert := mc.Insert(ctx, collName, common.DefaultPartition, floatColumn, vecColumn) - common.CheckErr(t, errInsert, true) + cp := CollectionParams{CollectionFieldsType: AllFields, AutoID: false, EnableDynamicField: true, + ShardsNum: 1, Dim: common.DefaultDim} + collName := createCollection(ctx, t, mc, cp) + + // insert + for i := 0; i < 4; i++ { + dp := DataParams{CollectionName: collName, PartitionName: "", CollectionFieldsType: AllFields, + start: i * common.DefaultNb, nb: common.DefaultNb, dim: common.DefaultDim, EnableDynamicField: true, WithRows: false} + _, _ = insertData(ctx, t, mc, dp) mc.Flush(ctx, collName, false) } @@ -36,6 +38,12 @@ func TestCompact(t *testing.T) { log.Printf("Id: %d, numRows: %d", s.ID, s.NumRows) } + indexHnsw, _ := entity.NewIndexHNSW(entity.L2, 8, 96) + for _, field := range common.AllVectorsFieldsName { + err := mc.CreateIndex(ctx, collName, field, indexHnsw, false) + common.CheckErr(t, err, true) + } + // compact compactionID, errCompact := mc.Compact(ctx, collName, 100) common.CheckErr(t, errCompact, true) diff --git a/test/testcases/delete_test.go b/test/testcases/delete_test.go index afed4c9c0..5bd43fc19 100644 --- a/test/testcases/delete_test.go +++ b/test/testcases/delete_test.go @@ -273,7 +273,7 @@ func TestDeleteExpressions(t *testing.T) { // create collection cp := CollectionParams{ - CollectionFieldsType: Int64FloatVecJSON, + CollectionFieldsType: AllFields, AutoID: false, EnableDynamicField: true, ShardsNum: common.DefaultShards, @@ -285,7 +285,7 @@ func TestDeleteExpressions(t *testing.T) { dp := DataParams{ CollectionName: collName, PartitionName: "", - CollectionFieldsType: Int64FloatVecJSON, + CollectionFieldsType: AllFields, start: 0, nb: common.DefaultNb, dim: common.DefaultDim, @@ -294,7 +294,10 @@ func TestDeleteExpressions(t *testing.T) { _, _ = insertData(ctx, t, mc, dp) idx, _ := entity.NewIndexHNSW(entity.L2, 8, 96) - _ = mc.CreateIndex(ctx, collName, common.DefaultFloatVecFieldName, idx, false) + for _, field := range common.AllVectorsFieldsName { + err := mc.CreateIndex(ctx, collName, field, idx, false) + common.CheckErr(t, err, true) + } // Load collection errLoad := mc.LoadCollection(ctx, collName, false) @@ -322,6 +325,10 @@ func TestDeleteExpressions(t *testing.T) { fmt.Sprintf("%s['list'] == [0, 1] ", common.DefaultJSONFieldName), fmt.Sprintf("%s['list'][0] < 10 ", common.DefaultJSONFieldName), fmt.Sprintf("%s[\"dynamicList\"] != [2, 3]", common.DefaultDynamicFieldName), + fmt.Sprintf("%s > 2500", common.DefaultJSONFieldName), + fmt.Sprintf("%s > 2000.5", common.DefaultJSONFieldName), + fmt.Sprintf("%s[0] == 2503", common.DefaultJSONFieldName), + fmt.Sprintf("%s like '21%%' ", common.DefaultJSONFieldName), // json contains fmt.Sprintf("json_contains (%s['list'], 2)", common.DefaultJSONFieldName), @@ -382,13 +389,42 @@ func TestDeleteInvalidExpr(t *testing.T) { _ = mc.CreateIndex(ctx, collName, common.DefaultFloatVecFieldName, idx, false) err := mc.LoadCollection(ctx, collName, false) - common.CheckErr(t, err, true, "") + common.CheckErr(t, err, true) err = mc.Delete(ctx, collName, "", "") - common.CheckErr(t, err, false, "invalid expression") + common.CheckErr(t, err, false, "invalid expression: invalid parameter") for _, _invalidExprs := range common.InvalidExpressions { err := mc.Delete(ctx, collName, "", _invalidExprs.Expr) common.CheckErr(t, err, _invalidExprs.ErrNil, _invalidExprs.ErrMsg) } } + +func TestDeleteComplexExprWithoutLoading(t *testing.T) { + // TODO + ctx := createContext(t, time.Second*common.DefaultTimeout*3) + // connect + mc := createMilvusClient(ctx, t) + + cp := CollectionParams{CollectionFieldsType: Int64FloatVecJSON, AutoID: false, EnableDynamicField: true, + ShardsNum: common.DefaultShards, Dim: common.DefaultDim} + collName := createCollection(ctx, t, mc, cp, client.WithConsistencyLevel(entity.ClStrong)) + + // prepare and insert data + dp := DataParams{CollectionName: collName, PartitionName: "", CollectionFieldsType: Int64FloatVecJSON, + start: 0, nb: common.DefaultNb, dim: common.DefaultDim, EnableDynamicField: true, WithRows: false} + _, _ = insertData(ctx, t, mc, dp) + mc.Flush(ctx, collName, false) + + err := mc.Delete(ctx, collName, "", "int64 < 100") + common.CheckErr(t, err, false, "collection not loaded") + + // load + idx, _ := entity.NewIndexHNSW(entity.L2, 8, 72) + _ = mc.CreateIndex(ctx, collName, common.DefaultFloatVecFieldName, idx, false) + err = mc.LoadCollection(ctx, collName, false) + common.CheckErr(t, err, true) + + err = mc.Delete(ctx, collName, "", "int64 < 100") + common.CheckErr(t, err, true) +} diff --git a/test/testcases/groupby_search_test.go b/test/testcases/groupby_search_test.go index 56d3e5ab7..bdc3b2f29 100644 --- a/test/testcases/groupby_search_test.go +++ b/test/testcases/groupby_search_test.go @@ -59,8 +59,8 @@ func genUnsupportedFloatGroupByIndex() []entity.Index { } } -func prepareDataForGroupBySearch(t *testing.T, loopInsert int, idx entity.Index, withGrowing bool) (*base.MilvusClient, context.Context, string) { - ctx := createContext(t, time.Second*common.DefaultTimeout) +func prepareDataForGroupBySearch(t *testing.T, loopInsert int, insertNi int, idx entity.Index, withGrowing bool) (*base.MilvusClient, context.Context, string) { + ctx := createContext(t, time.Second*common.DefaultTimeout*5) mc := createMilvusClient(ctx, t) // create collection with all datatype @@ -70,7 +70,7 @@ func prepareDataForGroupBySearch(t *testing.T, loopInsert int, idx entity.Index, // insert dp := DataParams{CollectionName: collName, PartitionName: "", CollectionFieldsType: AllFields, - start: 0, nb: 1000, dim: common.DefaultDim, EnableDynamicField: true, WithRows: false} + start: 0, nb: insertNi, dim: common.DefaultDim, EnableDynamicField: true, WithRows: false} for i := 0; i < loopInsert; i++ { _, _ = insertData(ctx, t, mc, dp) } @@ -79,18 +79,32 @@ func prepareDataForGroupBySearch(t *testing.T, loopInsert int, idx entity.Index, mc.Flush(ctx, collName, false) } + // skip scalar index + // create vector index and scalar index - supportedGroupByFields := []string{common.DefaultIntFieldName, "int8", "int16", "int32", "varchar", "bool"} - idxScalar := entity.NewScalarIndex() - for _, groupByField := range supportedGroupByFields { - mc.CreateIndex(ctx, collName, groupByField, idxScalar, false) - //common.CheckErr(t, err, true) + //supportedGroupByFields := []string{common.DefaultIntFieldName, "int8", "int16", "int32", "varchar", "bool"} + //idxScalar := entity.NewScalarIndex() + //for _, groupByField := range supportedGroupByFields { + // mc.CreateIndex(ctx, collName, groupByField, idxScalar, false) + // //common.CheckErr(t, err, true) + //} + idxHnsw, _ := entity.NewIndexHNSW(entity.COSINE, 8, 96) + indexBinary, _ := entity.NewIndexBinIvfFlat(entity.JACCARD, 64) + for _, fieldName := range common.AllVectorsFieldsName { + if fieldName == common.DefaultFloatVecFieldName { + err := mc.CreateIndex(ctx, collName, common.DefaultFloatVecFieldName, idx, false) + common.CheckErr(t, err, true) + } else if fieldName == common.DefaultBinaryVecFieldName { + err := mc.CreateIndex(ctx, collName, fieldName, indexBinary, false) + common.CheckErr(t, err, true) + } else { + err := mc.CreateIndex(ctx, collName, fieldName, idxHnsw, false) + common.CheckErr(t, err, true) + } } - err := mc.CreateIndex(ctx, collName, common.DefaultFloatVecFieldName, idx, false) - common.CheckErr(t, err, true) // load collection - err = mc.LoadCollection(ctx, collName, false) + err := mc.LoadCollection(ctx, collName, false) common.CheckErr(t, err, true) return mc, ctx, collName @@ -101,12 +115,12 @@ func prepareDataForGroupBySearch(t *testing.T, loopInsert int, idx entity.Index, // -> verify every top passage is the top of whole group // output_fields: pk + groupBy func TestSearchGroupByFloatDefault(t *testing.T) { - t.Skip("unstable case") + //t.Skip("unstable case and https://github.com/milvus-io/milvus/issues/31494") t.Parallel() for _, metricType := range common.SupportFloatMetricType { for _, idx := range genGroupByVectorIndex(metricType) { // prepare data - mc, ctx, collName := prepareDataForGroupBySearch(t, 10, idx, false) + mc, ctx, collName := prepareDataForGroupBySearch(t, 100, 200, idx, false) // search params queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector) @@ -150,7 +164,7 @@ func TestSearchGroupByFloatDefault(t *testing.T) { _str := fmt.Sprintf("GroupBy search with field %s, nq=%d and limit=%d , then hitsNum= %d, hitsRate=%v\n", groupByField, common.DefaultNq, common.DefaultTopK, hitsNum, hitsRate) log.Println(_str) - require.GreaterOrEqualf(t, hitsRate, float32(0.1), _str) + //require.GreaterOrEqualf(t, hitsRate, float32(0.1), _str) } } } @@ -246,7 +260,7 @@ func TestSearchGroupByBinaryGrowing(t *testing.T) { func TestSearchGroupByFloatGrowing(t *testing.T) { for _, metricType := range common.SupportFloatMetricType { idxHnsw, _ := entity.NewIndexHNSW(metricType, 8, 96) - mc, ctx, collName := prepareDataForGroupBySearch(t, 1, idxHnsw, true) + mc, ctx, collName := prepareDataForGroupBySearch(t, 5, 1000, idxHnsw, true) // search params queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector) @@ -300,7 +314,7 @@ func TestSearchGroupByFloatGrowing(t *testing.T) { func TestSearchGroupByPagination(t *testing.T) { // create index and load idx, _ := entity.NewIndexHNSW(entity.COSINE, 8, 96) - mc, ctx, collName := prepareDataForGroupBySearch(t, 5, idx, false) + mc, ctx, collName := prepareDataForGroupBySearch(t, 10, 1000, idx, false) // search params queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector) @@ -327,14 +341,14 @@ func TestSearchGroupByPagination(t *testing.T) { func TestSearchGroupByUnsupportedIndex(t *testing.T) { t.Parallel() for _, idx := range genUnsupportedFloatGroupByIndex() { - mc, ctx, collName := prepareDataForGroupBySearch(t, 3, idx, false) - + mc, ctx, collName := prepareDataForGroupBySearch(t, 3, 1000, idx, false) // groupBy search queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector) sp, _ := entity.NewIndexIvfFlatSearchParam(32) _, err := mc.Search(ctx, collName, []string{}, "", []string{common.DefaultIntFieldName, common.DefaultVarcharFieldName}, queryVec, common.DefaultFloatVecFieldName, entity.MetricType(idx.Params()["metrics_type"]), common.DefaultTopK, sp, client.WithGroupByField(common.DefaultVarcharFieldName)) + common.CheckErr(t, err, false, "trying to groupBy on unsupported index type will fail, "+ "currently only support ivf-flat, ivf_cc and HNSW") } @@ -343,7 +357,7 @@ func TestSearchGroupByUnsupportedIndex(t *testing.T) { // FLOAT, DOUBLE, JSON, ARRAY func TestSearchGroupByUnsupportedDataType(t *testing.T) { idxHnsw, _ := entity.NewIndexHNSW(entity.L2, 8, 96) - mc, ctx, collName := prepareDataForGroupBySearch(t, 1, idxHnsw, true) + mc, ctx, collName := prepareDataForGroupBySearch(t, 1, 1000, idxHnsw, true) // groupBy search with unsupported field type queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector) @@ -361,12 +375,43 @@ func TestSearchGroupByIterator(t *testing.T) { // TODO: sdk support } -// groupBy + range search +// groupBy + range search -> not supported func TestSearchGroupByRangeSearch(t *testing.T) { - // TODO: sdk support + idxHnsw, _ := entity.NewIndexHNSW(entity.COSINE, 8, 96) + mc, ctx, collName := prepareDataForGroupBySearch(t, 1, 1000, idxHnsw, true) + + // groupBy search with range + queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector) + sp, _ := entity.NewIndexHNSWSearchParam(50) + sp.AddRadius(0) + sp.AddRangeFilter(0.8) + + // range search + _, err := mc.Search(ctx, collName, []string{}, "", []string{common.DefaultIntFieldName, common.DefaultVarcharFieldName}, + queryVec, common.DefaultFloatVecFieldName, entity.COSINE, common.DefaultTopK, sp, + client.WithGroupByField(common.DefaultVarcharFieldName)) + + common.CheckErr(t, err, false, "Not allowed to do range-search when doing search-group-by") } // groupBy + advanced search -func TestSearchGroupByAdvancedSearch(t *testing.T) { - // TODO +func TestSearchGroupByHybridSearch(t *testing.T) { + // prepare data + indexHnsw, _ := entity.NewIndexHNSW(entity.L2, 8, 96) + mc, ctx, collName := prepareDataForGroupBySearch(t, 10, 1000, indexHnsw, false) + + // hybrid search with groupBy field + sp, _ := entity.NewIndexHNSWSearchParam(20) + expr := fmt.Sprintf("%s > 4", common.DefaultIntFieldName) + queryVec1 := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector) + queryVec2 := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector) + sReqs := []*client.ANNSearchRequest{ + client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.L2, expr, queryVec1, sp, common.DefaultTopK, client.WithOffset(2)), + client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.L2, expr, queryVec2, sp, common.DefaultTopK, client.WithGroupByField("varchar")), + } + //supportedGroupByFields := []string{common.DefaultIntFieldName, "int8", "int16", "int32", "varchar", "bool"} + resGroupBy, errSearch := mc.HybridSearch(ctx, collName, []string{}, common.DefaultTopK, []string{}, client.NewRRFReranker(), sReqs) + common.CheckErr(t, errSearch, true) + common.CheckSearchResult(t, resGroupBy, 1, common.DefaultTopK) + log.Println(resGroupBy[0].IDs, resGroupBy[0].GroupByValue) } diff --git a/test/testcases/hybrid_search_test.go b/test/testcases/hybrid_search_test.go new file mode 100644 index 000000000..a96f20599 --- /dev/null +++ b/test/testcases/hybrid_search_test.go @@ -0,0 +1,303 @@ +//go:build L0 + +package testcases + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/milvus-io/milvus-sdk-go/v2/client" + + "github.com/milvus-io/milvus-sdk-go/v2/entity" + "github.com/milvus-io/milvus-sdk-go/v2/test/common" +) + +func TestHybridSearchDefault(t *testing.T) { + ctx := createContext(t, time.Second*common.DefaultTimeout) + // connect + mc := createMilvusClient(ctx, t) + + // create -> insert [0, 3000) -> flush -> index -> load + cp := CollectionParams{CollectionFieldsType: Int64FloatVec, AutoID: false, EnableDynamicField: false, + ShardsNum: common.DefaultShards, Dim: common.DefaultDim} + + dp := DataParams{DoInsert: true, CollectionFieldsType: Int64FloatVec, start: 0, nb: common.DefaultNb, + dim: common.DefaultDim, EnableDynamicField: false} + + collName := prepareCollection(ctx, t, mc, cp, WithDataParams(dp), WithCreateOption(client.WithConsistencyLevel(entity.ClStrong))) + + // hybrid search + ranker := client.NewRRFReranker() + expr := fmt.Sprintf("%s > 10", common.DefaultIntFieldName) + sp, _ := entity.NewIndexFlatSearchParam() + queryVec1 := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector) + queryVec2 := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector) + sReqs := []*client.ANNSearchRequest{ + client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.L2, expr, queryVec1, sp, common.DefaultTopK), + client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.L2, expr, queryVec2, sp, common.DefaultTopK), + } + searchRes, errSearch := mc.HybridSearch(ctx, collName, []string{}, common.DefaultTopK, []string{"*"}, ranker, sReqs) + common.CheckErr(t, errSearch, true) + common.CheckSearchResult(t, searchRes, 1, common.DefaultTopK) + common.CheckOutputFields(t, searchRes[0].Fields, []string{common.DefaultIntFieldName, common.DefaultFloatFieldName, common.DefaultFloatVecFieldName}) +} + +// hybrid search default -> verify success +func TestHybridSearchMultiVectorsDefault(t *testing.T) { + t.Parallel() + ctx := createContext(t, time.Second*common.DefaultTimeout*2) + // connect + mc := createMilvusClient(ctx, t) + for _, enableDynamic := range []bool{false, true} { + // create -> insert [0, 3000) -> flush -> index -> load + cp := CollectionParams{CollectionFieldsType: AllFields, AutoID: false, EnableDynamicField: enableDynamic, + ShardsNum: common.DefaultShards, Dim: common.DefaultDim} + + dp := DataParams{DoInsert: true, CollectionFieldsType: AllFields, start: 0, nb: common.DefaultNb * 3, + dim: common.DefaultDim, EnableDynamicField: enableDynamic} + + ips := GenDefaultIndexParamsForAllVectors() + + collName := prepareCollection(ctx, t, mc, cp, WithDataParams(dp), WithIndexParams(ips), WithCreateOption(client.WithConsistencyLevel(entity.ClStrong))) + + // hybrid search with different limit + type limitGroup struct { + limit1 int + limit2 int + limit3 int + expLimit int + } + limits := []limitGroup{ + {limit1: 10, limit2: 5, limit3: 8, expLimit: 8}, + {limit1: 10, limit2: 5, limit3: 15, expLimit: 15}, + {limit1: 10, limit2: 5, limit3: 20, expLimit: 15}, + } + sp, _ := entity.NewIndexFlatSearchParam() + expr := fmt.Sprintf("%s > 5", common.DefaultIntFieldName) + queryVec1 := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector) + queryVec2 := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloat16Vector) + + // search with different reranker and limit + for _, reranker := range []client.Reranker{client.NewRRFReranker(), + client.NewWeightedReranker([]float64{0.8, 0.2}), + client.NewWeightedReranker([]float64{0.0, 0.2}), + client.NewWeightedReranker([]float64{0.4, 1.0}), + } { + for _, limit := range limits { + // hybrid search + sReqs := []*client.ANNSearchRequest{ + client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.L2, expr, queryVec1, sp, limit.limit1), + client.NewANNSearchRequest(common.DefaultFloat16VecFieldName, entity.L2, expr, queryVec2, sp, limit.limit2), + } + searchRes, errSearch := mc.HybridSearch(ctx, collName, []string{}, limit.limit3, []string{"*"}, reranker, sReqs) + common.CheckErr(t, errSearch, true) + common.CheckSearchResult(t, searchRes, 1, limit.expLimit) + common.CheckOutputFields(t, searchRes[0].Fields, common.GetAllFieldsName(enableDynamic, false)) + } + } + } +} + +// invalid limit: 0, -1, max+1 +// invalid WeightedReranker params +// invalid fieldName: not exist +// invalid metric type: mismatch +func TestHybridSearchInvalidParams(t *testing.T) { + ctx := createContext(t, time.Second*common.DefaultTimeout*2) + // connect + mc := createMilvusClient(ctx, t) + + // create -> insert [0, 3000) -> flush -> index -> load + cp := CollectionParams{CollectionFieldsType: AllVectors, AutoID: false, EnableDynamicField: false, + ShardsNum: common.DefaultShards, Dim: common.DefaultDim} + + dp := DataParams{DoInsert: true, CollectionFieldsType: AllVectors, start: 0, nb: common.DefaultNb, + dim: common.DefaultDim, EnableDynamicField: false} + + // index params + ips := GenDefaultIndexParamsForAllVectors() + collName := prepareCollection(ctx, t, mc, cp, WithDataParams(dp), WithIndexParams(ips), + WithCreateOption(client.WithConsistencyLevel(entity.ClStrong))) + + // hybrid search with invalid limit + ranker := client.NewRRFReranker() + sp, _ := entity.NewIndexFlatSearchParam() + queryVec1 := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector) + queryVec2 := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeBinaryVector) + sReqs := []*client.ANNSearchRequest{ + client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.L2, "", queryVec1, sp, common.DefaultTopK), + client.NewANNSearchRequest(common.DefaultBinaryVecFieldName, entity.JACCARD, "", queryVec2, sp, common.DefaultTopK), + } + for _, invalidLimit := range []int{-1, 0, common.MaxTopK + 1} { + sReqsInvalid := []*client.ANNSearchRequest{ + client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.L2, "", queryVec1, sp, invalidLimit)} + + for _, sReq := range [][]*client.ANNSearchRequest{sReqs, sReqsInvalid} { + _, errSearch := mc.HybridSearch(ctx, collName, []string{}, invalidLimit, []string{}, ranker, sReq) + common.CheckErr(t, errSearch, false, "should be greater than 0", "should be in range [1, 16384]") + } + } + + // hybrid search with invalid WeightedReranker params + for _, invalidRanker := range []client.Reranker{ + client.NewWeightedReranker([]float64{-1, 0.2}), + client.NewWeightedReranker([]float64{1.2, 0.2}), + client.NewWeightedReranker([]float64{0.2}), + client.NewWeightedReranker([]float64{0.2, 0.7, 0.5}), + } { + _, errSearch := mc.HybridSearch(ctx, collName, []string{}, common.DefaultTopK, []string{}, invalidRanker, sReqs) + common.CheckErr(t, errSearch, false, "rank param weight should be in range [0, 1]", + "the length of weights param mismatch with ann search requests") + } + + // invalid fieldName: not exist + sReqs = append(sReqs, client.NewANNSearchRequest("a", entity.L2, "", queryVec1, sp, common.DefaultTopK)) + _, errSearch := mc.HybridSearch(ctx, collName, []string{}, common.DefaultTopK, []string{}, ranker, sReqs) + common.CheckErr(t, errSearch, false, "failed to get field schema by name: fieldName(a) not found") + + // invalid metric type: mismatch + sReqsInvalidMetric := []*client.ANNSearchRequest{ + client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.COSINE, "", queryVec1, sp, common.DefaultTopK), + client.NewANNSearchRequest(common.DefaultBinaryVecFieldName, entity.JACCARD, "", queryVec2, sp, common.DefaultTopK), + } + _, errSearch = mc.HybridSearch(ctx, collName, []string{}, common.DefaultTopK, []string{}, ranker, sReqsInvalidMetric) + common.CheckErr(t, errSearch, false, "metric type not match: invalid parameter") +} + +// len(nq) != 1 +// vector type mismatch: vectors: float32, queryVec: binary +// vector dim mismatch +func TestHybridSearchInvalidVectors(t *testing.T) { + ctx := createContext(t, time.Second*common.DefaultTimeout*2) + // connect + mc := createMilvusClient(ctx, t) + + // create -> insert [0, 3000) -> flush -> index -> load + cp := CollectionParams{CollectionFieldsType: Int64FloatVec, AutoID: false, EnableDynamicField: false, + ShardsNum: common.DefaultShards, Dim: common.DefaultDim} + + dp := DataParams{DoInsert: true, CollectionFieldsType: Int64FloatVec, start: 0, nb: common.DefaultNb, + dim: common.DefaultDim, EnableDynamicField: false} + + collName := prepareCollection(ctx, t, mc, cp, WithDataParams(dp), WithCreateOption(client.WithConsistencyLevel(entity.ClStrong))) + + // hybrid search with invalid limit + ranker := client.NewRRFReranker() + sp, _ := entity.NewIndexFlatSearchParam() + queryVecNq := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector) + queryVecBinary := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeBinaryVector) + queryVecType := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloat16Vector) + queryVecDim := common.GenSearchVectors(1, common.DefaultDim*2, entity.FieldTypeFloatVector) + sReqs := [][]*client.ANNSearchRequest{ + {client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.L2, "", queryVecNq, sp, common.DefaultTopK)}, // nq != 1 + {client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.L2, "", queryVecType, sp, common.DefaultTopK)}, // TODO vector type not match + {client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.L2, "", queryVecDim, sp, common.DefaultTopK)}, // vector dim not match + {client.NewANNSearchRequest(common.DefaultBinaryVecFieldName, entity.JACCARD, "", queryVecBinary, sp, common.DefaultTopK)}, // not exist vector types + } + for _, invalidSReq := range sReqs { + _, errSearch := mc.HybridSearch(ctx, collName, []string{}, common.DefaultTopK, []string{}, ranker, invalidSReq) + common.CheckErr(t, errSearch, false, "nq should be equal to 1", "vector dimension mismatch", + "failed to get field schema by name", "vector type must be the same") + } +} + +// hybrid search Pagination -> verify success +func TestHybridSearchMultiVectorsPagination(t *testing.T) { + t.Parallel() + ctx := createContext(t, time.Second*common.DefaultTimeout*2) + // connect + mc := createMilvusClient(ctx, t) + + // create -> insert [0, 3000) -> flush -> index -> load + cp := CollectionParams{CollectionFieldsType: AllVectors, AutoID: false, EnableDynamicField: false, + ShardsNum: common.DefaultShards, Dim: common.DefaultDim} + + dp := DataParams{DoInsert: true, CollectionFieldsType: AllVectors, start: 0, nb: common.DefaultNb, + dim: common.DefaultDim, EnableDynamicField: false} + + // index params + ips := GenDefaultIndexParamsForAllVectors() + collName := prepareCollection(ctx, t, mc, cp, WithDataParams(dp), WithIndexParams(ips), WithCreateOption(client.WithConsistencyLevel(entity.ClStrong))) + + // hybrid search with different limit + sp, _ := entity.NewIndexFlatSearchParam() + expr := fmt.Sprintf("%s > 4", common.DefaultIntFieldName) + queryVec1 := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector) + queryVec2 := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloat16Vector) + for _, invalidOffset := range []int64{-1, common.MaxTopK + 1} { + sReqs := []*client.ANNSearchRequest{ + client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.L2, "", queryVec1, sp, common.DefaultTopK, client.WithOffset(invalidOffset)), + client.NewANNSearchRequest(common.DefaultFloat16VecFieldName, entity.L2, "", queryVec2, sp, common.DefaultTopK), + } + _, errSearch := mc.HybridSearch(ctx, collName, []string{}, common.DefaultTopK, []string{}, client.NewRRFReranker(), sReqs) + common.CheckErr(t, errSearch, false, "top k should be in range [1, 16384]") + } + + // search with different reranker and offset + for _, reranker := range []client.Reranker{ + client.NewRRFReranker(), + client.NewWeightedReranker([]float64{0.8, 0.2}), + client.NewWeightedReranker([]float64{0.0, 0.2}), + client.NewWeightedReranker([]float64{0.4, 1.0}), + } { + sReqs := []*client.ANNSearchRequest{ + client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.L2, expr, queryVec1, sp, common.DefaultTopK, client.WithOffset(5)), + client.NewANNSearchRequest(common.DefaultFloat16VecFieldName, entity.L2, expr, queryVec2, sp, common.DefaultTopK), + } + // hybrid search + searchRes, errSearch := mc.HybridSearch(ctx, collName, []string{}, common.DefaultTopK, []string{}, reranker, sReqs) + common.CheckErr(t, errSearch, true) + common.CheckSearchResult(t, searchRes, 1, common.DefaultTopK) + } +} + +// hybrid search Pagination -> verify success +func TestHybridSearchMultiVectorsRangeSearch(t *testing.T) { + ctx := createContext(t, time.Second*common.DefaultTimeout*5) + // connect + mc := createMilvusClient(ctx, t) + + // create -> insert [0, 3000) -> flush -> index -> load + cp := CollectionParams{CollectionFieldsType: AllVectors, AutoID: false, EnableDynamicField: false, + ShardsNum: common.DefaultShards, Dim: common.DefaultDim} + + dp := DataParams{DoInsert: true, CollectionFieldsType: AllVectors, start: 0, nb: common.DefaultNb * 3, + dim: common.DefaultDim, EnableDynamicField: false} + + // index params + ips := GenDefaultIndexParamsForAllVectors() + collName := prepareCollection(ctx, t, mc, cp, WithDataParams(dp), WithIndexParams(ips), WithCreateOption(client.WithConsistencyLevel(entity.ClStrong))) + + // hybrid search + sp, _ := entity.NewIndexFlatSearchParam() + expr := fmt.Sprintf("%s > 4", common.DefaultIntFieldName) + queryVec1 := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector) + queryVec2 := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloat16Vector) + + // search with different reranker and offset + sp.AddRadius(10) + sp.AddRangeFilter(0.01) + for _, reranker := range []client.Reranker{ + client.NewRRFReranker(), + client.NewWeightedReranker([]float64{0.8, 0.2}), + client.NewWeightedReranker([]float64{0.5, 0.5}), + } { + sReqs := []*client.ANNSearchRequest{ + client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.L2, expr, queryVec1, sp, common.DefaultTopK*2, client.WithOffset(1)), + client.NewANNSearchRequest(common.DefaultFloat16VecFieldName, entity.L2, expr, queryVec2, sp, common.DefaultTopK), + } + // hybrid search + resRange, errSearch := mc.HybridSearch(ctx, collName, []string{}, common.DefaultTopK, []string{}, reranker, sReqs) + common.CheckErr(t, errSearch, true) + common.CheckSearchResult(t, resRange, 1, common.DefaultTopK) + for _, res := range resRange { + for _, score := range res.Scores { + require.GreaterOrEqual(t, score, float32(0.01)) + require.LessOrEqual(t, score, float32(5)) + } + } + } +} diff --git a/test/testcases/index_test.go b/test/testcases/index_test.go index 10114324a..158748dd4 100644 --- a/test/testcases/index_test.go +++ b/test/testcases/index_test.go @@ -33,15 +33,15 @@ func supportScalarIndexFieldType(field entity.FieldType) bool { // test create index with supported float vector index, L2 metric type func TestCreateIndex(t *testing.T) { t.Parallel() + ctx := createContext(t, time.Second*common.DefaultTimeout*5) + // connect + mc := createMilvusClient(ctx, t) + collName, _ := createCollectionWithDataIndex(ctx, t, mc, false, false) // create index allFloatIndexes := common.GenAllFloatIndex() for _, idx := range allFloatIndexes { - ctx := createContext(t, time.Second*common.DefaultTimeout*3) - // connect - mc := createMilvusClient(ctx, t) // create default collection with flush data - collName, _ := createCollectionWithDataIndex(ctx, t, mc, false, false) err := mc.CreateIndex(ctx, collName, common.DefaultFloatVecFieldName, idx, false, client.WithIndexName(common.DefaultIndexName)) common.CheckErr(t, err, true) @@ -49,6 +49,80 @@ func TestCreateIndex(t *testing.T) { indexes, _ := mc.DescribeIndex(ctx, collName, common.DefaultFloatVecFieldName) expIndex := entity.NewGenericIndex(common.DefaultIndexName, idx.IndexType(), idx.Params()) common.CheckIndexResult(t, indexes, expIndex) + + // drop index + err = mc.DropIndex(ctx, collName, common.DefaultFloatVecFieldName) + common.CheckErr(t, err, true) + } +} + +// create index for fp16 and bf16 vectors +func TestCreateIndexMultiVectors(t *testing.T) { + ctx := createContext(t, time.Second*common.DefaultTimeout*5) + // connect + mc := createMilvusClient(ctx, t) + + // create collection + cp := CollectionParams{CollectionFieldsType: AllVectors, AutoID: false, EnableDynamicField: true, ShardsNum: 1, + Dim: common.DefaultDim} + collName := createCollection(ctx, t, mc, cp) + + // insert + dp := DataParams{CollectionName: collName, PartitionName: "", CollectionFieldsType: AllVectors, start: 0, + nb: common.DefaultNb, dim: common.DefaultDim, EnableDynamicField: true} + _, _ = insertData(ctx, t, mc, dp) + _ = mc.Flush(ctx, collName, false) + + // create index for all vectors + idxHnsw, _ := entity.NewIndexHNSW(entity.L2, 8, 96) + err := mc.CreateIndex(ctx, collName, common.DefaultFloatVecFieldName, idxHnsw, false) + common.CheckErr(t, err, true) + for _, idx := range common.GenAllFloatIndex() { + for _, fieldName := range []string{common.DefaultFloat16VecFieldName, common.DefaultBFloat16VecFieldName} { + err := mc.CreateIndex(ctx, collName, fieldName, idx, false, client.WithIndexName(fieldName)) + common.CheckErr(t, err, true) + + // describe index + indexes, _ := mc.DescribeIndex(ctx, collName, fieldName) + expIndex := entity.NewGenericIndex(fieldName, idx.IndexType(), idx.Params()) + common.CheckIndexResult(t, indexes, expIndex) + + // drop index + err = mc.DropIndex(ctx, collName, fieldName, client.WithIndexName(fieldName)) + common.CheckErr(t, err, true) + } + } + for _, metricType := range common.SupportBinIvfFlatMetricType { + idx, _ := entity.NewIndexBinFlat(metricType, 64) + err := mc.CreateIndex(ctx, collName, common.DefaultBinaryVecFieldName, idx, false, client.WithIndexName(common.DefaultBinaryVecFieldName)) + common.CheckErr(t, err, true) + + // drop index + err = mc.DropIndex(ctx, collName, common.DefaultBinaryVecFieldName, client.WithIndexName(common.DefaultBinaryVecFieldName)) + common.CheckErr(t, err, true) + } +} + +func TestDescribeIndexMultiVectors(t *testing.T) { + t.Skip("https://github.com/milvus-io/milvus-sdk-go/issues/689") + ctx := createContext(t, time.Second*common.DefaultTimeout*2) + // connect + mc := createMilvusClient(ctx, t) + // create -> insert [0, 3000) -> flush -> index -> load + cp := CollectionParams{CollectionFieldsType: AllFields, AutoID: false, EnableDynamicField: true, + ShardsNum: common.DefaultShards, Dim: common.DefaultDim} + + dp := DataParams{DoInsert: true, CollectionFieldsType: AllFields, start: 0, nb: common.DefaultNb * 2, + dim: common.DefaultDim, EnableDynamicField: true} + + // create index for all vector fields + ips := GenDefaultIndexParamsForAllVectors() + + collName := prepareCollection(ctx, t, mc, cp, WithDataParams(dp), WithIndexParams(ips), WithCreateOption(client.WithConsistencyLevel(entity.ClStrong))) + indexes, err := mc.DescribeIndex(ctx, collName, common.DefaultFloatVecFieldName) + common.CheckErr(t, err, true) + for _, idx := range indexes { + log.Println(idx.Params(), idx.IndexType()) } } @@ -92,6 +166,16 @@ func TestCreateScalarIndex(t *testing.T) { _, _ = insertData(ctx, t, mc, dp) mc.Flush(ctx, collName, false) + // create index for all vector fields + indexHnsw, _ := entity.NewIndexHNSW(entity.L2, 8, 96) + for _, field := range common.AllVectorsFieldsName { + err := mc.CreateIndex(ctx, collName, field, indexHnsw, false) + common.CheckErr(t, err, true) + indexes, _ := mc.DescribeIndex(ctx, collName, field) + expIndex := entity.NewGenericIndex(field, entity.HNSW, indexHnsw.Params()) + common.CheckIndexResult(t, indexes, expIndex) + } + coll, _ := mc.DescribeCollection(ctx, collName) idx := entity.NewScalarIndex() for _, field := range coll.Schema.Fields { @@ -160,11 +244,11 @@ func TestCreateIndexJsonField(t *testing.T) { // create vector index on json field idx, _ := entity.NewIndexHNSW(entity.L2, 8, 96) err := mc.CreateIndex(ctx, collName, common.DefaultJSONFieldName, idx, false, client.WithIndexName("json_index")) - common.CheckErr(t, err, false, "create index on JSON field is not supported") + common.CheckErr(t, err, false, "only support float vector or binary vector") - // create scalar index on json field - //err = mc.CreateIndex(ctx, collName, common.DefaultJSONFieldName, entity.NewScalarIndex(), false, client.WithIndexName("json_index")) - //common.CheckErr(t, err, false, "create index on json field is not supported") + //create scalar index on json field + err = mc.CreateIndex(ctx, collName, common.DefaultJSONFieldName, entity.NewScalarIndex(), false, client.WithIndexName("json_index")) + common.CheckErr(t, err, false, "create auto index on JSON field is not supported") } func TestCreateIndexArrayField(t *testing.T) { @@ -194,10 +278,10 @@ func TestCreateIndexArrayField(t *testing.T) { if field.DataType == entity.FieldTypeArray { // create scalar index err := mc.CreateIndex(ctx, collName, field.Name, scalarIdx, false, client.WithIndexName("scalar_index")) - common.CheckErr(t, err, false, "create index on Array field is not supported: invalid parameter") + common.CheckErr(t, err, false, "create auto index on Array field is not supported") // create vector index err1 := mc.CreateIndex(ctx, collName, field.Name, vectorIdx, false, client.WithIndexName("vector_index")) - common.CheckErr(t, err1, false, "create index on Array field is not supported: invalid parameter") + common.CheckErr(t, err1, false, "float or float16 or bfloat16 vector are only supported") } } } @@ -390,12 +474,12 @@ func TestCreateIndexNotSupportedField(t *testing.T) { // create index idx, _ := entity.NewIndexHNSW(entity.L2, 8, 96) err := mc.CreateIndex(ctx, collName, common.DefaultFloatFieldName, idx, false) - common.CheckErr(t, err, false, "index type not match") + common.CheckErr(t, err, false, "only support float vector or binary vector") // create scann index indexScann, _ := entity.NewIndexSCANN(entity.L2, 8, true) err = mc.CreateIndex(ctx, collName, common.DefaultFloatFieldName, indexScann, false) - common.CheckErr(t, err, false, "index type not match") + common.CheckErr(t, err, false, "float or float16 or bfloat16 vector are only supported") } // test create index with invalid params @@ -502,6 +586,32 @@ func TestCreateIndexAsync(t *testing.T) { } } +// create same index name on different vector field +func TestIndexMultiVectorDupName(t *testing.T) { + ctx := createContext(t, time.Second*common.DefaultTimeout) + mc := createMilvusClient(ctx, t) + + // create collection with all datatype + cp := CollectionParams{CollectionFieldsType: AllVectors, AutoID: false, EnableDynamicField: true, + ShardsNum: common.DefaultShards, Dim: common.DefaultDim} + collName := createCollection(ctx, t, mc, cp, client.WithConsistencyLevel(entity.ClStrong)) + + // insert + dp := DataParams{CollectionName: collName, PartitionName: "", CollectionFieldsType: AllVectors, + start: 0, nb: common.DefaultNb, dim: common.DefaultDim, EnableDynamicField: true, WithRows: false} + _, err := insertData(ctx, t, mc, dp) + common.CheckErr(t, err, true) + + // create index with same indexName on different fields + idx, _ := entity.NewIndexHNSW(entity.COSINE, 8, 96) + err = mc.CreateIndex(ctx, collName, common.DefaultFloatVecFieldName, idx, false, client.WithIndexName("index_1")) + common.CheckErr(t, err, true) + + // same index on another field + err = mc.CreateIndex(ctx, collName, common.DefaultFloat16VecFieldName, idx, false, client.WithIndexName("index_1")) + common.CheckErr(t, err, false, "reateIndex failed: at most one distinct index is allowed per field") +} + // test get index state func TestIndexState(t *testing.T) { ctx := createContext(t, time.Second*common.DefaultTimeout) diff --git a/test/testcases/insert_test.go b/test/testcases/insert_test.go index 30d157fa3..20a505f63 100644 --- a/test/testcases/insert_test.go +++ b/test/testcases/insert_test.go @@ -205,6 +205,11 @@ func TestInsertColumnsMismatchFields(t *testing.T) { _, errInsert2 := mc.Insert(ctx, collName, "", intColumn, floatColumn, vecColumn, floatColumn) common.CheckErr(t, errInsert2, false, "duplicated column") + // + binaryColumn := common.GenColumnData(0, common.DefaultNb, entity.FieldTypeBinaryVector, "binaryVec", common.WithVectorDim(common.DefaultDim)) + _, errInsert4 := mc.Insert(ctx, collName, "", intColumn, floatColumn, vecColumn, binaryColumn) + common.CheckErr(t, errInsert4, false, "does not exist") + // order(column) != order(fields) _, errInsert3 := mc.Insert(ctx, collName, "", floatColumn, vecColumn, intColumn) common.CheckErr(t, errInsert3, true) @@ -440,20 +445,22 @@ func TestInsertArrayRows(t *testing.T) { mc := createMilvusClient(ctx, t) // create collection - cp := CollectionParams{CollectionFieldsType: AllFields, AutoID: false, EnableDynamicField: false, - ShardsNum: common.DefaultShards, Dim: common.DefaultDim, MaxCapacity: common.TestCapacity} - collName := createCollection(ctx, t, mc, cp) + for _, dynamic := range []bool{true, false} { + cp := CollectionParams{CollectionFieldsType: AllFields, AutoID: false, EnableDynamicField: dynamic, + ShardsNum: common.DefaultShards, Dim: common.DefaultDim, MaxCapacity: common.TestCapacity} + collName := createCollection(ctx, t, mc, cp) - // prepare and insert array rows data - dp := DataParams{CollectionName: collName, PartitionName: "", CollectionFieldsType: AllFields, - start: 0, nb: common.DefaultNb, dim: common.DefaultDim, EnableDynamicField: false, WithRows: true} - _, _ = insertData(ctx, t, mc, dp, common.WithArrayCapacity(common.TestCapacity)) + // prepare and insert array rows data + dp := DataParams{CollectionName: collName, PartitionName: "", CollectionFieldsType: AllFields, + start: 0, nb: common.DefaultNb, dim: common.DefaultDim, EnableDynamicField: dynamic, WithRows: true} + _, _ = insertData(ctx, t, mc, dp, common.WithArrayCapacity(common.TestCapacity)) - // flush and check row count - errFlush := mc.Flush(ctx, collName, false) - common.CheckErr(t, errFlush, true) - stats, _ := mc.GetCollectionStatistics(ctx, collName) - require.Equal(t, strconv.Itoa(common.DefaultNb), stats[common.RowCount]) + // flush and check row count + errFlush := mc.Flush(ctx, collName, false) + common.CheckErr(t, errFlush, true) + stats, _ := mc.GetCollectionStatistics(ctx, collName) + require.Equal(t, strconv.Itoa(common.DefaultNb), stats[common.RowCount]) + } } // test insert array data type not match array field element type diff --git a/test/testcases/load_release_test.go b/test/testcases/load_release_test.go index 06441a9f1..0d0e86af7 100644 --- a/test/testcases/load_release_test.go +++ b/test/testcases/load_release_test.go @@ -316,6 +316,42 @@ func TestLoadPartitionsAsync(t *testing.T) { } } +func TestLoadCollectionMultiVectors(t *testing.T) { + ctx := createContext(t, time.Second*common.DefaultTimeout*5) + // connect + mc := createMilvusClient(ctx, t) + + // create collection + cp := CollectionParams{CollectionFieldsType: AllVectors, AutoID: false, EnableDynamicField: true, + ShardsNum: common.DefaultShards, Dim: common.DefaultDim} + collName := createCollection(ctx, t, mc, cp) + + // insert + dp := DataParams{CollectionName: collName, PartitionName: "", CollectionFieldsType: AllVectors, + start: 0, nb: common.DefaultNb, dim: common.DefaultDim, EnableDynamicField: true, WithRows: false} + _, _ = insertData(ctx, t, mc, dp) + mc.Flush(ctx, collName, false) + + // create hnsw index on part vector field and load -> load failed + indexHnsw, _ := entity.NewIndexHNSW(entity.L2, 8, 96) + for _, field := range []string{common.DefaultFloatVecFieldName, common.DefaultFloat16VecFieldName, common.DefaultBFloat16VecFieldName} { + err := mc.CreateIndex(ctx, collName, field, indexHnsw, false) + common.CheckErr(t, err, true) + } + + err := mc.LoadCollection(ctx, collName, false) + common.CheckErr(t, err, false, "there is no vector index on field") + + // create index for another vector field + indexBinary, _ := entity.NewIndexBinIvfFlat(entity.JACCARD, 64) + for _, field := range []string{common.DefaultBinaryVecFieldName} { + err := mc.CreateIndex(ctx, collName, field, indexBinary, false) + common.CheckErr(t, err, true) + } + err = mc.LoadCollection(ctx, collName, false) + common.CheckErr(t, err, true) +} + // test release partition func TestReleasePartition(t *testing.T) { ctx := createContext(t, time.Second*common.DefaultTimeout) diff --git a/test/testcases/main_test.go b/test/testcases/main_test.go index 7df17a10e..74d21e5c6 100644 --- a/test/testcases/main_test.go +++ b/test/testcases/main_test.go @@ -247,6 +247,7 @@ const ( VarcharBinaryVec CollectionFieldsType = "PkVarcharBinaryVec" // varchar + binaryVec Int64FloatVecJSON CollectionFieldsType = "PkInt64FloatVecJson" // int64 + float + floatVec + json Int64FloatVecArray CollectionFieldsType = "Int64FloatVecArray" // int64 + float + floatVec + all array + AllVectors CollectionFieldsType = "AllVectors" // int64 + fp32Vec + fp16Vec + binaryVec AllFields CollectionFieldsType = "AllFields" // all scalar fields + floatVec ) @@ -270,6 +271,14 @@ func createCollection(ctx context.Context, t *testing.T, mc *base.MilvusClient, case Int64FloatVecArray: fields = common.GenDefaultFields(cp.AutoID) fields = append(fields, common.GenAllArrayFieldsWithCapacity(cp.MaxCapacity)...) + case AllVectors: + fields = []*entity.Field{ + common.GenField(common.DefaultIntFieldName, entity.FieldTypeInt64, common.WithIsPrimaryKey(true), common.WithAutoID(cp.AutoID)), + common.GenField(common.DefaultFloatVecFieldName, entity.FieldTypeFloatVector, common.WithDim(cp.Dim)), + common.GenField(common.DefaultFloat16VecFieldName, entity.FieldTypeFloat16Vector, common.WithDim(cp.Dim)), + common.GenField(common.DefaultBFloat16VecFieldName, entity.FieldTypeBFloat16Vector, common.WithDim(cp.Dim)), + common.GenField(common.DefaultBinaryVecFieldName, entity.FieldTypeBinaryVector, common.WithDim(cp.Dim)), + } case AllFields: fields = common.GenAllFields() } @@ -339,6 +348,11 @@ func insertData(ctx context.Context, t *testing.T, mc *base.MilvusClient, dp Dat data = append(data, intColumn, floatColumn, vecColumn) } + case AllVectors: + if dp.WithRows { + rows = common.GenAllVectorsRows(dp.start, dp.nb, dp.dim, dp.EnableDynamicField) + } + data = common.GenAllVectorsData(dp.start, dp.nb, dp.dim, opts...) case AllFields: if dp.WithRows { rows = common.GenAllFieldsRows(dp.start, dp.nb, dp.dim, dp.EnableDynamicField, opts...) @@ -429,7 +443,7 @@ func prepareCollection(ctx context.Context, t *testing.T, mc *base.MilvusClient, // default build index idx, err := entity.NewIndexHNSW(entity.L2, 8, 96) common.CheckErr(t, err, true) - defaultIndexParams := IndexParams{BuildIndex: true, Index: idx, FieldName: common.DefaultFloatVecFieldName, async: false} + defaultIndexParams := []IndexParams{{BuildIndex: true, Index: idx, FieldName: common.DefaultFloatVecFieldName, async: false}} // default load collection defaultLp := LoadParams{DoLoad: true, async: false} @@ -462,14 +476,16 @@ func prepareCollection(ctx context.Context, t *testing.T, mc *base.MilvusClient, } // index - if opt.IndexParams.BuildIndex { - var err error - if opt.IndexOpts == nil { - err = mc.CreateIndex(ctx, collName, opt.IndexParams.FieldName, opt.IndexParams.Index, opt.IndexParams.async) - } else { - err = mc.CreateIndex(ctx, collName, opt.IndexParams.FieldName, opt.IndexParams.Index, opt.IndexParams.async, opt.IndexOpts) + for _, idxParams := range opt.IndexParams { + if idxParams.BuildIndex { + var err error + if opt.IndexOpts == nil { + err = mc.CreateIndex(ctx, collName, idxParams.FieldName, idxParams.Index, idxParams.async) + } else { + err = mc.CreateIndex(ctx, collName, idxParams.FieldName, idxParams.Index, idxParams.async, opt.IndexOpts) + } + common.CheckErr(t, err, true) } - common.CheckErr(t, err, true) } // load @@ -490,6 +506,20 @@ func prepareCollection(ctx context.Context, t *testing.T, mc *base.MilvusClient, return collName } +func GenDefaultIndexParamsForAllVectors() []IndexParams { + indexHnsw, _ := entity.NewIndexHNSW(entity.L2, 8, 96) + indexBinary, _ := entity.NewIndexBinIvfFlat(entity.JACCARD, 64) + ips := make([]IndexParams, 4) + for _, fieldName := range common.AllVectorsFieldsName { + if fieldName == common.DefaultBinaryVecFieldName { + ips = append(ips, IndexParams{BuildIndex: true, Index: indexBinary, FieldName: fieldName, async: false}) + } else { + ips = append(ips, IndexParams{BuildIndex: true, Index: indexHnsw, FieldName: fieldName, async: false}) + } + } + return ips +} + func TestMain(m *testing.M) { flag.Parse() log.Printf("parse addr=%s", *addr) diff --git a/test/testcases/option.go b/test/testcases/option.go index c41d4a4bd..171f69669 100644 --- a/test/testcases/option.go +++ b/test/testcases/option.go @@ -65,7 +65,7 @@ type LoadParams struct { type ClientParamsOption struct { DataParams DataParams FlushParams FlushParams - IndexParams IndexParams + IndexParams []IndexParams LoadParams LoadParams CreateOpts client.CreateCollectionOption IndexOpts client.IndexOption @@ -86,9 +86,9 @@ func WithFlushParams(fp FlushParams) PrepareCollectionOption { } } -func WithIndexParams(ip IndexParams) PrepareCollectionOption { +func WithIndexParams(ips []IndexParams) PrepareCollectionOption { return func(opt *ClientParamsOption) { - opt.IndexParams = ip + opt.IndexParams = ips } } diff --git a/test/testcases/partition_key_test.go b/test/testcases/partition_key_test.go index 42c9e8647..1b7ba5623 100644 --- a/test/testcases/partition_key_test.go +++ b/test/testcases/partition_key_test.go @@ -173,7 +173,6 @@ func TestPartitionKeyDefaultVarchar(t *testing.T) { } func TestPartitionKeyInvalidNumPartition(t *testing.T) { - t.Skip("https://github.com/milvus-io/milvus-sdk-go/issues/537") ctx := createContext(t, time.Second*common.DefaultTimeout) mc := createMilvusClient(ctx, t) @@ -190,7 +189,6 @@ func TestPartitionKeyInvalidNumPartition(t *testing.T) { numPartitions int64 errMsg string }{ - {0, "the specified partitions should be greater than 0 if partition key is used"}, {common.MaxPartitionNum + 1, "exceeds max configuration (4096)"}, {-1, "the specified partitions should be greater than 0 if partition key is used"}, } @@ -200,6 +198,12 @@ func TestPartitionKeyInvalidNumPartition(t *testing.T) { err := mc.CreateCollection(ctx, schema, common.DefaultShards, client.WithPartitionNum(npStruct.numPartitions)) common.CheckErr(t, err, false, npStruct.errMsg) } + + // PartitionNum is 0, actually default 64 partitions + err := mc.CreateCollection(ctx, schema, common.DefaultShards, client.WithPartitionNum(0)) + common.CheckErr(t, err, true) + partitions, _ := mc.ShowPartitions(ctx, collName) + require.Equal(t, len(partitions), common.DefaultPartitionNum) } func TestPartitionKeyNumPartition(t *testing.T) { @@ -372,10 +376,9 @@ func TestPartitionKeyPartitionOperation(t *testing.T) { err = mc.DeleteByPks(ctx, collName, partitions[2].Name, entity.NewColumnInt64(common.DefaultIntFieldName, []int64{0, 1})) common.CheckErr(t, err, false, "not support manually specifying the partition names if partition key mode is used") - // TODO https://github.com/milvus-io/milvus-sdk-go/issues/538 // bulk insert -> error - //_, err = mc.BulkInsert(ctx, collName, partitions[0].Name, []string{""}) - //common.CheckErr(t, err, false, "not support manually specifying the partition names if partition key mode is used") + _, err = mc.BulkInsert(ctx, collName, partitions[0].Name, []string{""}) + common.CheckErr(t, err, false, "not allow to set partition name for collection with partition key: importing data failed") // query partitions -> error _, err = mc.QueryByPks( diff --git a/test/testcases/query_test.go b/test/testcases/query_test.go index 938bd064f..44f1fbe37 100644 --- a/test/testcases/query_test.go +++ b/test/testcases/query_test.go @@ -235,8 +235,15 @@ func TestQueryNonPrimaryFields(t *testing.T) { mc.Flush(ctx, collName, false) // create index - idx, _ := entity.NewIndexHNSW(entity.L2, 8, 96) - mc.CreateIndex(ctx, collName, "floatVec", idx, false) + indexHnsw, _ := entity.NewIndexHNSW(entity.L2, 8, 96) + indexBinary, _ := entity.NewIndexBinIvfFlat(entity.JACCARD, 64) + for _, fieldName := range common.AllVectorsFieldsName { + if fieldName == common.DefaultBinaryVecFieldName { + mc.CreateIndex(ctx, collName, fieldName, indexBinary, false) + } else { + mc.CreateIndex(ctx, collName, fieldName, indexHnsw, false) + } + } // Load collection errLoad := mc.LoadCollection(ctx, collName, false) @@ -264,7 +271,6 @@ func TestQueryNonPrimaryFields(t *testing.T) { // test query empty or one scalar output fields func TestQueryEmptyOutputFields(t *testing.T) { - t.Skip("https://github.com/milvus-io/milvus/issues/28465") t.Parallel() ctx := createContext(t, time.Second*common.DefaultTimeout) // connect @@ -291,8 +297,11 @@ func TestQueryEmptyOutputFields(t *testing.T) { entity.NewColumnInt64(common.DefaultIntFieldName, ids.(*entity.ColumnInt64).Data()[:10]), []string{""}, ) - - common.CheckErr(t, err, false, "not exist") + if enableDynamic { + common.CheckErr(t, err, false, "parse output field name failed") + } else { + common.CheckErr(t, err, false, "not exist") + } // query with "float" output fields -> output "int64, float" queryFloatOutputs, _ := mc.QueryByPks( @@ -406,7 +415,9 @@ func TestOutputAllFields(t *testing.T) { common.CheckErr(t, errFlush, true) idx, _ := entity.NewIndexHNSW(entity.L2, 8, 96) - _ = mc.CreateIndex(ctx, collName, common.DefaultFloatVecFieldName, idx, false) + for _, fieldName := range []string{"floatVec", "fp16Vec", "bf16Vec", "binaryVec"} { + _ = mc.CreateIndex(ctx, collName, fieldName, idx, false) + } // Load collection errLoad := mc.LoadCollection(ctx, collName, false) @@ -414,7 +425,7 @@ func TestOutputAllFields(t *testing.T) { // query output all fields -> output all fields, includes vector and $meta field allFieldsName := append(common.AllArrayFieldsName, "int64", "bool", "int8", "int16", "int32", "float", - "double", "varchar", "json", "floatVec", common.DefaultDynamicFieldName) + "double", "varchar", "json", "floatVec", "fp16Vec", "bf16Vec", "binaryVec", common.DefaultDynamicFieldName) queryResultAll, errQuery := mc.Query(ctx, collName, []string{}, fmt.Sprintf("%s == 0", common.DefaultIntFieldName), []string{"*"}) common.CheckErr(t, errQuery, true) @@ -652,8 +663,18 @@ func TestQueryArrayFieldExpr(t *testing.T) { errFlush := mc.Flush(ctx, collName, false) common.CheckErr(t, errFlush, true) - idx, _ := entity.NewIndexHNSW(entity.L2, 8, 96) - _ = mc.CreateIndex(ctx, collName, common.DefaultFloatVecFieldName, idx, false) + // index + indexHnsw, _ := entity.NewIndexHNSW(entity.L2, 8, 96) + indexBinary, _ := entity.NewIndexBinIvfFlat(entity.JACCARD, 64) + for _, fieldName := range common.AllVectorsFieldsName { + if fieldName == common.DefaultBinaryVecFieldName { + err := mc.CreateIndex(ctx, collName, fieldName, indexBinary, false) + common.CheckErr(t, err, true) + } else { + err := mc.CreateIndex(ctx, collName, fieldName, indexHnsw, false) + common.CheckErr(t, err, true) + } + } // Load collection errLoad := mc.LoadCollection(ctx, collName, false) diff --git a/test/testcases/search_test.go b/test/testcases/search_test.go index 1d5f03497..25bb02254 100644 --- a/test/testcases/search_test.go +++ b/test/testcases/search_test.go @@ -336,7 +336,6 @@ func TestSearchPartitions(t *testing.T) { // test search empty output fields []string{} -> [], []string{""} func TestSearchEmptyOutputFields(t *testing.T) { - t.Skip("https://github.com/milvus-io/milvus/issues/28465") t.Parallel() ctx := createContext(t, time.Second*common.DefaultTimeout) // connect @@ -368,11 +367,11 @@ func TestSearchEmptyOutputFields(t *testing.T) { common.CheckSearchResult(t, searchResPkOutput, common.DefaultNq, common.DefaultTopK) // search vector output fields []string{""} - _, errSearchExist := mc.Search( + res, errSearchExist := mc.Search( ctx, collName, []string{}, "", - []string{""}, + []string{"a"}, common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector), common.DefaultFloatVecFieldName, entity.L2, @@ -380,25 +379,24 @@ func TestSearchEmptyOutputFields(t *testing.T) { sp, ) - //if enableDynamic { - // common.CheckErr(t, errSearchExist, true) - // common.CheckOutputFields(t, sp1[0].Fields, []string{""}) - //} else { - common.CheckErr(t, errSearchExist, false, "not exist") - //} + if enableDynamic { + common.CheckErr(t, errSearchExist, true) + common.CheckOutputFields(t, res[0].Fields, []string{"a"}) + } else { + common.CheckErr(t, errSearchExist, false, "not exist") + } common.CheckSearchResult(t, searchResPkOutput, common.DefaultNq, common.DefaultTopK) } } // test search output fields not exist -> output existed fields func TestSearchNotExistOutputFields(t *testing.T) { - t.Skip("https://github.com/milvus-io/milvus/issues/28465") t.Parallel() ctx := createContext(t, time.Second*common.DefaultTimeout) // connect mc := createMilvusClient(ctx, t) - for _, enableDynamic := range []bool{false} { + for _, enableDynamic := range []bool{false, true} { // create collection with data collName, _ := createCollectionWithDataIndex(ctx, t, mc, false, true, client.WithEnableDynamicSchema(enableDynamic)) @@ -425,7 +423,11 @@ func TestSearchNotExistOutputFields(t *testing.T) { common.DefaultTopK, sp, ) - common.CheckErr(t, errSearch, true) + if enableDynamic { + common.CheckErr(t, errSearch, true) + } else { + common.CheckErr(t, errSearch, false, "not exist") + } } } } @@ -500,8 +502,15 @@ func TestSearchOutputAllFields(t *testing.T) { _ = mc.Flush(ctx, collName, false) // create index - idx, _ := entity.NewIndexHNSW(entity.L2, 8, 96) - _ = mc.CreateIndex(ctx, collName, "floatVec", idx, false) + indexHnsw, _ := entity.NewIndexHNSW(entity.L2, 8, 96) + indexBinary, _ := entity.NewIndexBinIvfFlat(entity.JACCARD, 64) + for _, fieldName := range common.AllVectorsFieldsName { + if fieldName == common.DefaultBinaryVecFieldName { + mc.CreateIndex(ctx, collName, fieldName, indexBinary, false) + } else { + mc.CreateIndex(ctx, collName, fieldName, indexHnsw, false) + } + } // Load collection errLoad := mc.LoadCollection(ctx, collName, false) @@ -509,12 +518,7 @@ func TestSearchOutputAllFields(t *testing.T) { // search vector output all scalar fields queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector) - allFields := []string{"int64", "bool", "int8", "int16", "int32", "float", "double", "varchar", "json", "floatVec"} - allFields = append(allFields, common.AllArrayFieldsName...) - - if enableDynamic { - allFields = append(allFields, common.DefaultDynamicFieldName) - } + allFields := common.GetAllFieldsName(enableDynamic, false) sp, _ := entity.NewIndexHNSWSearchParam(74) searchRes, _ := mc.Search(ctx, collName, []string{}, "", @@ -604,7 +608,6 @@ func TestSearchInvalidVectorField(t *testing.T) { } // test search with invalid vectors -// TODO Issue https://github.com/milvus-io/milvus-sdk-go/issues/377 func TestSearchInvalidVectors(t *testing.T) { t.Parallel() ctx := createContext(t, time.Second*common.DefaultTimeout*2) @@ -628,7 +631,7 @@ func TestSearchInvalidVectors(t *testing.T) { {vectors: common.GenSearchVectors(common.DefaultNq, 64, entity.FieldTypeFloatVector), errMsg: "vector dimension mismatch"}, // vector type not match - {vectors: common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeBinaryVector), errMsg: "vector dimension mismatch"}, + {vectors: common.GenSearchVectors(common.DefaultNq, common.DefaultDim*32, entity.FieldTypeBinaryVector), errMsg: "vector type must be the same"}, // empty vectors {vectors: []entity.Vector{}, errMsg: "nq [0] is invalid"}, @@ -1467,6 +1470,90 @@ func TestVectorOutputField(t *testing.T) { } } +// test search with fp16/ bf16 /binary vector +func TestSearchMultiVectors(t *testing.T) { + ctx := createContext(t, time.Second*common.DefaultTimeout*2) + // connect + mc := createMilvusClient(ctx, t) + + // create -> insert [0, 3000) -> flush -> index -> load + cp := CollectionParams{CollectionFieldsType: AllVectors, AutoID: false, EnableDynamicField: true, + ShardsNum: common.DefaultShards, Dim: common.DefaultDim} + + dp := DataParams{DoInsert: true, CollectionFieldsType: AllVectors, start: 0, nb: common.DefaultNb * 2, + dim: common.DefaultDim, EnableDynamicField: true} + + // index params + ips := make([]IndexParams, 4) + var idx entity.Index + for _, fieldName := range common.AllVectorsFieldsName { + if fieldName == common.DefaultBinaryVecFieldName { + idx, _ = entity.NewIndexBinFlat(entity.JACCARD, 64) + } else { + idx, _ = entity.NewIndexFlat(entity.L2) + } + ips = append(ips, IndexParams{BuildIndex: true, Index: idx, FieldName: fieldName, async: false}) + } + + collName := prepareCollection(ctx, t, mc, cp, WithDataParams(dp), WithIndexParams(ips), WithCreateOption(client.WithConsistencyLevel(entity.ClStrong))) + + // search with all kinds of vectors + type mFieldNameType struct { + fieldName string + fieldType entity.FieldType + metricType entity.MetricType + } + fnts := []mFieldNameType{ + {fieldName: common.DefaultFloatVecFieldName, fieldType: entity.FieldTypeFloatVector, metricType: entity.L2}, + {fieldName: common.DefaultBinaryVecFieldName, fieldType: entity.FieldTypeBinaryVector, metricType: entity.JACCARD}, + {fieldName: common.DefaultFloat16VecFieldName, fieldType: entity.FieldTypeFloat16Vector, metricType: entity.L2}, + {fieldName: common.DefaultBFloat16VecFieldName, fieldType: entity.FieldTypeBFloat16Vector, metricType: entity.L2}, + } + + //sp, _ := entity.NewIndexHNSWSearchParam(20) + sp, _ := entity.NewIndexFlatSearchParam() + for _, fnt := range fnts { + queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, fnt.fieldType) + + // search + resSearch, errSearch := mc.Search(ctx, collName, []string{}, fmt.Sprintf("%s > 10", common.DefaultIntFieldName), + []string{"*"}, queryVec, fnt.fieldName, fnt.metricType, common.DefaultTopK*2, sp) + common.CheckErr(t, errSearch, true) + common.CheckSearchResult(t, resSearch, common.DefaultNq, common.DefaultTopK*2) + common.CheckOutputFields(t, resSearch[0].Fields, []string{common.DefaultIntFieldName, common.DefaultFloatVecFieldName, + common.DefaultBinaryVecFieldName, common.DefaultFloat16VecFieldName, common.DefaultBFloat16VecFieldName, common.DefaultDynamicFieldName}) + + //pagination search + resPage, errPage := mc.Search(ctx, collName, []string{}, fmt.Sprintf("%s > 10", common.DefaultIntFieldName), + []string{"*"}, queryVec, fnt.fieldName, fnt.metricType, common.DefaultTopK, sp, client.WithOffset(10)) + common.CheckErr(t, errPage, true) + common.CheckSearchResult(t, resPage, common.DefaultNq, common.DefaultTopK) + for i := 0; i < common.DefaultNq; i++ { + require.Equal(t, resSearch[i].IDs.(*entity.ColumnInt64).Data()[10:], resPage[i].IDs.(*entity.ColumnInt64).Data()) + } + common.CheckOutputFields(t, resPage[0].Fields, []string{common.DefaultIntFieldName, common.DefaultFloatVecFieldName, + common.DefaultBinaryVecFieldName, common.DefaultFloat16VecFieldName, common.DefaultBFloat16VecFieldName, common.DefaultDynamicFieldName}) + + // range search + sp.AddRadius(50.2) + sp.AddRangeFilter(0) + resRange, errRange := mc.Search(ctx, collName, []string{}, fmt.Sprintf("%s > 10", common.DefaultIntFieldName), + []string{"*"}, queryVec, fnt.fieldName, fnt.metricType, common.DefaultTopK, sp, client.WithOffset(10)) + common.CheckErr(t, errRange, true) + common.CheckSearchResult(t, resRange, common.DefaultNq, common.DefaultTopK) + common.CheckOutputFields(t, resRange[0].Fields, []string{common.DefaultIntFieldName, common.DefaultFloatVecFieldName, + common.DefaultBinaryVecFieldName, common.DefaultFloat16VecFieldName, common.DefaultBFloat16VecFieldName, common.DefaultDynamicFieldName}) + for _, res := range resRange { + for _, score := range res.Scores { + require.GreaterOrEqual(t, score, float32(0)) + require.LessOrEqual(t, score, float32(50.2)) + } + } + // TODO iterator search + } + +} + // TODO offset and limit // TODO consistency level // TODO WithGuaranteeTimestamp diff --git a/test/testcases/upsert_test.go b/test/testcases/upsert_test.go index 9443b2507..5e770fd1d 100644 --- a/test/testcases/upsert_test.go +++ b/test/testcases/upsert_test.go @@ -32,7 +32,8 @@ func TestUpsert(t *testing.T) { // create -> insert [0, 3000) -> flush -> index -> load cp := CollectionParams{CollectionFieldsType: AllFields, AutoID: false, EnableDynamicField: enableDynamic, ShardsNum: common.DefaultShards, Dim: common.DefaultDim} - collName := prepareCollection(ctx, t, mc, cp, WithCreateOption(client.WithConsistencyLevel(entity.ClStrong))) + ips := GenDefaultIndexParamsForAllVectors() + collName := prepareCollection(ctx, t, mc, cp, WithIndexParams(ips), WithCreateOption(client.WithConsistencyLevel(entity.ClStrong))) upsertNb := 10 // upsert exist entities [0, 10) @@ -133,7 +134,7 @@ func TestUpsertVarcharPk(t *testing.T) { ShardsNum: common.DefaultShards, Dim: common.DefaultDim} idx, _ := entity.NewIndexBinIvfFlat(entity.JACCARD, 16) - ip := IndexParams{BuildIndex: true, Index: idx, FieldName: common.DefaultBinaryVecFieldName, async: false} + ip := []IndexParams{{BuildIndex: true, Index: idx, FieldName: common.DefaultBinaryVecFieldName, async: false}} collName := prepareCollection(ctx, t, mc, cp, WithIndexParams(ip), WithCreateOption(client.WithConsistencyLevel(entity.ClStrong))) upsertNb := 10 @@ -243,7 +244,7 @@ func TestUpsertInvalidColumnData(t *testing.T) { dp := DataParams{DoInsert: true, CollectionFieldsType: Int64FloatVecJSON, start: 0, nb: 200, dim: common.DefaultDim, EnableDynamicField: false} collName := prepareCollection(ctx, t, mc, cp, WithDataParams(dp), - WithIndexParams(IndexParams{BuildIndex: false}), + WithIndexParams([]IndexParams{{BuildIndex: false}}), WithLoadParams(LoadParams{DoLoad: false}), WithCreateOption(client.WithConsistencyLevel(entity.ClStrong))) upsertNb := 10 @@ -285,7 +286,9 @@ func TestUpsertSamePksManyTimes(t *testing.T) { // create -> insert [0, 3000) -> flush -> index -> load cp := CollectionParams{CollectionFieldsType: AllFields, AutoID: false, EnableDynamicField: true, ShardsNum: common.DefaultShards, Dim: common.DefaultDim} - collName := prepareCollection(ctx, t, mc, cp, WithCreateOption(client.WithConsistencyLevel(entity.ClStrong))) + + ips := GenDefaultIndexParamsForAllVectors() + collName := prepareCollection(ctx, t, mc, cp, WithIndexParams(ips), WithCreateOption(client.WithConsistencyLevel(entity.ClStrong))) var data []entity.Column upsertNb := 1000 @@ -405,7 +408,7 @@ func TestUpsertWithoutLoading(t *testing.T) { dim: common.DefaultDim, EnableDynamicField: true} collName := prepareCollection(ctx, t, mc, cp, WithDataParams(dp), WithFlushParams(FlushParams{DoFlush: false}), - WithIndexParams(IndexParams{BuildIndex: false}), + WithIndexParams([]IndexParams{{BuildIndex: false}}), WithLoadParams(LoadParams{DoLoad: false}), WithCreateOption(client.WithConsistencyLevel(entity.ClStrong))) // upsert